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