1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/x11/app.cpp
3// Purpose:     wxApp
4// Author:      Julian Smart
5// Modified by:
6// Created:     17/09/98
7// RCS-ID:      $Id: app.cpp 61713 2009-08-20 00:01:55Z VZ $
8// Copyright:   (c) Julian Smart
9// Licence:     wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12// for compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#include "wx/app.h"
16
17#ifndef WX_PRECOMP
18    #include "wx/hash.h"
19    #include "wx/intl.h"
20    #include "wx/log.h"
21    #include "wx/utils.h"
22    #include "wx/frame.h"
23    #include "wx/icon.h"
24    #include "wx/dialog.h"
25    #include "wx/timer.h"
26    #include "wx/memory.h"
27    #include "wx/gdicmn.h"
28    #include "wx/module.h"
29#endif
30
31#include "wx/evtloop.h"
32#include "wx/filename.h"
33
34#include "wx/univ/theme.h"
35#include "wx/univ/renderer.h"
36
37#if wxUSE_THREADS
38    #include "wx/thread.h"
39#endif
40
41#include "wx/x11/private.h"
42
43#include <string.h>
44
45//------------------------------------------------------------------------
46//   global data
47//------------------------------------------------------------------------
48
49wxWindowHash *wxWidgetHashTable = NULL;
50wxWindowHash *wxClientWidgetHashTable = NULL;
51
52static bool g_showIconic = false;
53static wxSize g_initialSize = wxDefaultSize;
54
55// This is required for wxFocusEvent::SetWindow(). It will only
56// work for focus events which we provoke ourselves (by calling
57// SetFocus()). It will not work for those events, which X11
58// generates itself.
59static wxWindow *g_nextFocus = NULL;
60static wxWindow *g_prevFocus = NULL;
61
62//------------------------------------------------------------------------
63//   X11 error handling
64//------------------------------------------------------------------------
65
66#ifdef __WXDEBUG__
67typedef int (*XErrorHandlerFunc)(Display *, XErrorEvent *);
68
69XErrorHandlerFunc gs_pfnXErrorHandler = 0;
70
71static int wxXErrorHandler(Display *dpy, XErrorEvent *xevent)
72{
73    // just forward to the default handler for now
74    if (gs_pfnXErrorHandler)
75        return gs_pfnXErrorHandler(dpy, xevent);
76    else
77        return 0;
78}
79#endif // __WXDEBUG__
80
81//------------------------------------------------------------------------
82//   wxApp
83//------------------------------------------------------------------------
84
85long wxApp::sm_lastMessageTime = 0;
86
87IMPLEMENT_DYNAMIC_CLASS(wxApp, wxEvtHandler)
88
89BEGIN_EVENT_TABLE(wxApp, wxEvtHandler)
90    EVT_IDLE(wxAppBase::OnIdle)
91END_EVENT_TABLE()
92
93bool wxApp::Initialize(int& argC, wxChar **argV)
94{
95#if defined(__WXDEBUG__) && !wxUSE_NANOX
96    // install the X error handler
97    gs_pfnXErrorHandler = XSetErrorHandler( wxXErrorHandler );
98#endif // __WXDEBUG__
99
100    wxString displayName;
101    bool syncDisplay = false;
102
103    int argCOrig = argC;
104    for ( int i = 0; i < argCOrig; i++ )
105    {
106        if (wxStrcmp( argV[i], _T("-display") ) == 0)
107        {
108            if (i < (argCOrig - 1))
109            {
110                argV[i++] = NULL;
111
112                displayName = argV[i];
113
114                argV[i] = NULL;
115                argC -= 2;
116            }
117        }
118        else if (wxStrcmp( argV[i], _T("-geometry") ) == 0)
119        {
120            if (i < (argCOrig - 1))
121            {
122                argV[i++] = NULL;
123
124                int w, h;
125                if (wxSscanf(argV[i], _T("%dx%d"), &w, &h) != 2)
126                {
127                    wxLogError( _("Invalid geometry specification '%s'"),
128                                wxString(argV[i]).c_str() );
129                }
130                else
131                {
132                    g_initialSize = wxSize(w, h);
133                }
134
135                argV[i] = NULL;
136                argC -= 2;
137            }
138        }
139        else if (wxStrcmp( argV[i], _T("-sync") ) == 0)
140        {
141            syncDisplay = true;
142
143            argV[i] = NULL;
144            argC--;
145        }
146        else if (wxStrcmp( argV[i], _T("-iconic") ) == 0)
147        {
148            g_showIconic = true;
149
150            argV[i] = NULL;
151            argC--;
152        }
153    }
154
155    if ( argC != argCOrig )
156    {
157        // remove the arguments we consumed
158        for ( int i = 0; i < argC; i++ )
159        {
160            while ( !argV[i] )
161            {
162                memmove(argV + i, argV + i + 1, (argCOrig - i)*sizeof(wxChar *));
163            }
164        }
165    }
166
167    // open and set up the X11 display
168    if ( !wxSetDisplay(displayName) )
169    {
170        wxLogError(_("wxWidgets could not open display. Exiting."));
171        return false;
172    }
173
174    Display *dpy = wxGlobalDisplay();
175    if (syncDisplay)
176        XSynchronize(dpy, True);
177
178    XSelectInput(dpy, XDefaultRootWindow(dpy), PropertyChangeMask);
179
180    wxSetDetectableAutoRepeat( true );
181
182
183    if ( !wxAppBase::Initialize(argC, argV) )
184        return false;
185
186#if wxUSE_UNICODE
187    // Glib's type system required by Pango
188    g_type_init();
189#endif
190
191#if wxUSE_INTL
192    wxFont::SetDefaultEncoding(wxLocale::GetSystemEncoding());
193#endif
194
195    wxWidgetHashTable = new wxWindowHash;
196    wxClientWidgetHashTable = new wxWindowHash;
197
198    return true;
199}
200
201void wxApp::CleanUp()
202{
203    delete wxWidgetHashTable;
204    wxWidgetHashTable = NULL;
205    delete wxClientWidgetHashTable;
206    wxClientWidgetHashTable = NULL;
207
208    wxAppBase::CleanUp();
209}
210
211wxApp::wxApp()
212{
213    m_mainColormap = NULL;
214    m_topLevelWidget = NULL;
215    m_maxRequestSize = 0;
216    m_showIconic = false;
217    m_initialSize = wxDefaultSize;
218
219#if !wxUSE_NANOX
220    m_visualInfo = NULL;
221#endif
222}
223
224wxApp::~wxApp()
225{
226#if !wxUSE_NANOX
227    delete m_visualInfo;
228#endif
229}
230
231#if !wxUSE_NANOX
232
233//-----------------------------------------------------------------------
234// X11 predicate function for exposure compression
235//-----------------------------------------------------------------------
236
237struct wxExposeInfo
238{
239    Window window;
240    Bool found_non_matching;
241};
242
243extern "C"
244Bool wxX11ExposePredicate (Display *display, XEvent *xevent, XPointer arg)
245{
246    wxExposeInfo *info = (wxExposeInfo*) arg;
247
248    if (info->found_non_matching)
249       return FALSE;
250
251    if (xevent->xany.type != Expose)
252    {
253        info->found_non_matching = true;
254        return FALSE;
255    }
256
257    if (xevent->xexpose.window != info->window)
258    {
259        info->found_non_matching = true;
260        return FALSE;
261    }
262
263    return TRUE;
264}
265
266#endif // wxUSE_NANOX
267
268//-----------------------------------------------------------------------
269// Processes an X event, returning true if the event was processed.
270//-----------------------------------------------------------------------
271
272bool wxApp::ProcessXEvent(WXEvent* _event)
273{
274    XEvent* event = (XEvent*) _event;
275
276    wxWindow* win = NULL;
277    Window window = XEventGetWindow(event);
278#if 0
279    Window actualWindow = window;
280#endif
281
282    // Find the first wxWindow that corresponds to this event window
283    // Because we're receiving events after a window
284    // has been destroyed, assume a 1:1 match between
285    // Window and wxWindow, so if it's not in the table,
286    // it must have been destroyed.
287
288    win = wxGetWindowFromTable(window);
289    if (!win)
290    {
291#if wxUSE_TWO_WINDOWS
292        win = wxGetClientWindowFromTable(window);
293        if (!win)
294#endif
295            return false;
296    }
297
298#ifdef __WXDEBUG__
299    wxString windowClass = win->GetClassInfo()->GetClassName();
300#endif
301
302    switch (event->type)
303    {
304        case Expose:
305        {
306#if wxUSE_TWO_WINDOWS && !wxUSE_NANOX
307            if (event->xexpose.window != (Window)win->GetClientAreaWindow())
308            {
309                XEvent tmp_event;
310                wxExposeInfo info;
311                info.window = event->xexpose.window;
312                info.found_non_matching = false;
313                while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event, wxX11ExposePredicate, (XPointer) &info ))
314                {
315                    // Don't worry about optimizing redrawing the border etc.
316                }
317                win->NeedUpdateNcAreaInIdle();
318            }
319            else
320#endif
321            {
322                win->GetUpdateRegion().Union( XExposeEventGetX(event), XExposeEventGetY(event),
323                                              XExposeEventGetWidth(event), XExposeEventGetHeight(event));
324                win->GetClearRegion().Union( XExposeEventGetX(event), XExposeEventGetY(event),
325                                         XExposeEventGetWidth(event), XExposeEventGetHeight(event));
326
327#if !wxUSE_NANOX
328                XEvent tmp_event;
329                wxExposeInfo info;
330                info.window = event->xexpose.window;
331                info.found_non_matching = false;
332                while (XCheckIfEvent( wxGlobalDisplay(), &tmp_event, wxX11ExposePredicate, (XPointer) &info ))
333                {
334                    win->GetUpdateRegion().Union( tmp_event.xexpose.x, tmp_event.xexpose.y,
335                                                  tmp_event.xexpose.width, tmp_event.xexpose.height );
336
337                    win->GetClearRegion().Union( tmp_event.xexpose.x, tmp_event.xexpose.y,
338                                                 tmp_event.xexpose.width, tmp_event.xexpose.height );
339                }
340#endif
341
342                // This simplifies the expose and clear areas to simple
343                // rectangles.
344                win->GetUpdateRegion() = win->GetUpdateRegion().GetBox();
345                win->GetClearRegion() = win->GetClearRegion().GetBox();
346
347                // If we only have one X11 window, always indicate
348                // that borders might have to be redrawn.
349                if (win->GetMainWindow() == win->GetClientAreaWindow())
350                    win->NeedUpdateNcAreaInIdle();
351
352                // Only erase background, paint in idle time.
353                win->SendEraseEvents();
354
355                // EXPERIMENT
356                //win->Update();
357            }
358
359            return true;
360        }
361
362#if !wxUSE_NANOX
363        case GraphicsExpose:
364        {
365            wxLogTrace( _T("expose"), _T("GraphicsExpose from %s"), win->GetName().c_str());
366
367            win->GetUpdateRegion().Union( event->xgraphicsexpose.x, event->xgraphicsexpose.y,
368                                          event->xgraphicsexpose.width, event->xgraphicsexpose.height);
369
370            win->GetClearRegion().Union( event->xgraphicsexpose.x, event->xgraphicsexpose.y,
371                                         event->xgraphicsexpose.width, event->xgraphicsexpose.height);
372
373            if (event->xgraphicsexpose.count == 0)
374            {
375                // Only erase background, paint in idle time.
376                win->SendEraseEvents();
377                // win->Update();
378            }
379
380            return true;
381        }
382#endif
383
384        case KeyPress:
385        {
386            if (!win->IsEnabled())
387                return false;
388
389            wxKeyEvent keyEvent(wxEVT_KEY_DOWN);
390            wxTranslateKeyEvent(keyEvent, win, window, event);
391
392            // wxLogDebug( "OnKey from %s", win->GetName().c_str() );
393
394            // We didn't process wxEVT_KEY_DOWN, so send wxEVT_CHAR
395            if (win->GetEventHandler()->ProcessEvent( keyEvent ))
396                return true;
397
398            keyEvent.SetEventType(wxEVT_CHAR);
399            // Do the translation again, retaining the ASCII
400            // code.
401            if (wxTranslateKeyEvent(keyEvent, win, window, event, true) &&
402                win->GetEventHandler()->ProcessEvent( keyEvent ))
403                return true;
404
405            if ( (keyEvent.m_keyCode == WXK_TAB) &&
406                 win->GetParent() && (win->GetParent()->HasFlag( wxTAB_TRAVERSAL)) )
407            {
408                wxNavigationKeyEvent new_event;
409                new_event.SetEventObject( win->GetParent() );
410                /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
411                new_event.SetDirection( (keyEvent.m_keyCode == WXK_TAB) );
412                /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
413                new_event.SetWindowChange( keyEvent.ControlDown() );
414                new_event.SetCurrentFocus( win );
415                return win->GetParent()->GetEventHandler()->ProcessEvent( new_event );
416            }
417
418            return false;
419        }
420        case KeyRelease:
421        {
422            if (!win->IsEnabled())
423                return false;
424
425            wxKeyEvent keyEvent(wxEVT_KEY_UP);
426            wxTranslateKeyEvent(keyEvent, win, window, event);
427
428            return win->GetEventHandler()->ProcessEvent( keyEvent );
429        }
430        case ConfigureNotify:
431        {
432#if wxUSE_NANOX
433            if (event->update.utype == GR_UPDATE_SIZE)
434#endif
435            {
436                wxTopLevelWindow *tlw = wxDynamicCast(win, wxTopLevelWindow);
437                if ( tlw )
438                {
439                    tlw->SetConfigureGeometry( XConfigureEventGetX(event), XConfigureEventGetY(event),
440                        XConfigureEventGetWidth(event), XConfigureEventGetHeight(event) );
441                }
442
443                if ( tlw && tlw->IsShown() )
444                {
445                    tlw->SetNeedResizeInIdle();
446                }
447                else
448                {
449                    wxSizeEvent sizeEvent( wxSize(XConfigureEventGetWidth(event), XConfigureEventGetHeight(event)), win->GetId() );
450                    sizeEvent.SetEventObject( win );
451
452                    return win->GetEventHandler()->ProcessEvent( sizeEvent );
453                }
454            }
455            return false;
456        }
457#if !wxUSE_NANOX
458        case PropertyNotify:
459        {
460            //wxLogDebug("PropertyNotify: %s", windowClass.c_str());
461            return HandlePropertyChange(_event);
462        }
463        case ClientMessage:
464        {
465            if (!win->IsEnabled())
466                return false;
467
468            Atom wm_delete_window = XInternAtom(wxGlobalDisplay(), "WM_DELETE_WINDOW", True);
469            Atom wm_protocols = XInternAtom(wxGlobalDisplay(), "WM_PROTOCOLS", True);
470
471            if (event->xclient.message_type == wm_protocols)
472            {
473                if ((Atom) (event->xclient.data.l[0]) == wm_delete_window)
474                {
475                    win->Close(false);
476                    return true;
477                }
478            }
479            return false;
480        }
481#if 0
482        case DestroyNotify:
483        {
484            printf( "destroy from %s\n", win->GetName().c_str() );
485            break;
486        }
487        case CreateNotify:
488        {
489            printf( "create from %s\n", win->GetName().c_str() );
490            break;
491        }
492        case MapRequest:
493        {
494            printf( "map request from %s\n", win->GetName().c_str() );
495            break;
496        }
497        case ResizeRequest:
498        {
499            printf( "resize request from %s\n", win->GetName().c_str() );
500
501            Display *disp = (Display*) wxGetDisplay();
502            XEvent report;
503
504            //  to avoid flicker
505            report = * event;
506            while( XCheckTypedWindowEvent (disp, actualWindow, ResizeRequest, &report));
507
508            wxSize sz = win->GetSize();
509            wxSizeEvent sizeEvent(sz, win->GetId());
510            sizeEvent.SetEventObject(win);
511
512            return win->GetEventHandler()->ProcessEvent( sizeEvent );
513        }
514#endif
515#endif
516#if wxUSE_NANOX
517        case GR_EVENT_TYPE_CLOSE_REQ:
518        {
519            if (win)
520            {
521                win->Close(false);
522                return true;
523            }
524            return false;
525            break;
526        }
527#endif
528        case EnterNotify:
529        case LeaveNotify:
530        case ButtonPress:
531        case ButtonRelease:
532        case MotionNotify:
533        {
534            if (!win->IsEnabled())
535                return false;
536
537            // Here we check if the top level window is
538            // disabled, which is one aspect of modality.
539            wxWindow *tlw = win;
540            while (tlw && !tlw->IsTopLevel())
541                tlw = tlw->GetParent();
542            if (tlw && !tlw->IsEnabled())
543                return false;
544
545            if (event->type == ButtonPress)
546            {
547                if ((win != wxWindow::FindFocus()) && win->AcceptsFocus())
548                {
549                    // This might actually be done in wxWindow::SetFocus()
550                    // and not here. TODO.
551                    g_prevFocus = wxWindow::FindFocus();
552                    g_nextFocus = win;
553
554                    wxLogTrace( _T("focus"), _T("About to call SetFocus on %s of type %s due to button press"), win->GetName().c_str(), win->GetClassInfo()->GetClassName() );
555
556                    // Record the fact that this window is
557                    // getting the focus, because we'll need to
558                    // check if its parent is getting a bogus
559                    // focus and duly ignore it.
560                    // TODO: may need to have this code in SetFocus, too.
561                    extern wxWindow* g_GettingFocus;
562                    g_GettingFocus = win;
563                    win->SetFocus();
564                }
565            }
566
567#if !wxUSE_NANOX
568            if (event->type == LeaveNotify || event->type == EnterNotify)
569            {
570                // Throw out NotifyGrab and NotifyUngrab
571                if (event->xcrossing.mode != NotifyNormal)
572                    return false;
573            }
574#endif
575            wxMouseEvent wxevent;
576            wxTranslateMouseEvent(wxevent, win, window, event);
577            return win->GetEventHandler()->ProcessEvent( wxevent );
578        }
579        case FocusIn:
580#if !wxUSE_NANOX
581            if ((event->xfocus.detail != NotifyPointer) &&
582                (event->xfocus.mode == NotifyNormal))
583#endif
584            {
585                wxLogTrace( _T("focus"), _T("FocusIn from %s of type %s"), win->GetName().c_str(), win->GetClassInfo()->GetClassName() );
586
587                extern wxWindow* g_GettingFocus;
588                if (g_GettingFocus && g_GettingFocus->GetParent() == win)
589                {
590                    // Ignore this, this can be a spurious FocusIn
591                    // caused by a child having its focus set.
592                    g_GettingFocus = NULL;
593                    wxLogTrace( _T("focus"), _T("FocusIn from %s of type %s being deliberately ignored"), win->GetName().c_str(), win->GetClassInfo()->GetClassName() );
594                    return true;
595                }
596                else
597                {
598                    wxFocusEvent focusEvent(wxEVT_SET_FOCUS, win->GetId());
599                    focusEvent.SetEventObject(win);
600                    focusEvent.SetWindow( g_prevFocus );
601                    g_prevFocus = NULL;
602
603                    return win->GetEventHandler()->ProcessEvent(focusEvent);
604                }
605            }
606            return false;
607
608        case FocusOut:
609#if !wxUSE_NANOX
610            if ((event->xfocus.detail != NotifyPointer) &&
611                (event->xfocus.mode == NotifyNormal))
612#endif
613            {
614                wxLogTrace( _T("focus"), _T("FocusOut from %s of type %s"), win->GetName().c_str(), win->GetClassInfo()->GetClassName() );
615
616                wxFocusEvent focusEvent(wxEVT_KILL_FOCUS, win->GetId());
617                focusEvent.SetEventObject(win);
618                focusEvent.SetWindow( g_nextFocus );
619                g_nextFocus = NULL;
620                return win->GetEventHandler()->ProcessEvent(focusEvent);
621            }
622            return false;
623
624#ifdef __WXDEBUG__
625        default:
626            //wxString eventName = wxGetXEventName(XEvent& event);
627            //wxLogDebug(wxT("Event %s not handled"), eventName.c_str());
628            break;
629#endif // __WXDEBUG__
630    }
631
632    return false;
633}
634
635// This should be redefined in a derived class for
636// handling property change events for XAtom IPC.
637bool wxApp::HandlePropertyChange(WXEvent *event)
638{
639    // by default do nothing special
640    // TODO: what to do for X11
641    // XtDispatchEvent((XEvent*) event);
642    return false;
643}
644
645void wxApp::WakeUpIdle()
646{
647    // TODO: use wxMotif implementation?
648
649    // Wake up the idle handler processor, even if it is in another thread...
650}
651
652
653// Create display, and other initialization
654bool wxApp::OnInitGui()
655{
656#if wxUSE_LOG
657    // Eventually this line will be removed, but for
658    // now we don't want to try popping up a dialog
659    // for error messages.
660    delete wxLog::SetActiveTarget(new wxLogStderr);
661#endif
662
663    if (!wxAppBase::OnInitGui())
664        return false;
665
666    Display *dpy = wxGlobalDisplay();
667    GetMainColormap(dpy);
668
669    m_maxRequestSize = XMaxRequestSize(dpy);
670
671#if !wxUSE_NANOX
672    m_visualInfo = new wxXVisualInfo;
673    wxFillXVisualInfo(m_visualInfo, dpy);
674#endif
675
676    return true;
677}
678
679#if wxUSE_UNICODE
680
681#include <pango/pango.h>
682#include <pango/pangox.h>
683#ifdef HAVE_PANGO_XFT
684    #include <pango/pangoxft.h>
685#endif
686
687PangoContext* wxApp::GetPangoContext()
688{
689    static PangoContext *s_pangoContext = NULL;
690    if ( !s_pangoContext )
691    {
692        Display *dpy = wxGlobalDisplay();
693
694#ifdef HAVE_PANGO_XFT
695        int xscreen = DefaultScreen(dpy);
696        static int use_xft = -1;
697        if (use_xft == -1)
698        {
699            wxString val = wxGetenv( L"GDK_USE_XFT" );
700            use_xft = val == L"1";
701        }
702
703        if (use_xft)
704            s_pangoContext = pango_xft_get_context(dpy, xscreen);
705        else
706#endif // HAVE_PANGO_XFT
707            s_pangoContext = pango_x_get_context(dpy);
708
709        if (!PANGO_IS_CONTEXT(s_pangoContext))
710            wxLogError( wxT("No pango context.") );
711    }
712
713    return s_pangoContext;
714}
715
716#endif // wxUSE_UNICODE
717
718WXColormap wxApp::GetMainColormap(WXDisplay* display)
719{
720    if (!display) /* Must be called first with non-NULL display */
721        return m_mainColormap;
722
723    int defaultScreen = DefaultScreen((Display*) display);
724    Screen* screen = XScreenOfDisplay((Display*) display, defaultScreen);
725
726    Colormap c = DefaultColormapOfScreen(screen);
727
728    if (!m_mainColormap)
729        m_mainColormap = (WXColormap) c;
730
731    return (WXColormap) c;
732}
733
734Window wxGetWindowParent(Window window)
735{
736    wxASSERT_MSG( window, _T("invalid window") );
737
738    return (Window) 0;
739
740#ifndef __VMS
741   // VMS chokes on unreacheable code
742   Window parent, root = 0;
743#if wxUSE_NANOX
744    int noChildren = 0;
745#else
746    unsigned int noChildren = 0;
747#endif
748    Window* children = NULL;
749
750    // #define XQueryTree(d,w,r,p,c,nc)     GrQueryTree(w,p,c,nc)
751    int res = 1;
752#if !wxUSE_NANOX
753    res =
754#endif
755        XQueryTree((Display*) wxGetDisplay(), window, & root, & parent,
756             & children, & noChildren);
757    if (children)
758        XFree(children);
759    if (res)
760        return parent;
761    else
762        return (Window) 0;
763#endif
764}
765
766void wxApp::Exit()
767{
768    wxApp::CleanUp();
769
770    wxAppConsole::Exit();
771}
772
773// Yield to other processes
774
775bool wxApp::Yield(bool onlyIfNeeded)
776{
777    // Sometimes only 2 yields seem
778    // to do the trick, e.g. in the
779    // progress dialog
780    int i;
781    for (i = 0; i < 2; i++)
782    {
783        static bool s_inYield = false;
784
785        if ( s_inYield )
786        {
787            if ( !onlyIfNeeded )
788            {
789                wxFAIL_MSG( wxT("wxYield called recursively" ) );
790            }
791
792            return false;
793        }
794
795        s_inYield = true;
796
797        // Make sure we have an event loop object,
798        // or Pending/Dispatch will fail
799        wxEventLoopGuarantor dummyLoopIfNeeded;
800
801        // Call dispatch at least once so that sockets
802        // can be tested
803        wxTheApp->Dispatch();
804
805        while (wxTheApp && wxTheApp->Pending())
806            wxTheApp->Dispatch();
807
808#if wxUSE_TIMER
809        wxTimer::NotifyTimers();
810#endif
811        ProcessIdle();
812
813        s_inYield = false;
814    }
815
816    return true;
817}
818
819#ifdef __WXDEBUG__
820
821void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
822{
823    // While the GUI isn't working that well, just print out the
824    // message.
825#if 1
826    wxAppBase::OnAssert(file, line, cond, msg);
827#else
828    wxString msg2;
829    msg2.Printf("At file %s:%d: %s", file, line, msg);
830    wxLogDebug(msg2);
831#endif
832}
833
834#endif // __WXDEBUG__
835