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