]> git.saurik.com Git - wxWidgets.git/commitdiff
streamlining OSX event support first step, see #11805, see #11797
authorStefan Csomor <csomor@advancedconcepts.ch>
Mon, 15 Mar 2010 15:40:47 +0000 (15:40 +0000)
committerStefan Csomor <csomor@advancedconcepts.ch>
Mon, 15 Mar 2010 15:40:47 +0000 (15:40 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@63687 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

include/wx/osx/carbon/evtloop.h
include/wx/osx/cocoa/evtloop.h
include/wx/osx/evtloop.h
src/osx/carbon/evtloop.cpp
src/osx/cocoa/evtloop.mm
src/osx/core/evtloop_cf.cpp
src/osx/iphone/evtloop.mm

index 5e881875cc2fb37b20bceaab3125539694332acc..0824ca263696b7b982fcd74f885fad682822b977 100644 (file)
@@ -20,22 +20,9 @@ class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxCFEventLoop
 public:
     wxGUIEventLoop();
 
-    // implement/override base class pure virtual
-    virtual bool Pending() const;
-    virtual bool Dispatch();
-    virtual int DispatchTimeout(unsigned long timeout);
-
-    virtual void WakeUp();
-    virtual bool YieldFor(long eventsToProcess);
-
 protected:
-    virtual CFRunLoopRef CFGetCurrentRunLoop() const;
-
-private:
-    // dispatch an event and release it
-    void DispatchAndReleaseEvent(EventRef event);
+    virtual int DoDispatchTimeout(unsigned long timeout);
 
-    double      m_sleepTime;
 };
 
 #endif // _WX_MAC_CARBON_EVTLOOP_H_
index 8fc7c332581d1d9bc81b318211bbe8109f22ed2a..be4e788bbb0fb778c5eefc60daa488314393d699 100644 (file)
@@ -15,20 +15,10 @@ class WXDLLIMPEXP_BASE wxGUIEventLoop : public wxCFEventLoop
 {
 public:
     wxGUIEventLoop();
-
-    // implement/override base class pure virtual
-    virtual bool Pending() const;
-    virtual bool Dispatch();
-    virtual int DispatchTimeout(unsigned long timeout);
-
-    virtual void WakeUp();
-    virtual bool YieldFor(long eventsToProcess);
-
+    
 protected:
-    virtual CFRunLoopRef CFGetCurrentRunLoop() const;
-
-private:
-    double m_sleepTime;
+    virtual int DoDispatchTimeout(unsigned long timeout);
+    
 };
 
 #endif // _WX_OSX_COCOA_EVTLOOP_H_
index a295706f588a26cc0efc0f3a8d74ff28eeb479ad..5ddb819d5533117b648d1daab5200492e178ad65 100644 (file)
 
 typedef struct __CFRunLoop * CFRunLoopRef;
 
-class WXDLLIMPEXP_BASE wxCFEventLoop : public wxEventLoopManual
+class WXDLLIMPEXP_BASE wxCFEventLoop : public wxEventLoopBase
 {
 public:
+    wxCFEventLoop();
+    virtual ~wxCFEventLoop();
+
+    // enters a loop calling OnNextIteration(), Pending() and Dispatch() and
+    // terminating when Exit() is called
+    virtual int Run();
+    
+    // sets the "should exit" flag and wakes up the loop so that it terminates
+    // soon
+    virtual void Exit(int rc = 0);
+
+    // return true if any events are available
+    virtual bool Pending() const;
+    
+    // dispatch a single event, return false if we should exit from the loop
+    virtual bool Dispatch();
+    
+    // same as Dispatch() but doesn't wait for longer than the specified (in
+    // ms) timeout, return true if an event was processed, false if we should
+    // exit the loop or -1 if timeout expired
+    virtual int DispatchTimeout(unsigned long timeout);
+
+    // implement this to wake up the loop: usually done by posting a dummy event
+    // to it (can be called from non main thread)
+    virtual void WakeUp();
+    
+    virtual bool YieldFor(long eventsToProcess);
+
 #if wxUSE_EVENTLOOP_SOURCE
     virtual wxEventLoopSource *
       AddSourceForFD(int fd, wxEventLoopSourceHandler *handler, int flags);
@@ -25,7 +53,24 @@ public:
 
 protected:
     // get the currently executing CFRunLoop
-    virtual CFRunLoopRef CFGetCurrentRunLoop() const = 0;
+    virtual CFRunLoopRef CFGetCurrentRunLoop() const;
+
+    virtual int DoDispatchTimeout(unsigned long timeout);
+
+    double m_sleepTime;
+
+    // should we exit the loop?
+    bool m_shouldExit;
+
+    // the loop exit code
+    int m_exitcode;
+
+private:
+    // process all already pending events and dispatch a new one (blocking
+    // until it appears in the event queue if necessary)
+    //
+    // returns the return value of DoDispatchTimeout()
+    int DoProcessEvents();
 };
 
 #if wxUSE_GUI
