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