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