index 928e7f90a76c359f8564e3f5fbc987e5460d7cf3..abaafd3181f7d4a72946575c58c656aee273ef54 100644 (file)
 
 wxGUIEventLoop::wxGUIEventLoop()
 {
-    m_sleepTime = kEventDurationNoWait;
 }
 
-void wxGUIEventLoop::WakeUp()
-{
-    extern void wxMacWakeUp();
-
-    wxMacWakeUp();
-}
-
-CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
-{
-    return CFRunLoopGetCurrent();
-}
-
-void wxGUIEventLoop::DispatchAndReleaseEvent(EventRef theEvent)
+static void DispatchAndReleaseEvent(EventRef theEvent)
 {
     if ( wxTheApp )
         wxTheApp->MacSetCurrentEvent( theEvent, NULL );
@@ -66,64 +53,7 @@ void wxGUIEventLoop::DispatchAndReleaseEvent(EventRef theEvent)
     ReleaseEvent( theEvent );
 }
 
-bool wxGUIEventLoop::Pending() const
-{
-    EventRef theEvent;
-
-    return ReceiveNextEvent
-           (
-            0,          // we want any event at all so we don't specify neither
-            NULL,       // the number of event types nor the types themselves
-            kEventDurationNoWait,
-            false,      // don't remove the event from queue
-            &theEvent
-           ) == noErr;
-}
-
-bool wxGUIEventLoop::Dispatch()
-{
-    if ( !wxTheApp )
-        return false;
-
-    wxMacAutoreleasePool autoreleasepool;
-
-    EventRef theEvent;
-
-    OSStatus status = ReceiveNextEvent(0, NULL, m_sleepTime, true, &theEvent) ;
-
-    switch (status)
-    {
-        case eventLoopTimedOutErr :
-            // process pending wx events before sending idle events
-            wxTheApp->ProcessPendingEvents();
-            if ( wxTheApp->ProcessIdle() )
-                m_sleepTime = kEventDurationNoWait ;
-            else
-            {
-                m_sleepTime = kEventDurationSecond;
-#if wxUSE_THREADS
-                wxMutexGuiLeave();
-                wxMilliSleep(20);
-                wxMutexGuiEnter();
-#endif
-            }
-            break;
-
-        case eventLoopQuitErr :
-            // according to QA1061 this may also occur
-            // when a WakeUp Process is executed
-            break;
-
-        default:
-            DispatchAndReleaseEvent(theEvent);
-            m_sleepTime = kEventDurationNoWait ;
-            break;
-    }
-
-    return true;
-}
-
-int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
+int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
 {
     EventRef event;
     OSStatus status = ReceiveNextEvent(0, NULL, timeout/1000, true, &event);
@@ -137,6 +67,8 @@ int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
             return -1;
 
         case eventLoopQuitErr:
+            // according to QA1061 this may also occur
+            // when a WakeUp Process is executed
             return 0;
 
         case noErr:
@@ -144,44 +76,3 @@ int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
             return 1;
     }
 }
-
-bool wxGUIEventLoop::YieldFor(long eventsToProcess)
-{
-#if wxUSE_THREADS
-    // Yielding from a non-gui thread needs to bail out, otherwise we end up
-    // possibly sending events in the thread too.
-    if ( !wxThread::IsMain() )
-    {
-        return true;
-    }
-#endif // wxUSE_THREADS
-
-    m_isInsideYield = true;
-    m_eventsToProcessInsideYield = eventsToProcess;
-
-#if wxUSE_LOG
-    // disable log flushing from here because a call to wxYield() shouldn't
-    // normally result in message boxes popping up &c
-    wxLog::Suspend();
-#endif // wxUSE_LOG
-
-    // process all pending events:
-    while ( Pending() )
-        Dispatch();
-
-    // it's necessary to call ProcessIdle() to update the frames sizes which
-    // might have been changed (it also will update other things set from
-    // OnUpdateUI() which is a nice (and desired) side effect)
-    while ( ProcessIdle() ) {}
-
-    // if there are pending events, we must process them.
-    if (wxTheApp)
-        wxTheApp->ProcessPendingEvents();
-    
-#if wxUSE_LOG
-    wxLog::Resume();
-#endif // wxUSE_LOG
-    m_isInsideYield = false;
-
-    return true;
-}
index d22e47a0e006b09aef9818579ae4c392b57b5973..69c550b6d05cbe8c7e40b83291acf0c401203e7f 100644 (file)
@@ -77,26 +77,14 @@ static int CalculateNSEventMaskFromEventCategory(wxEventCategory cat)
 
 wxGUIEventLoop::wxGUIEventLoop()
 {
-    m_sleepTime = 0.0;
-}
-
-void wxGUIEventLoop::WakeUp()
-{
-    extern void wxMacWakeUp();
-
-    wxMacWakeUp();
-}
-
-CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
-{
-    NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
-    return [nsloop getCFRunLoop];
 }
 
 //-----------------------------------------------------------------------------
 // events dispatch and loop handling
 //-----------------------------------------------------------------------------
 
