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