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