+#if 0
+
 bool wxGUIEventLoop::Pending() const
 {
 #if 0
@@ -155,48 +143,9 @@ bool wxGUIEventLoop::Dispatch()
     return true;
 }
 
-bool wxGUIEventLoop::YieldFor(long eventsToProcess)
-{
-#if wxUSE_THREADS
-    // Yielding from a non-gui thread needs to bail out, otherwise we end up
-    // possibly sending events in the thread too.
-    if ( !wxThread::IsMain() )
-    {
-        return true;
-    }
-#endif // wxUSE_THREADS
-
-    m_isInsideYield = true;
-    m_eventsToProcessInsideYield = eventsToProcess;
-
-#if wxUSE_LOG
-    // disable log flushing from here because a call to wxYield() shouldn't
-    // normally result in message boxes popping up &c
-    wxLog::Suspend();
-#endif // wxUSE_LOG
-
-    // process all pending events:
-    while ( Pending() )
-        Dispatch();
-
-    // it's necessary to call ProcessIdle() to update the frames sizes which
-    // might have been changed (it also will update other things set from
-    // OnUpdateUI() which is a nice (and desired) side effect)
-    while ( ProcessIdle() ) {}
-
-    // if there are pending events, we must process them.
-    if (wxTheApp)
-        wxTheApp->ProcessPendingEvents();
-
-#if wxUSE_LOG
-    wxLog::Resume();
-#endif // wxUSE_LOG
-    m_isInsideYield = false;
-
-    return true;
-}
+#endif
 
-int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
+int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
 {
     wxMacAutoreleasePool autoreleasepool;
 
@@ -205,10 +154,11 @@ int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
                 untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
                 inMode:NSDefaultRunLoopMode
                 dequeue: YES];
-    if ( !event )
+    
+    if ( event == nil )
         return -1;
 
     [NSApp sendEvent: event];
 
-    return true;
+    return 1;
 }
index 94b0940ece1b66cf267dc9c32eab5049c83a0a78..3bd0c3c73371514cfa48397a6117983e66fa75aa 100644 (file)
@@ -3,7 +3,7 @@
 // Purpose:     wxEventLoop implementation common to both Carbon and Cocoa
 // Author:      Vadim Zeitlin
 // Created:     2009-10-18
-// RCS-ID:      $Id: wxhead.cpp,v 1.10 2009-06-29 10:23:04 zeitlin Exp $
+// RCS-ID:      $Id$
 // Copyright:   (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
 // Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
@@ -29,6 +29,7 @@
 
 #ifndef WX_PRECOMP
     #include "wx/log.h"
+    #include "wx/app.h"
 #endif
 
 #include "wx/evtloopsrc.h"
