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