]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/app.cpp
Oops.
[wxWidgets.git] / src / gtk1 / 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 /* on some SGIs, the default visual is just 256 colours, so we
301 make sure we get the best. this can sometimes be wasteful,
302 of course, but what do these guys pay $30.000 for? */
303 GdkVisual* visual = gdk_visual_get_best();
304 gtk_widget_set_default_visual( visual );
305
306 /* Nothing to do for 15, 16, 24, 32 bit displays */
307 if (visual->depth > 8) return TRUE;
308
309 /* this initiates the standard palette as defined by GdkImlib
310 in the GNOME libraries. it ensures that all GNOME applications
311 use the same 64 colormap entries on 8-bit displays so you
312 can use several rather graphics-heavy applications at the
313 same time.
314 NOTE: this doesn't really seem to work this way... */
315
316 /*
317 GdkColormap *cmap = gdk_colormap_new( gdk_visual_get_system(), TRUE );
318
319 for (int i = 0; i < 64; i++)
320 {
321 GdkColor col;
322 col.red = g_palette[i*3 + 0] << 8;
323 col.green = g_palette[i*3 + 1] << 8;
324 col.blue = g_palette[i*3 + 2] << 8;
325 col.pixel = 0;
326
327 gdk_color_alloc( cmap, &col );
328 }
329
330 gtk_widget_set_default_colormap( cmap );
331 */
332
333 /* initialize color cube for 8-bit color reduction dithering */
334
335 GdkColormap *cmap = gtk_widget_get_default_colormap();
336
337 m_colorCube = (unsigned char*)malloc(32 * 32 * 32);
338
339 for (int r = 0; r < 32; r++)
340 {
341 for (int g = 0; g < 32; g++)
342 {
343 for (int b = 0; b < 32; b++)
344 {
345 int rr = (r << 3) | (r >> 2);
346 int gg = (g << 3) | (g >> 2);
347 int bb = (b << 3) | (b >> 2);
348
349 int index = -1;
350
351 GdkColor *colors = cmap->colors;
352 if (colors)
353 {
354 int max = 3 * 65536;
355
356 for (int i = 0; i < cmap->size; i++)
357 {
358 int rdiff = ((rr << 8) - colors[i].red);
359 int gdiff = ((gg << 8) - colors[i].green);
360 int bdiff = ((bb << 8) - colors[i].blue);
361 int sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
362 if (sum < max)
363 {
364 index = i; max = sum;
365 }
366 }
367 }
368 else
369 {
370 #if (GTK_MINOR_VERSION > 0)
371 /* assume 8-bit true or static colors. this really
372 exists. */
373 GdkVisual* vis = gdk_colormap_get_visual( cmap );
374 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
375 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
376 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
377 #else
378 wxFAIL_MSG( _T("Unsupported graphics hardware") );
379 #endif
380 }
381 m_colorCube[ (r*1024) + (g*32) + b ] = index;
382 }
383 }
384 }
385
386 return TRUE;
387 }
388
389 bool wxApp::ProcessIdle()
390 {
391 wxIdleEvent event;
392 event.SetEventObject( this );
393 ProcessEvent( event );
394
395 return event.MoreRequested();
396 }
397
398 void wxApp::OnIdle( wxIdleEvent &event )
399 {
400 static bool inOnIdle = FALSE;
401
402 /* Avoid recursion (via ProcessEvent default case) */
403 if (inOnIdle)
404 return;
405
406 inOnIdle = TRUE;
407
408 #if wxUSE_THREADS
409 /* Resend in the main thread events which have been prepared in other
410 threads */
411 ProcessPendingEvents();
412 #endif
413
414 /* 'Garbage' collection of windows deleted with Close(). */
415 DeletePendingObjects();
416
417 /* flush the logged messages if any */
418 #if wxUSE_LOG
419 wxLog *log = wxLog::GetActiveTarget();
420 if (log != NULL && log->HasPendingMessages())
421 log->Flush();
422 #endif // wxUSE_LOG
423
424 /* Send OnIdle events to all windows */
425 bool needMore = SendIdleEvents();
426
427 if (needMore)
428 event.RequestMore(TRUE);
429
430 inOnIdle = FALSE;
431 }
432
433 bool wxApp::SendIdleEvents()
434 {
435 bool needMore = FALSE;
436
437 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
438 while (node)
439 {
440 wxWindow* win = node->GetData();
441 if (SendIdleEvents(win))
442 needMore = TRUE;
443 node = node->GetNext();
444 }
445
446 return needMore;
447 }
448
449 bool wxApp::SendIdleEvents( wxWindow* win )
450 {
451 bool needMore = FALSE;
452
453 wxIdleEvent event;
454 event.SetEventObject(win);
455
456 win->OnInternalIdle();
457
458 win->ProcessEvent(event);
459
460 if (event.MoreRequested())
461 needMore = TRUE;
462
463 wxNode* node = win->GetChildren().First();
464 while (node)
465 {
466 wxWindow* win = (wxWindow*) node->Data();
467 if (SendIdleEvents(win))
468 needMore = TRUE;
469
470 node = node->Next();
471 }
472 return needMore ;
473 }
474
475 int wxApp::MainLoop()
476 {
477 gtk_main();
478 return 0;
479 }
480
481 void wxApp::ExitMainLoop()
482 {
483 gtk_main_quit();
484 }
485
486 bool wxApp::Initialized()
487 {
488 return m_initialized;
489 }
490
491 bool wxApp::Pending()
492 {
493 return (gtk_events_pending() > 0);
494 }
495
496 void wxApp::Dispatch()
497 {
498 gtk_main_iteration();
499 }
500
501 #if wxUSE_THREADS
502 void wxApp::ProcessPendingEvents()
503 {
504 wxNode *node = wxPendingEvents->First();
505 wxCriticalSectionLocker locker(*wxPendingEventsLocker);
506
507 while (node)
508 {
509 wxEvtHandler *handler = (wxEvtHandler *)node->Data();
510
511 handler->ProcessPendingEvents();
512
513 delete node;
514
515 node = wxPendingEvents->First();
516 }
517 }
518 #endif // wxUSE_THREADS
519
520 void wxApp::DeletePendingObjects()
521 {
522 wxNode *node = wxPendingDelete.First();
523 while (node)
524 {
525 wxObject *obj = (wxObject *)node->Data();
526
527 delete obj;
528
529 if (wxPendingDelete.Find(obj))
530 delete node;
531
532 node = wxPendingDelete.First();
533 }
534 }
535
536 wxWindow *wxApp::GetTopWindow()
537 {
538 if (m_topWindow)
539 return m_topWindow;
540 else if (wxTopLevelWindows.GetCount() > 0)
541 return wxTopLevelWindows.GetFirst()->GetData();
542 else
543 return NULL;
544 }
545
546 void wxApp::SetTopWindow( wxWindow *win )
547 {
548 m_topWindow = win;
549 }
550
551 bool wxApp::Initialize()
552 {
553 wxBuffer = new wxChar[BUFSIZ + 512];
554
555 wxClassInfo::InitializeClasses();
556
557 wxSystemSettings::Init();
558
559 // GL: I'm annoyed ... I don't know where to put this and I don't want to
560 // create a module for that as it's part of the core.
561 #if wxUSE_THREADS
562 wxPendingEvents = new wxList();
563 wxPendingEventsLocker = new wxCriticalSection();
564 #endif
565
566 /*
567 wxTheFontNameDirectory = new wxFontNameDirectory;
568 wxTheFontNameDirectory->Initialize();
569 */
570
571 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
572 wxTheColourDatabase->Initialize();
573
574 wxInitializeStockLists();
575 wxInitializeStockObjects();
576
577 #if wxUSE_WX_RESOURCES
578 wxTheResourceCache = new wxResourceCache( wxKEY_STRING );
579
580 wxInitializeResourceSystem();
581 #endif
582
583 wxImage::InitStandardHandlers();
584
585 wxModule::RegisterModules();
586 if (!wxModule::InitializeModules()) return FALSE;
587
588 return TRUE;
589 }
590
591 void wxApp::CleanUp()
592 {
593 wxModule::CleanUpModules();
594
595 #if wxUSE_WX_RESOURCES
596 wxFlushResources();
597
598 if (wxTheResourceCache)
599 delete wxTheResourceCache;
600 wxTheResourceCache = (wxResourceCache*) NULL;
601
602 wxCleanUpResourceSystem();
603 #endif
604
605 if (wxTheColourDatabase)
606 delete wxTheColourDatabase;
607 wxTheColourDatabase = (wxColourDatabase*) NULL;
608
609 /*
610 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
611 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
612 */
613
614 wxDeleteStockObjects();
615
616 wxDeleteStockLists();
617
618 wxImage::CleanUpHandlers();
619
620 delete wxTheApp;
621 wxTheApp = (wxApp*) NULL;
622
623 // GL: I'm annoyed ... I don't know where to put this and I don't want to
624 // create a module for that as it's part of the core.
625 #if wxUSE_THREADS
626 delete wxPendingEvents;
627 delete wxPendingEventsLocker;
628 #endif
629
630 wxSystemSettings::Done();
631
632 delete[] wxBuffer;
633
634 wxClassInfo::CleanUpClasses();
635
636 // check for memory leaks
637 #if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
638 if (wxDebugContext::CountObjectsLeft() > 0)
639 {
640 wxLogDebug(_T("There were memory leaks.\n"));
641 wxDebugContext::Dump();
642 wxDebugContext::PrintStatistics();
643 }
644 #endif // Debug
645
646 #if wxUSE_LOG
647 // do this as the very last thing because everything else can log messages
648 wxLog::DontCreateOnDemand();
649
650 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
651 if (oldLog)
652 delete oldLog;
653 }
654
655 wxLog *wxApp::CreateLogTarget()
656 {
657 return new wxLogGui;
658 }
659 #endif // wxUSE_LOG
660
661 //-----------------------------------------------------------------------------
662 // wxEntry
663 //-----------------------------------------------------------------------------
664
665 int wxEntry( int argc, char *argv[] )
666 {
667 gtk_set_locale();
668
669 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
670
671 gtk_init( &argc, &argv );
672
673 wxSetDetectableAutoRepeat( TRUE );
674
675 if (!wxApp::Initialize())
676 return -1;
677
678 if (!wxTheApp)
679 {
680 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
681 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
682
683 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
684
685 wxObject *test_app = app_ini();
686
687 wxTheApp = (wxApp*) test_app;
688 }
689
690 wxCHECK_MSG( wxTheApp, -1, _T("wxWindows error: no application object") );
691
692 wxTheApp->argc = argc;
693 wxTheApp->argv = argv;
694
695 wxString name(wxFileNameFromPath(argv[0]));
696 wxStripExtension( name );
697 wxTheApp->SetAppName( name );
698
699 int retValue = 0;
700
701 if ( !wxTheApp->OnInitGui() )
702 retValue = -1;
703
704 // Here frames insert themselves automatically into wxTopLevelWindows by
705 // getting created in OnInit().
706 if ( retValue == 0 )
707 {
708 if ( !wxTheApp->OnInit() )
709 retValue = -1;
710 }
711
712 if ( retValue == 0 )
713 {
714 /* delete pending toplevel windows (typically a single
715 dialog) so that, if there isn't any left, we don't
716 call OnRun() */
717 wxTheApp->DeletePendingObjects();
718
719 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
720
721 if (wxTheApp->Initialized())
722 {
723 retValue = wxTheApp->OnRun();
724
725 wxWindow *topWindow = wxTheApp->GetTopWindow();
726 if (topWindow)
727 {
728 /* Forcibly delete the window. */
729 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
730 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
731 {
732 topWindow->Close( TRUE );
733 wxTheApp->DeletePendingObjects();
734 }
735 else
736 {
737 delete topWindow;
738 wxTheApp->SetTopWindow( (wxWindow*) NULL );
739 }
740 }
741 wxTheApp->OnExit();
742 }
743 }
744
745 #if wxUSE_LOG
746 // flush the logged messages if any
747 wxLog *log = wxLog::GetActiveTarget();
748 if (log != NULL && log->HasPendingMessages())
749 log->Flush();
750
751 // continuing to use user defined log target is unsafe from now on because
752 // some resources may be already unavailable, so replace it by something
753 // more safe
754 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
755 if ( oldlog )
756 delete oldlog;
757 #endif // wxUSE_LOG
758
759 wxApp::CleanUp();
760
761 return retValue;
762 }
763