@@ -140,3 +141,233 @@ wxCFEventLoop::AddSourceForFD(int WXUNUSED(fd),
 #endif // MAC_OS_X_VERSION_MAX_ALLOWED
 
 #endif // wxUSE_EVENTLOOP_SOURCE
+
+wxCFEventLoop::wxCFEventLoop()
+{
+    m_shouldExit = false;
+    m_sleepTime = 0.0;
+}
+
+wxCFEventLoop::~wxCFEventLoop()
+{
+}
+                        
+
+CFRunLoopRef wxCFEventLoop::CFGetCurrentRunLoop() const
+{
+    return CFGetCurrentRunLoop();
+}
+
+void wxCFEventLoop::WakeUp()
+{
+    extern void wxMacWakeUp();
+    
+    wxMacWakeUp();
+}
+
+bool wxCFEventLoop::YieldFor(long eventsToProcess)
+{
+#if wxUSE_THREADS
+    // Yielding from a non-gui thread needs to bail out, otherwise we end up
+    // possibly sending events in the thread too.
+    if ( !wxThread::IsMain() )
+    {
+        return true;
+    }
+#endif // wxUSE_THREADS
+    
+    m_isInsideYield = true;
+    m_eventsToProcessInsideYield = eventsToProcess;
+    
+#if wxUSE_LOG
+    // disable log flushing from here because a call to wxYield() shouldn't
+    // normally result in message boxes popping up &c
+    wxLog::Suspend();
+#endif // wxUSE_LOG
+    
+    // process all pending events:
+    while ( DoProcessEvents() == 1 )
+        ;
+    
+    // it's necessary to call ProcessIdle() to update the frames sizes which
+    // might have been changed (it also will update other things set from
+    // OnUpdateUI() which is a nice (and desired) side effect)
+    while ( ProcessIdle() ) {}
+    
+    // if there are pending events, we must process them.
+    if (wxTheApp)
+        wxTheApp->ProcessPendingEvents();
+    
+#if wxUSE_LOG
+    wxLog::Resume();
+#endif // wxUSE_LOG
+    m_isInsideYield = false;
+    
+    return true;
+}
+
+// implement/override base class pure virtual
+bool wxCFEventLoop::Pending() const
+{
+    return true;
+}
+
+int wxCFEventLoop::DoProcessEvents()
+{
+    // process pending wx events first as they correspond to low-level events
+    // which happened before, i.e. typically pending events were queued by a
+    // previous call to Dispatch() and if we didn't process them now the next
+    // call to it might enqueue them again (as happens with e.g. socket events
+    // which would be generated as long as there is input available on socket
+    // and this input is only removed from it when pending event handlers are
+    // executed)
+    if ( wxTheApp )
+        wxTheApp->ProcessPendingEvents();
+    
+    return DispatchTimeout( (unsigned long)(m_sleepTime * 1000.0) );
+}
+
+bool wxCFEventLoop::Dispatch()
+{
+    return DispatchTimeout( (unsigned long)(m_sleepTime * 1000.0) ) != 0;
+}
+
+int wxCFEventLoop::DispatchTimeout(unsigned long timeout)
+{
+    if ( !wxTheApp )
+        return 0;
+
+    wxMacAutoreleasePool autoreleasepool;
+    
+    int status = DoDispatchTimeout(timeout);
+    
+    switch( status )
+    {
+        case 0:
+            break;
+        case -1:
+            if ( m_shouldExit )
+                return 0;
+            
+            if ( ProcessIdle() )
+                m_sleepTime = 0.0 ;
+            else
+            {
+                m_sleepTime = 1.0;
+#if wxUSE_THREADS
+                wxMutexGuiLeave();
+                wxMilliSleep(20);
+                wxMutexGuiEnter();
+#endif
+            }
+            break;
+        case 1:
+            m_sleepTime = 0;
+            break;
+    }
+    
+    return status;
+}
+
+int wxCFEventLoop::DoDispatchTimeout(unsigned long timeout)
+{    
+    SInt32 status = CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout / 1000.0 , true);
+    switch( status )
+    {
+        case kCFRunLoopRunFinished:
+            wxFAIL_MSG( "incorrect run loop state" );
+            break;
+        case kCFRunLoopRunStopped:
+            return 0;
+            break;
+        case kCFRunLoopRunTimedOut:
+            return -1;
+            break;
+        case kCFRunLoopRunHandledSource:
+        default:
+            break;
+    }
+    return 1;
+}
+
+// enters a loop calling OnNextIteration(), Pending() and Dispatch() and
+// terminating when Exit() is called
+int wxCFEventLoop::Run()
+{
+    // event loops are not recursive, you need to create another loop!
+    wxCHECK_MSG( !IsRunning(), -1, wxT("can't reenter a message loop") );
+    
+    // ProcessIdle() and ProcessEvents() below may throw so the code here should
+    // be exception-safe, hence we must use local objects for all actions we
+    // should undo
+    wxEventLoopActivator activate(this);
+    
+    // we must ensure that OnExit() is called even if an exception is thrown
+    // from inside ProcessEvents() but we must call it from Exit() in normal
+    // situations because it is supposed to be called synchronously,
+    // wxModalEventLoop depends on this (so we can't just use ON_BLOCK_EXIT or
+    // something similar here)
+#if wxUSE_EXCEPTIONS
+    for ( ;; )
+    {
+        try
+        {
+#endif // wxUSE_EXCEPTIONS
+            for ( ;; )
+            {
+                // generate and process idle events for as long as we don't
+                // have anything else to do
+                DoProcessEvents();
+            
+                // if the "should exit" flag is set, the loop should terminate
+                // but not before processing any remaining messages so while
+                // Pending() returns true, do process them
+                if ( m_shouldExit )
+                {
+                    while ( DoProcessEvents() == 1 )
+                        ;
+                    
+                    break;
+                }
+            }
+            
+#if wxUSE_EXCEPTIONS
+            // exit the outer loop as well
+            break;
+        }
+        catch ( ... )
+        {
+            try
+            {
+                if ( !wxTheApp || !wxTheApp->OnExceptionInMainLoop() )
+                {
+                    OnExit();
+                    break;
+                }
+                //else: continue running the event loop
+            }
+            catch ( ... )
+            {
+                // OnException() throwed, possibly rethrowing the same
+                // exception again: very good, but we still need OnExit() to
+                // be called
+                OnExit();
+                throw;
+            }
+        }
+    }
+#endif // wxUSE_EXCEPTIONS
+    
+    return m_exitcode;
+}
+
+// sets the "should exit" flag and wakes up the loop so that it terminates
+// soon
+void wxCFEventLoop::Exit(int rc)
+{
+    m_exitcode = rc;
+    m_shouldExit = true;
+    m_sleepTime = 0;
+}
+
+
index 0b1831b970a29529b15367f56ba32db894954505..67e4784965945b5d4c733fae2c0df6ccff7cc2c9 100644 (file)
@@ -80,118 +80,21 @@ wxGUIEventLoop::wxGUIEventLoop()
     m_sleepTime = 0.0;
 }
 
