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