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