Committing in .
[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 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 // Release lock again
158 gdk_threads_leave();
159
160 // Return FALSE to indicate that no more idle events are
161 // to be sent (single shot instead of continuous stream)
162 return FALSE;
163 }
164
165 gint wxapp_idle_callback( gpointer WXUNUSED(data) )
166 {
167 if (!wxTheApp) return TRUE;
168
169 // when getting called from GDK's time-out handler
170 // we are no longer within GDK's grab on the GUI
171 // thread so we must lock it here ourselves
172 gdk_threads_enter();
173
174 // Sent idle event to all who request them
175 while (wxTheApp->ProcessIdle()) { }
176
177 /* Indicate that we are now in idle mode - even so deeply
178 in idle mode that we don't get any idle events anymore.
179 this is like wxMSW where an idle event is sent only
180 once each time after the event queue has been completely
181 emptied */
182 g_isIdle = TRUE;
183 wxTheApp->m_idleTag = 0;
184
185 // Release lock again
186 gdk_threads_leave();
187
188 // Return FALSE to indicate that no more idle events are
189 // to be sent (single shot instead of continuous stream)
190 return FALSE;
191 }
192
193 void wxapp_install_idle_handler()
194 {
195 wxASSERT_MSG( wxTheApp->m_idleTag == 0, wxT("attempt to install idle handler twice") );
196
197 if (g_pendingTag == 0)
198 g_pendingTag = gtk_idle_add_priority( 900, wxapp_pending_callback, (gpointer) NULL );
199
200 /* This routine gets called by all event handlers
201 indicating that the idle is over. It may also
202 get called from other thread for sending events
203 to the main thread (and processing these in
204 idle time). Very low priority. */
205
206 wxTheApp->m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
207
208 g_isIdle = FALSE;
209 }
210
211 #if wxUSE_THREADS
212
213 static int g_threadUninstallLevel = 0;
214
215 void wxapp_install_thread_wakeup()
216 {
217 g_threadUninstallLevel++;
218
219 if (g_threadUninstallLevel != 1) return;
220
221 if (wxTheApp->m_wakeUpTimerTag) return;
222
223 wxTheApp->m_wakeUpTimerTag = gtk_timeout_add( 50, wxapp_wakeup_timerout_callback, (gpointer) NULL );
224 }
225
226 void wxapp_uninstall_thread_wakeup()
227 {
228 g_threadUninstallLevel--;
229
230 if (g_threadUninstallLevel != 0) return;
231
232 if (!wxTheApp->m_wakeUpTimerTag) return;
233
234 gtk_timeout_remove( wxTheApp->m_wakeUpTimerTag );
235 wxTheApp->m_wakeUpTimerTag = 0;
236 }
237
238 gint wxapp_wakeup_timerout_callback( gpointer WXUNUSED(data) )
239 {
240 // when getting called from GDK's time-out handler
241 // we are no longer within GDK's grab on the GUI
242 // thread so we must lock it here ourselves
243 gdk_threads_enter();
244
245 wxapp_uninstall_thread_wakeup();
246
247 // unblock other threads wishing to do some GUI things
248 wxMutexGuiLeave();
249
250 g_mainThreadLocked = TRUE;
251
252 // wake up other threads
253 wxUsleep( 1 );
254
255 // block other thread again
256 wxMutexGuiEnter();
257
258 g_mainThreadLocked = FALSE;
259
260 wxapp_install_thread_wakeup();
261
262 // release lock again
263 gdk_threads_leave();
264
265 return TRUE;
266 }
267
268 #endif // wxUSE_THREADS
269
270 //-----------------------------------------------------------------------------
271 // wxApp
272 //-----------------------------------------------------------------------------
273
274 IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
275
276 BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
277 EVT_IDLE(wxApp::OnIdle)
278 END_EVENT_TABLE()
279
280 wxApp::wxApp()
281 {
282 wxTheApp = this;
283
284 m_topWindow = (wxWindow *) NULL;
285 m_exitOnFrameDelete = TRUE;
286
287 m_idleTag = gtk_idle_add_priority( 1000, wxapp_idle_callback, (gpointer) NULL );
288
289 #if wxUSE_THREADS
290 m_wakeUpTimerTag = 0;
291 wxapp_install_thread_wakeup();
292 #endif
293
294 m_colorCube = (unsigned char*) NULL;
295
296 m_useBestVisual = FALSE;
297 }
298
299 wxApp::~wxApp()
300 {
301 if (m_idleTag) gtk_idle_remove( m_idleTag );
302
303 #if wxUSE_THREADS
304 wxapp_uninstall_thread_wakeup();
305 #endif
306
307 if (m_colorCube) free(m_colorCube);
308 }
309
310 bool wxApp::OnInitGui()
311 {
312 GdkVisual *visual = gdk_visual_get_system();
313
314 /* on some machines, the default visual is just 256 colours, so
315 we make sure we get the best. this can sometimes be wasteful,
316 of course, but what do these guys pay $30.000 for? */
317
318 if ((gdk_visual_get_best() != gdk_visual_get_system()) &&
319 (m_useBestVisual))
320 {
321 #ifdef __WXGTK20__
322 /* seems gtk_widget_set_default_visual no longer exists? */
323 GdkVisual* vis = gtk_widget_get_default_visual();
324 #else
325 GdkVisual* vis = gdk_visual_get_best();
326 gtk_widget_set_default_visual( vis );
327 #endif
328
329 GdkColormap *colormap = gdk_colormap_new( vis, FALSE );
330 gtk_widget_set_default_colormap( colormap );
331
332 visual = vis;
333 }
334
335 /* Nothing to do for 15, 16, 24, 32 bit displays */
336 if (visual->depth > 8) return TRUE;
337
338 /* initialize color cube for 8-bit color reduction dithering */
339
340 GdkColormap *cmap = gtk_widget_get_default_colormap();
341
342 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
343
344 for (int r = 0; r < 32; r++)
345 {
346 for (int g = 0; g < 32; g++)
347 {
348 for (int b = 0; b < 32; b++)
349 {
350 int rr = (r << 3) | (r >> 2);
351 int gg = (g << 3) | (g >> 2);
352 int bb = (b << 3) | (b >> 2);
353
354 int index = -1;
355
356 GdkColor *colors = cmap->colors;
357 if (colors)
358 {
359 int max = 3 * 65536;
360
361 for (int i = 0; i < cmap->size; i++)
362 {
363 int rdiff = ((rr << 8) - colors[i].red);
364 int gdiff = ((gg << 8) - colors[i].green);
365 int bdiff = ((bb << 8) - colors[i].blue);
366 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
367 if (sum < max)
368 {
369 index = i; max = sum;
370 }
371 }
372 }
373 else
374 {
375 #if (GTK_MINOR_VERSION > 0)
376 /* assume 8-bit true or static colors. this really
377 exists. */
378 GdkVisual* vis = gdk_colormap_get_visual( cmap );
379 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
380 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
381 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
382 #else
383 wxFAIL_MSG( wxT("Unsupported graphics hardware") );
384 #endif
385 }
386 m_colorCube[ (r*1024) + (g*32) + b ] = index;
387 }
388 }
389 }
390
391 return TRUE;
392 }
393
394 bool wxApp::ProcessIdle()
395 {
396 wxIdleEvent event;
397 event.SetEventObject( this );
398 ProcessEvent( event );
399
400 return event.MoreRequested();
401 }
402
403 void wxApp::OnIdle( wxIdleEvent &event )
404 {
405 static bool s_inOnIdle = FALSE;
406
407 /* Avoid recursion (via ProcessEvent default case) */
408 if (s_inOnIdle)
409 return;
410
411 s_inOnIdle = TRUE;
412
413 /* Resend in the main thread events which have been prepared in other
414 threads */
415 ProcessPendingEvents();
416
417 /* 'Garbage' collection of windows deleted with Close(). */
418 DeletePendingObjects();
419
420 /* Send OnIdle events to all windows */
421 bool needMore = SendIdleEvents();
422
423 if (needMore)
424 event.RequestMore(TRUE);
425
426 s_inOnIdle = FALSE;
427
428 /* flush the logged messages if any */
429 #if wxUSE_LOG
430 wxLog::FlushActive();
431 #endif // wxUSE_LOG
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 }