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