-void wxGUIEventLoop::WakeUp()
-{
-    extern void wxMacWakeUp();
-
-    wxMacWakeUp();
-}
-
-CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
-{
-    return CFRunLoopGetCurrent();
-}
-
-bool wxGUIEventLoop::Pending() const
+int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
 {
     wxMacAutoreleasePool autoreleasepool;
-    // a pointer to the event is returned if there is one, or nil if not
-    /*
-    return [[UIApplication sharedApplication]
-            nextEventMatchingMask: NSAnyEventMask
-            untilDate: nil
-            inMode: NSDefaultRunLoopMode
-            dequeue: NO];
-    */
-    return false;
-}
-
-bool wxGUIEventLoop::Dispatch()
-{
-    if ( !wxTheApp )
-        return false;
-
-    wxMacAutoreleasePool autoreleasepool;
-
-/*
-    if(UIEvent *event = [[UIApplication sharedApplication]
-                nextEventMatchingMask:NSAnyEventMask
-                untilDate:[NSDate dateWithTimeIntervalSinceNow: m_sleepTime]
-                inMode:NSDefaultRunLoopMode
-                dequeue: YES])
-    {
-        m_sleepTime = 0.0;
-        [[UIApplication sharedApplication] sendEvent: event];
-    }
-    else
-*/
-    {
-        if ( wxTheApp->ProcessIdle() )
-            m_sleepTime = 0.0 ;
-        else
-        {
-            m_sleepTime = 1.0;
-#if wxUSE_THREADS
-            wxMutexGuiLeave();
-            wxMilliSleep(20);
-            wxMutexGuiEnter();
-#endif
-        }
-    }
-
-    return true;
-}
 
-bool wxGUIEventLoop::YieldFor(long eventsToProcess)
-{
-#if wxUSE_THREADS
-    // Yielding from a non-gui thread needs to bail out, otherwise we end up
-    // possibly sending events in the thread too.
-    if ( !wxThread::IsMain() )
-    {
-        return true;
-    }
-#endif // wxUSE_THREADS
-
-    m_isInsideYield = true;
-    m_eventsToProcessInsideYield = eventsToProcess;
-
-#if wxUSE_LOG
-    // disable log flushing from here because a call to wxYield() shouldn't
-    // normally result in message boxes popping up &c
-    wxLog::Suspend();
-#endif // wxUSE_LOG
-
-    // process all pending events:
-    while ( Pending() )
-        Dispatch();
-
-    // it's necessary to call ProcessIdle() to update the frames sizes which
-    // might have been changed (it also will update other things set from
-    // OnUpdateUI() which is a nice (and desired) side effect)
-    while ( ProcessIdle() ) {}
-
-#if wxUSE_LOG
-    wxLog::Resume();
-#endif // wxUSE_LOG
-    m_isInsideYield = false;
-
-    return true;
-}
-
-int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
-{
-    wxMacAutoreleasePool autoreleasepool;
 /*
     UIEvent *event = [[UIApplication sharedApplication]
                 nextEventMatchingMask:NSAnyEventMask
                 untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
                 inMode:NSDefaultRunLoopMode
                 dequeue: YES];
-    if ( !event )
+
+    if ( event == nil )
         return -1;
 
     [NSApp sendEvent: event];
 */
-    return true;
+    return 1;
 }