]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk/app.cpp
compilation fixes
[wxWidgets.git] / src / gtk / app.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: app.cpp
3// Purpose:
4// Author: Robert Roebling
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling, Julian Smart
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#ifdef __GNUG__
11 #pragma implementation "app.h"
12#endif
13
14#include "wx/app.h"
15#include "wx/gdicmn.h"
16#include "wx/utils.h"
17#include "wx/intl.h"
18#include "wx/log.h"
19#include "wx/memory.h"
20#include "wx/font.h"
21#include "wx/settings.h"
22#include "wx/dialog.h"
23
24#if wxUSE_WX_RESOURCES
25 #include "wx/resource.h"
26#endif
27
28#include "wx/module.h"
29#include "wx/image.h"
30
31#if wxUSE_THREADS
32 #include "wx/thread.h"
33#endif
34
35#include <unistd.h>
36
37#include <glib.h>
38#include <gdk/gdk.h>
39#include <gtk/gtk.h>
40
41#include "wx/gtk/win_gtk.h"
42
43//-----------------------------------------------------------------------------
44// global data
45//-----------------------------------------------------------------------------
46
47wxApp *wxTheApp = (wxApp *) NULL;
48wxAppInitializerFunction wxAppBase::m_appInitFn = (wxAppInitializerFunction) NULL;
49
50extern bool g_isIdle;
51
52bool g_mainThreadLocked = FALSE;
53gint g_pendingTag = 0;
54
55static GtkWidget *gs_RootWindow = (GtkWidget*) NULL;
56
57//-----------------------------------------------------------------------------
58// local functions
59//-----------------------------------------------------------------------------
60
61/* forward declaration */
62gint wxapp_idle_callback( gpointer WXUNUSED(data) );
63gint wxapp_pending_callback( gpointer WXUNUSED(data) );
64void wxapp_install_idle_handler();
65
66#if wxUSE_THREADS
67gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) );
68#endif
69
70//-----------------------------------------------------------------------------
71// wxExit
72//-----------------------------------------------------------------------------
73
74void wxExit()
75{
76 gtk_main_quit();
77}
78
79//-----------------------------------------------------------------------------
80// wxYield
81//-----------------------------------------------------------------------------
82
83static bool gs_inYield = FALSE;
84
85bool wxYield()
86{
87#if wxUSE_THREADS
88 if ( !wxThread::IsMain() )
89 {
90 // can't call gtk_main_iteration() from other threads like this
91 return TRUE;
92 }
93#endif // wxUSE_THREADS
94
95#ifdef __WXDEBUG__
96 if (gs_inYield)
97 wxFAIL_MSG( wxT("wxYield called recursively" ) );
98#endif
99
100 gs_inYield = TRUE;
101
102 if (!g_isIdle)
103 {
104 // We need to remove idle callbacks or the loop will
105 // never finish.
106 gtk_idle_remove( wxTheApp->m_idleTag );
107 wxTheApp->m_idleTag = 0;
108 g_isIdle = TRUE;
109 }
110
111 while (gtk_events_pending())
112 gtk_main_iteration();
113
114 // disable log flushing from here because a call to wxYield() shouldn't
115 // normally result in message boxes popping up &c
116 wxLog::Suspend();
117
118 /* it's necessary to call ProcessIdle() to update the frames sizes which
119 might have been changed (it also will update other things set from
120 OnUpdateUI() which is a nice (and desired) side effect) */
121 while (wxTheApp->ProcessIdle()) { }
122
123 // let the logs be flashed again
124 wxLog::Resume();
125
126 gs_inYield = FALSE;
127
128 return TRUE;
129}
130
131//-----------------------------------------------------------------------------
132// wxYieldIfNeeded
133// Like wxYield, but fails silently if the yield is recursive.
134//-----------------------------------------------------------------------------
135
136bool wxYieldIfNeeded()
137{
138 if (gs_inYield)
139 return FALSE;
140
141 return wxYield();
142}
143
144//-----------------------------------------------------------------------------
145// wxWakeUpIdle
146//-----------------------------------------------------------------------------
147
148void wxWakeUpIdle()
149{
150#if wxUSE_THREADS
151 if (!wxThread::IsMain())
152 wxMutexGuiEnter();
153#endif
154
155 if (g_isIdle)
156 wxapp_install_idle_handler();
157
158#if wxUSE_THREADS
159 if (!wxThread::IsMain())
160 wxMutexGuiLeave();
161#endif
162}
163
164//-----------------------------------------------------------------------------
165// local functions
166//-----------------------------------------------------------------------------
167
168gint wxapp_pending_callback( gpointer WXUNUSED(data) )
169{
170 if (!wxTheApp) return TRUE;
171
172 // when getting called from GDK's time-out handler
173 // we are no longer within GDK's grab on the GUI
174 // thread so we must lock it here ourselves
175 gdk_threads_enter();
176
177 // Sent idle event to all who request them
178 wxTheApp->ProcessPendingEvents();
179
180 g_pendingTag = 0;
181
182 /* flush the logged messages if any */
183#if wxUSE_LOG
184 wxLog::FlushActive();
185#endif // wxUSE_LOG
186
187 // Release lock again
188 gdk_threads_leave();
189
190 // Return FALSE to indicate that no more idle events are
191 // to be sent (single shot instead of continuous stream)
192 return FALSE;
193}
194
195gint wxapp_idle_callback( gpointer WXUNUSED(data) )
196{
197 if (!wxTheApp) return TRUE;
198
199 // when getting called from GDK's time-out handler
200 // we are no longer within GDK's grab on the GUI
201 // thread so we must lock it here ourselves
202 gdk_threads_enter();
203
204 /* Indicate that we are now in idle mode - even so deeply
205 in idle mode that we don't get any idle events anymore.
206 this is like wxMSW where an idle event is sent only
207 once each time after the event queue has been completely
208 emptied */
209 g_isIdle = TRUE;
210 wxTheApp->m_idleTag = 0;
211
212 // Sent idle event to all who request them
213 while (wxTheApp->ProcessIdle()) { }
214
215 // Release lock again
216 gdk_threads_leave();
217
218 // Return FALSE to indicate that no more idle events are
219 // to be sent (single shot instead of continuous stream)
220 return FALSE;
221}
222
223void wxapp_install_idle_handler()
224{
225 wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
226
227 g_isIdle = FALSE;
228
229 if (g_pendingTag == 0)
230 g_pendingTag = gtk_idle_add_priority( 900, wxapp_pending_callback, (gpointer) NULL );
231
232 /* This routine gets called by all event handlers
233 indicating that the idle is over. It may also
234 get called from other thread for sending events
235 to the main thread (and processing these in
236 idle time). Very low priority. */
237
238 wxTheApp->m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
239}
240
241#if wxUSE_THREADS
242
243static int g_threadUninstallLevel = 0;
244
245void wxapp_install_thread_wakeup()
246{
247 g_threadUninstallLevel++;
248
249 if (g_threadUninstallLevel != 1) return;
250
251 if (wxTheApp->m_wakeUpTimerTag) return;
252
253 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 50, wxapp_wakeup_timerout_callback, (gpointer) NULL );
254}
255
256void wxapp_uninstall_thread_wakeup()
257{
258 g_threadUninstallLevel--;
259
260 if (g_threadUninstallLevel != 0) return;
261
262 if (!wxTheApp->m_wakeUpTimerTag) return;
263
264 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
265 wxTheApp->m_wakeUpTimerTag = 0;
266}
267
268gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
269{
270 // when getting called from GDK's time-out handler
271 // we are no longer within GDK's grab on the GUI
272 // thread so we must lock it here ourselves
273 gdk_threads_enter();
274
275 wxapp_uninstall_thread_wakeup();
276
277 // unblock other threads wishing to do some GUI things
278 wxMutexGuiLeave();
279
280 g_mainThreadLocked = TRUE;
281
282 // wake up other threads
283 wxUsleep( 1 );
284
285 // block other thread again
286 wxMutexGuiEnter();
287
288 g_mainThreadLocked = FALSE;
289
290 wxapp_install_thread_wakeup();
291
292 // release lock again
293 gdk_threads_leave();
294
295 return TRUE;
296}
297
298#endif // wxUSE_THREADS
299
300//-----------------------------------------------------------------------------
301// wxApp
302//-----------------------------------------------------------------------------
303
304IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
305
306BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
307 EVT_IDLE(wxApp::OnIdle)
308END_EVENT_TABLE()
309
310wxApp::wxApp()
311{
312 m_idleTag = 0;
313 wxapp_install_idle_handler();
314
315#if wxUSE_THREADS
316 m_wakeUpTimerTag = 0;
317 wxapp_install_thread_wakeup();
318#endif
319
320 m_colorCube = (unsigned char*) NULL;
321}
322
323wxApp::~wxApp()
324{
325 if (m_idleTag) gtk_idle_remove( m_idleTag );
326
327#if wxUSE_THREADS
328 wxapp_uninstall_thread_wakeup();
329#endif
330
331 if (m_colorCube) free(m_colorCube);
332}
333
334bool wxApp::OnInitGui()
335{
336 if ( !wxAppBase::OnInitGui() )
337 return FALSE;
338
339 GdkVisual *visual = gdk_visual_get_system();
340
341 /* on some machines, the default visual is just 256 colours, so
342 we make sure we get the best. this can sometimes be wasteful,
343 of course, but what do these guys pay $30.000 for? */
344
345 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
346 (m_useBestVisual))
347 {
348#ifdef __WXGTK20__
349 /* seems gtk_widget_set_default_visual no longer exists? */
350 GdkVisual* vis = gtk_widget_get_default_visual();
351#else
352 GdkVisual* vis = gdk_visual_get_best();
353 gtk_widget_set_default_visual( vis );
354#endif
355
356 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
357 gtk_widget_set_default_colormap( colormap );
358
359 visual = vis;
360 }
361
362 /* Nothing to do for 15, 16, 24, 32 bit displays */
363 if (visual->depth > 8) return TRUE;
364
365 /* initialize color cube for 8-bit color reduction dithering */
366
367 GdkColormap *cmap = gtk_widget_get_default_colormap();
368
369 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
370
371 for (int r = 0; r < 32; r++)
372 {
373 for (int g = 0; g < 32; g++)
374 {
375 for (int b = 0; b < 32; b++)
376 {
377 int rr = (r << 3) | (r >> 2);
378 int gg = (g << 3) | (g >> 2);
379 int bb = (b << 3) | (b >> 2);
380
381 int index = -1;
382
383 GdkColor *colors = cmap->colors;
384 if (colors)
385 {
386 int max = 3 * 65536;
387
388 for (int i = 0; i < cmap->size; i++)
389 {
390 int rdiff = ((rr << 8) - colors[i].red);
391 int gdiff = ((gg << 8) - colors[i].green);
392 int bdiff = ((bb << 8) - colors[i].blue);
393 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
394 if (sum < max)
395 {
396 index = i; max = sum;
397 }
398 }
399 }
400 else
401 {
402#if (GTK_MINOR_VERSION > 0)
403 /* assume 8-bit true or static colors. this really
404 exists. */
405 GdkVisual* vis = gdk_colormap_get_visual( cmap );
406 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
407 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
408 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
409#else
410 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
411#endif
412 }
413 m_colorCube[ (r*1024) + (g*32) + b ] = index;
414 }
415 }
416 }
417
418 return TRUE;
419}
420
421bool wxApp::ProcessIdle()
422{
423 wxIdleEvent event;
424 event.SetEventObject( this );
425 ProcessEvent( event );
426
427 return event.MoreRequested();
428}
429
430void wxApp::OnIdle( wxIdleEvent &event )
431{
432 static bool s_inOnIdle = FALSE;
433
434 /* Avoid recursion (via ProcessEvent default case) */
435 if (s_inOnIdle)
436 return;
437
438 s_inOnIdle = TRUE;
439
440 /* Resend in the main thread events which have been prepared in other
441 threads */
442 ProcessPendingEvents();
443
444 /* 'Garbage' collection of windows deleted with Close(). */
445 DeletePendingObjects();
446
447 /* Send OnIdle events to all windows */
448 bool needMore = SendIdleEvents();
449
450 if (needMore)
451 event.RequestMore(TRUE);
452
453 s_inOnIdle = FALSE;
454}
455
456bool wxApp::SendIdleEvents()
457{
458 bool needMore = FALSE;
459
460 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
461 while (node)
462 {
463 wxWindow* win = node->GetData();
464 if (SendIdleEvents(win))
465 needMore = TRUE;
466 node = node->GetNext();
467 }
468
469 return needMore;
470}
471
472bool wxApp::SendIdleEvents( wxWindow* win )
473{
474 bool needMore = FALSE;
475
476 wxIdleEvent event;
477 event.SetEventObject(win);
478
479 win->GetEventHandler()->ProcessEvent(event);
480
481 win->OnInternalIdle();
482
483 if (event.MoreRequested())
484 needMore = TRUE;
485
486 wxNode* node = win->GetChildren().First();
487 while (node)
488 {
489 wxWindow* win = (wxWindow*) node->Data();
490 if (SendIdleEvents(win))
491 needMore = TRUE;
492
493 node = node->Next();
494 }
495 return needMore ;
496}
497
498int wxApp::MainLoop()
499{
500 gtk_main();
501 return 0;
502}
503
504void wxApp::ExitMainLoop()
505{
506 if (gtk_main_level() > 0)
507 gtk_main_quit();
508}
509
510bool wxApp::Initialized()
511{
512 return m_initialized;
513}
514
515bool wxApp::Pending()
516{
517 return (gtk_events_pending() > 0);
518}
519
520void wxApp::Dispatch()
521{
522 gtk_main_iteration();
523}
524
525void wxApp::DeletePendingObjects()
526{
527 wxNode *node = wxPendingDelete.First();
528 while (node)
529 {
530 wxObject *obj = (wxObject *)node->Data();
531
532 delete obj;
533
534 if (wxPendingDelete.Find(obj))
535 delete node;
536
537 node = wxPendingDelete.First();
538 }
539}
540
541bool wxApp::Initialize()
542{
543 wxBuffer = new wxChar[BUFSIZ + 512];
544
545 wxClassInfo::InitializeClasses();
546
547 wxSystemSettings::Init();
548
549#if wxUSE_INTL
550 wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
551#endif
552
553 // GL: I'm annoyed ... I don't know where to put this and I don't want to
554 // create a module for that as it's part of the core.
555#if wxUSE_THREADS
556 wxPendingEvents = new wxList();
557 wxPendingEventsLocker = new wxCriticalSection();
558#endif
559
560 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
561 wxTheColourDatabase->Initialize();
562
563 wxInitializeStockLists();
564 wxInitializeStockObjects();
565
566#if wxUSE_WX_RESOURCES
567 wxInitializeResourceSystem();
568#endif
569
570 wxModule::RegisterModules();
571 if (!wxModule::InitializeModules()) return FALSE;
572
573 return TRUE;
574}
575
576void wxApp::CleanUp()
577{
578 wxModule::CleanUpModules();
579
580#if wxUSE_WX_RESOURCES
581 wxCleanUpResourceSystem();
582#endif
583
584 if (wxTheColourDatabase)
585 delete wxTheColourDatabase;
586
587 wxTheColourDatabase = (wxColourDatabase*) NULL;
588
589 wxDeleteStockObjects();
590
591 wxDeleteStockLists();
592
593 delete wxTheApp;
594 wxTheApp = (wxApp*) NULL;
595
596 // GL: I'm annoyed ... I don't know where to put this and I don't want to
597 // create a module for that as it's part of the core.
598#if wxUSE_THREADS
599 delete wxPendingEvents;
600 delete wxPendingEventsLocker;
601#endif
602
603 wxSystemSettings::Done();
604
605 delete[] wxBuffer;
606
607 wxClassInfo::CleanUpClasses();
608
609 // check for memory leaks
610#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
611 if (wxDebugContext::CountObjectsLeft(TRUE) > 0)
612 {
613 wxLogDebug(wxT("There were memory leaks.\n"));
614 wxDebugContext::Dump();
615 wxDebugContext::PrintStatistics();
616 }
617#endif // Debug
618
619#if wxUSE_LOG
620 // do this as the very last thing because everything else can log messages
621 wxLog::DontCreateOnDemand();
622
623 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
624 if (oldLog)
625 delete oldLog;
626#endif // wxUSE_LOG
627}
628
629//-----------------------------------------------------------------------------
630// Access to the root window global
631//-----------------------------------------------------------------------------
632
633GtkWidget* wxGetRootWindow()
634{
635 if (gs_RootWindow == NULL) {
636 gs_RootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
637 gtk_widget_realize( gs_RootWindow );
638 }
639 return gs_RootWindow;
640}
641
642//-----------------------------------------------------------------------------
643// wxEntry
644//-----------------------------------------------------------------------------
645
646
647int wxEntryStart( int argc, char *argv[] )
648{
649#if wxUSE_THREADS
650 /* GTK 1.2 up to version 1.2.3 has broken threads */
651 if ((gtk_major_version == 1) &&
652 (gtk_minor_version == 2) &&
653 (gtk_micro_version < 4))
654 {
655 printf( "wxWindows warning: GUI threading disabled due to outdated GTK version\n" );
656 }
657 else
658 {
659 g_thread_init(NULL);
660 }
661#endif
662
663 gtk_set_locale();
664
665 // We should have the wxUSE_WCHAR_T test on the _outside_
666#if wxUSE_WCHAR_T
667#if defined(__WXGTK20__)
668 // gtk+ 2.0 supports Unicode through UTF-8 strings
669 wxConvCurrent = &wxConvUTF8;
670#else
671 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
672#endif
673#else
674 if (!wxOKlibc()) wxConvCurrent = (wxMBConv*) NULL;
675#endif
676
677 gdk_threads_enter();
678
679 gtk_init( &argc, &argv );
680
681 wxSetDetectableAutoRepeat( TRUE );
682
683 if (!wxApp::Initialize())
684 {
685 gdk_threads_leave();
686 return -1;
687 }
688
689 return 0;
690}
691
692
693int wxEntryInitGui()
694{
695 int retValue = 0;
696
697 if ( !wxTheApp->OnInitGui() )
698 retValue = -1;
699
700 wxGetRootWindow();
701
702 return retValue;
703}
704
705
706void wxEntryCleanup()
707{
708#if wxUSE_LOG
709 // flush the logged messages if any
710 wxLog *log = wxLog::GetActiveTarget();
711 if (log != NULL && log->HasPendingMessages())
712 log->Flush();
713
714 // continuing to use user defined log target is unsafe from now on because
715 // some resources may be already unavailable, so replace it by something
716 // more safe
717 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
718 if ( oldlog )
719 delete oldlog;
720#endif // wxUSE_LOG
721
722 wxApp::CleanUp();
723
724 gdk_threads_leave();
725}
726
727
728
729int wxEntry( int argc, char *argv[] )
730{
731#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
732 // This seems to be necessary since there are 'rogue'
733 // objects present at this point (perhaps global objects?)
734 // Setting a checkpoint will ignore them as far as the
735 // memory checking facility is concerned.
736 // Of course you may argue that memory allocated in globals should be
737 // checked, but this is a reasonable compromise.
738 wxDebugContext::SetCheckpoint();
739#endif
740 int err = wxEntryStart(argc, argv);
741 if (err)
742 return err;
743
744 if (!wxTheApp)
745 {
746 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
747 wxT("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
748
749 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
750
751 wxObject *test_app = app_ini();
752
753 wxTheApp = (wxApp*) test_app;
754 }
755
756 wxCHECK_MSG( wxTheApp, -1, wxT("wxWindows error: no application object") );
757
758 wxTheApp->argc = argc;
759#if wxUSE_UNICODE
760 wxTheApp->argv = new wxChar*[argc+1];
761 int mb_argc = 0;
762 while (mb_argc < argc)
763 {
764 wxTheApp->argv[mb_argc] = wxStrdup(wxConvLibc.cMB2WX(argv[mb_argc]));
765 mb_argc++;
766 }
767 wxTheApp->argv[mb_argc] = (wxChar *)NULL;
768#else
769 wxTheApp->argv = argv;
770#endif
771
772 wxString name(wxFileNameFromPath(argv[0]));
773 wxStripExtension( name );
774 wxTheApp->SetAppName( name );
775
776 int retValue;
777 retValue = wxEntryInitGui();
778
779 // Here frames insert themselves automatically into wxTopLevelWindows by
780 // getting created in OnInit().
781 if ( retValue == 0 )
782 {
783 if ( !wxTheApp->OnInit() )
784 retValue = -1;
785 }
786
787 if ( retValue == 0 )
788 {
789 /* delete pending toplevel windows (typically a single
790 dialog) so that, if there isn't any left, we don't
791 call OnRun() */
792 wxTheApp->DeletePendingObjects();
793
794 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
795
796 if (wxTheApp->Initialized())
797 {
798 wxTheApp->OnRun();
799
800 wxWindow *topWindow = wxTheApp->GetTopWindow();
801 if (topWindow)
802 {
803 /* Forcibly delete the window. */
804 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
805 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
806 {
807 topWindow->Close( TRUE );
808 wxTheApp->DeletePendingObjects();
809 }
810 else
811 {
812 delete topWindow;
813 wxTheApp->SetTopWindow( (wxWindow*) NULL );
814 }
815 }
816
817 retValue = wxTheApp->OnExit();
818 }
819 }
820
821 wxEntryCleanup();
822
823 return retValue;
824}
825
826#include "wx/gtk/info.xpm"
827#include "wx/gtk/error.xpm"
828#include "wx/gtk/question.xpm"
829#include "wx/gtk/warning.xpm"
830
831wxIcon
832wxApp::GetStdIcon(int which) const
833{
834 switch(which)
835 {
836 case wxICON_INFORMATION:
837 return wxIcon(info_xpm);
838
839 case wxICON_QUESTION:
840 return wxIcon(question_xpm);
841
842 case wxICON_EXCLAMATION:
843 return wxIcon(warning_xpm);
844
845 default:
846 wxFAIL_MSG(wxT("requested non existent standard icon"));
847 // still fall through
848
849 case wxICON_HAND:
850 return wxIcon(error_xpm);
851 }
852}