]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk1/app.cpp
wxGTK now chooses the best visual X offers,
[wxWidgets.git] / src / gtk1 / app.cpp
... / ...
CommitLineData
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
47wxApp *wxTheApp = (wxApp *) NULL;
48wxAppInitializerFunction wxApp::m_appInitFn = (wxAppInitializerFunction) NULL;
49
50#if wxUSE_THREADS
51extern wxList *wxPendingEvents;
52extern wxCriticalSection *wxPendingEventsLocker;
53#endif
54extern wxResourceCache *wxTheResourceCache;
55extern bool g_isIdle;
56
57unsigned 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
129extern void wxFlushResources(void);
130
131//-----------------------------------------------------------------------------
132// global functions
133//-----------------------------------------------------------------------------
134
135void wxExit()
136{
137 gtk_main_quit();
138}
139
140/* forward declaration */
141gint wxapp_idle_callback( gpointer WXUNUSED(data) );
142
143bool 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
182gint 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
216void 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
229static 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
265IMPLEMENT_DYNAMIC_CLASS(wxApp,wxEvtHandler)
266
267BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
268 EVT_IDLE(wxApp::OnIdle)
269END_EVENT_TABLE()
270
271wxApp::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
287wxApp::~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
298bool 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 /* assume 8-bit true or static colors. this really
371 exists. */
372 GdkVisual* vis = gdk_colormap_get_visual( cmap );
373 index = (r >> (5 - vis->red_prec)) << vis->red_shift;
374 index |= (g >> (5 - vis->green_prec)) << vis->green_shift;
375 index |= (b >> (5 - vis->blue_prec)) << vis->blue_shift;
376 }
377
378 m_colorCube[ (r*1024) + (g*32) + b ] = index;
379 }
380 }
381 }
382
383 return TRUE;
384}
385
386bool wxApp::ProcessIdle()
387{
388 wxIdleEvent event;
389 event.SetEventObject( this );
390 ProcessEvent( event );
391
392 return event.MoreRequested();
393}
394
395void wxApp::OnIdle( wxIdleEvent &event )
396{
397 static bool inOnIdle = FALSE;
398
399 /* Avoid recursion (via ProcessEvent default case) */
400 if (inOnIdle)
401 return;
402
403 inOnIdle = TRUE;
404
405#if wxUSE_THREADS
406 /* Resend in the main thread events which have been prepared in other
407 threads */
408 ProcessPendingEvents();
409#endif
410
411 /* 'Garbage' collection of windows deleted with Close(). */
412 DeletePendingObjects();
413
414 /* flush the logged messages if any */
415#if wxUSE_LOG
416 wxLog *log = wxLog::GetActiveTarget();
417 if (log != NULL && log->HasPendingMessages())
418 log->Flush();
419#endif // wxUSE_LOG
420
421 /* Send OnIdle events to all windows */
422 bool needMore = SendIdleEvents();
423
424 if (needMore)
425 event.RequestMore(TRUE);
426
427 inOnIdle = FALSE;
428}
429
430bool wxApp::SendIdleEvents()
431{
432 bool needMore = FALSE;
433
434 wxWindowList::Node* node = wxTopLevelWindows.GetFirst();
435 while (node)
436 {
437 wxWindow* win = node->GetData();
438 if (SendIdleEvents(win))
439 needMore = TRUE;
440 node = node->GetNext();
441 }
442
443 return needMore;
444}
445
446bool wxApp::SendIdleEvents( wxWindow* win )
447{
448 bool needMore = FALSE;
449
450 wxIdleEvent event;
451 event.SetEventObject(win);
452
453 win->OnInternalIdle();
454
455 win->ProcessEvent(event);
456
457 if (event.MoreRequested())
458 needMore = TRUE;
459
460 wxNode* node = win->GetChildren().First();
461 while (node)
462 {
463 wxWindow* win = (wxWindow*) node->Data();
464 if (SendIdleEvents(win))
465 needMore = TRUE;
466
467 node = node->Next();
468 }
469 return needMore ;
470}
471
472int wxApp::MainLoop()
473{
474 gtk_main();
475 return 0;
476}
477
478void wxApp::ExitMainLoop()
479{
480 gtk_main_quit();
481}
482
483bool wxApp::Initialized()
484{
485 return m_initialized;
486}
487
488bool wxApp::Pending()
489{
490 return (gtk_events_pending() > 0);
491}
492
493void wxApp::Dispatch()
494{
495 gtk_main_iteration();
496}
497
498#if wxUSE_THREADS
499void wxApp::ProcessPendingEvents()
500{
501 wxNode *node = wxPendingEvents->First();
502 wxCriticalSectionLocker locker(*wxPendingEventsLocker);
503
504 while (node)
505 {
506 wxEvtHandler *handler = (wxEvtHandler *)node->Data();
507
508 handler->ProcessPendingEvents();
509
510 delete node;
511
512 node = wxPendingEvents->First();
513 }
514}
515#endif // wxUSE_THREADS
516
517void wxApp::DeletePendingObjects()
518{
519 wxNode *node = wxPendingDelete.First();
520 while (node)
521 {
522 wxObject *obj = (wxObject *)node->Data();
523
524 delete obj;
525
526 if (wxPendingDelete.Find(obj))
527 delete node;
528
529 node = wxPendingDelete.First();
530 }
531}
532
533wxWindow *wxApp::GetTopWindow()
534{
535 if (m_topWindow)
536 return m_topWindow;
537 else if (wxTopLevelWindows.GetCount() > 0)
538 return wxTopLevelWindows.GetFirst()->GetData();
539 else
540 return NULL;
541}
542
543void wxApp::SetTopWindow( wxWindow *win )
544{
545 m_topWindow = win;
546}
547
548bool wxApp::Initialize()
549{
550 wxBuffer = new wxChar[BUFSIZ + 512];
551
552 wxClassInfo::InitializeClasses();
553
554 wxSystemSettings::Init();
555
556 // GL: I'm annoyed ... I don't know where to put this and I don't want to
557 // create a module for that as it's part of the core.
558#if wxUSE_THREADS
559 wxPendingEvents = new wxList();
560 wxPendingEventsLocker = new wxCriticalSection();
561#endif
562
563/*
564 wxTheFontNameDirectory = new wxFontNameDirectory;
565 wxTheFontNameDirectory->Initialize();
566*/
567
568 wxTheColourDatabase = new wxColourDatabase( wxKEY_STRING );
569 wxTheColourDatabase->Initialize();
570
571 wxInitializeStockLists();
572 wxInitializeStockObjects();
573
574#if wxUSE_WX_RESOURCES
575 wxTheResourceCache = new wxResourceCache( wxKEY_STRING );
576
577 wxInitializeResourceSystem();
578#endif
579
580 wxImage::InitStandardHandlers();
581
582 wxModule::RegisterModules();
583 if (!wxModule::InitializeModules()) return FALSE;
584
585 return TRUE;
586}
587
588void wxApp::CleanUp()
589{
590 wxModule::CleanUpModules();
591
592#if wxUSE_WX_RESOURCES
593 wxFlushResources();
594
595 if (wxTheResourceCache)
596 delete wxTheResourceCache;
597 wxTheResourceCache = (wxResourceCache*) NULL;
598
599 wxCleanUpResourceSystem();
600#endif
601
602 if (wxTheColourDatabase)
603 delete wxTheColourDatabase;
604 wxTheColourDatabase = (wxColourDatabase*) NULL;
605
606/*
607 if (wxTheFontNameDirectory) delete wxTheFontNameDirectory;
608 wxTheFontNameDirectory = (wxFontNameDirectory*) NULL;
609*/
610
611 wxDeleteStockObjects();
612
613 wxDeleteStockLists();
614
615 wxImage::CleanUpHandlers();
616
617 delete wxTheApp;
618 wxTheApp = (wxApp*) NULL;
619
620 // GL: I'm annoyed ... I don't know where to put this and I don't want to
621 // create a module for that as it's part of the core.
622#if wxUSE_THREADS
623 delete wxPendingEvents;
624 delete wxPendingEventsLocker;
625#endif
626
627 wxSystemSettings::Done();
628
629 delete[] wxBuffer;
630
631 wxClassInfo::CleanUpClasses();
632
633 // check for memory leaks
634#if (defined(__WXDEBUG__) && wxUSE_MEMORY_TRACING) || wxUSE_DEBUG_CONTEXT
635 if (wxDebugContext::CountObjectsLeft() > 0)
636 {
637 wxLogDebug(_T("There were memory leaks.\n"));
638 wxDebugContext::Dump();
639 wxDebugContext::PrintStatistics();
640 }
641#endif // Debug
642
643#if wxUSE_LOG
644 // do this as the very last thing because everything else can log messages
645 wxLog::DontCreateOnDemand();
646
647 wxLog *oldLog = wxLog::SetActiveTarget( (wxLog*) NULL );
648 if (oldLog)
649 delete oldLog;
650}
651
652wxLog *wxApp::CreateLogTarget()
653{
654 return new wxLogGui;
655}
656#endif // wxUSE_LOG
657
658//-----------------------------------------------------------------------------
659// wxEntry
660//-----------------------------------------------------------------------------
661
662int wxEntry( int argc, char *argv[] )
663{
664 gtk_set_locale();
665
666 if (!wxOKlibc()) wxConvCurrent = &wxConvLocal;
667
668 gtk_init( &argc, &argv );
669
670 wxSetDetectableAutoRepeat( TRUE );
671
672 if (!wxApp::Initialize())
673 return -1;
674
675 if (!wxTheApp)
676 {
677 wxCHECK_MSG( wxApp::GetInitializerFunction(), -1,
678 _T("wxWindows error: No initializer - use IMPLEMENT_APP macro.\n") );
679
680 wxAppInitializerFunction app_ini = wxApp::GetInitializerFunction();
681
682 wxObject *test_app = app_ini();
683
684 wxTheApp = (wxApp*) test_app;
685 }
686
687 wxCHECK_MSG( wxTheApp, -1, _T("wxWindows error: no application object") );
688
689 wxTheApp->argc = argc;
690 wxTheApp->argv = argv;
691
692 wxString name(wxFileNameFromPath(argv[0]));
693 wxStripExtension( name );
694 wxTheApp->SetAppName( name );
695
696 int retValue = 0;
697
698 if ( !wxTheApp->OnInitGui() )
699 retValue = -1;
700
701 // Here frames insert themselves automatically into wxTopLevelWindows by
702 // getting created in OnInit().
703 if ( retValue == 0 )
704 {
705 if ( !wxTheApp->OnInit() )
706 retValue = -1;
707 }
708
709 if ( retValue == 0 )
710 {
711 /* delete pending toplevel windows (typically a single
712 dialog) so that, if there isn't any left, we don't
713 call OnRun() */
714 wxTheApp->DeletePendingObjects();
715
716 wxTheApp->m_initialized = wxTopLevelWindows.GetCount() != 0;
717
718 if (wxTheApp->Initialized())
719 {
720 retValue = wxTheApp->OnRun();
721
722 wxWindow *topWindow = wxTheApp->GetTopWindow();
723 if (topWindow)
724 {
725 /* Forcibly delete the window. */
726 if (topWindow->IsKindOf(CLASSINFO(wxFrame)) ||
727 topWindow->IsKindOf(CLASSINFO(wxDialog)) )
728 {
729 topWindow->Close( TRUE );
730 wxTheApp->DeletePendingObjects();
731 }
732 else
733 {
734 delete topWindow;
735 wxTheApp->SetTopWindow( (wxWindow*) NULL );
736 }
737 }
738 wxTheApp->OnExit();
739 }
740 }
741
742#if wxUSE_LOG
743 // flush the logged messages if any
744 wxLog *log = wxLog::GetActiveTarget();
745 if (log != NULL && log->HasPendingMessages())
746 log->Flush();
747
748 // continuing to use user defined log target is unsafe from now on because
749 // some resources may be already unavailable, so replace it by something
750 // more safe
751 wxLog *oldlog = wxLog::SetActiveTarget(new wxLogStderr);
752 if ( oldlog )
753 delete oldlog;
754#endif // wxUSE_LOG
755
756 wxApp::CleanUp();
757
758 return retValue;
759}
760