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