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