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