From 0056673c672ab444863c57344936c9a322124df6 Mon Sep 17 00:00:00 2001 From: Stefan Csomor Date: Mon, 15 Mar 2010 15:40:47 +0000 Subject: [PATCH] streamlining OSX event support first step, see #11805, see #11797 git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@63687 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- include/wx/osx/carbon/evtloop.h | 15 +- include/wx/osx/cocoa/evtloop.h | 16 +-- include/wx/osx/evtloop.h | 49 ++++++- src/osx/carbon/evtloop.cpp | 117 +--------------- src/osx/cocoa/evtloop.mm | 64 +-------- src/osx/core/evtloop_cf.cpp | 233 +++++++++++++++++++++++++++++++- src/osx/iphone/evtloop.mm | 105 +------------- 7 files changed, 298 insertions(+), 301 deletions(-) diff --git a/include/wx/osx/carbon/evtloop.h b/include/wx/osx/carbon/evtloop.h index 5e881875cc..0824ca2636 100644 --- a/include/wx/osx/carbon/evtloop.h +++ b/include/wx/osx/carbon/evtloop.h @@ -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_ diff --git a/include/wx/osx/cocoa/evtloop.h b/include/wx/osx/cocoa/evtloop.h index 8fc7c33258..be4e788bbb 100644 --- a/include/wx/osx/cocoa/evtloop.h +++ b/include/wx/osx/cocoa/evtloop.h @@ -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_ diff --git a/include/wx/osx/evtloop.h b/include/wx/osx/evtloop.h index a295706f58..5ddb819d55 100644 --- a/include/wx/osx/evtloop.h +++ b/include/wx/osx/evtloop.h @@ -15,9 +15,37 @@ 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 diff --git a/src/osx/carbon/evtloop.cpp b/src/osx/carbon/evtloop.cpp index 928e7f90a7..abaafd3181 100644 --- a/src/osx/carbon/evtloop.cpp +++ b/src/osx/carbon/evtloop.cpp @@ -39,22 +39,9 @@ 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; -} diff --git a/src/osx/cocoa/evtloop.mm b/src/osx/cocoa/evtloop.mm index d22e47a0e0..69c550b6d0 100644 --- a/src/osx/cocoa/evtloop.mm +++ b/src/osx/cocoa/evtloop.mm @@ -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; } diff --git a/src/osx/core/evtloop_cf.cpp b/src/osx/core/evtloop_cf.cpp index 94b0940ece..3bd0c3c733 100644 --- a/src/osx/core/evtloop_cf.cpp +++ b/src/osx/core/evtloop_cf.cpp @@ -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 // 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; +} + + diff --git a/src/osx/iphone/evtloop.mm b/src/osx/iphone/evtloop.mm index 0b1831b970..67e4784965 100644 --- a/src/osx/iphone/evtloop.mm +++ b/src/osx/iphone/evtloop.mm @@ -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; } -- 2.45.2