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