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