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