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