From dde19c2180ef8d6415af7bb2492bfcb0a2d5c7e4 Mon Sep 17 00:00:00 2001 From: Francesco Montorsi Date: Sun, 15 Feb 2009 14:25:08 +0000 Subject: [PATCH] second part of #10320: move wxApp event handling functions to wxEventLoopBase (in particular move Yield() functions); add backward compatible redirections to wxApp; update docs; remove global lists wxPendingEvents and wxPendingEventsLocker git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@58911 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 5 + include/wx/app.h | 107 +++---------------- include/wx/cocoa/app.h | 1 - include/wx/cocoa/evtloop.h | 1 + include/wx/dfb/app.h | 2 - include/wx/dfb/evtloop.h | 6 +- include/wx/event.h | 30 ++---- include/wx/evtloop.h | 148 ++++++++++++++++++++++++-- include/wx/gtk/app.h | 4 - include/wx/gtk/evtloop.h | 5 + include/wx/gtk1/app.h | 2 - include/wx/mgl/app.h | 2 - include/wx/motif/app.h | 1 - include/wx/msw/app.h | 2 - include/wx/msw/evtloop.h | 5 + include/wx/os2/app.h | 2 - include/wx/osx/app.h | 1 - include/wx/osx/carbon/evtloop.h | 1 + include/wx/osx/cocoa/evtloop.h | 1 + include/wx/palmos/app.h | 2 - include/wx/palmos/evtloop.h | 1 + include/wx/unix/evtloop.h | 1 + include/wx/x11/app.h | 1 - interface/wx/app.h | 98 ++---------------- interface/wx/event.h | 12 +-- interface/wx/evtloop.h | 178 ++++++++++++++++++++++++++++---- src/common/appbase.cpp | 110 +++++--------------- src/common/appcmn.cpp | 17 ++- src/common/event.cpp | 113 +++++++------------- src/common/evtloopcmn.cpp | 148 ++++++++++++++++++++++++++ src/dfb/app.cpp | 43 -------- src/dfb/evtloop.cpp | 27 ++++- src/generic/progdlgg.cpp | 7 +- src/gtk/app.cpp | 171 ++---------------------------- src/gtk/clipbrd.cpp | 3 +- src/gtk/evtloop.cpp | 144 ++++++++++++++++++++++++++ src/gtk1/app.cpp | 58 ----------- src/gtk1/evtloop.cpp | 49 +++++++++ src/mgl/app.cpp | 50 --------- src/mgl/evtloop.cpp | 36 +++++++ src/motif/app.cpp | 26 ----- src/motif/evtloop.cpp | 14 +++ src/msw/app.cpp | 171 ------------------------------ src/msw/evtloop.cpp | 159 ++++++++++++++++++++++++++++ src/os2/app.cpp | 60 ----------- src/os2/evtloop.cpp | 49 +++++++++ src/palmos/app.cpp | 7 -- src/palmos/evtloop.cpp | 5 + src/x11/app.cpp | 46 --------- src/x11/evtloop.cpp | 30 ++++++ 50 files changed, 1098 insertions(+), 1064 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 7cfb0b0c33..c8b5cc7925 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -261,6 +261,8 @@ Deprecated methods and their replacements - wxDos2UnixFilename, wxUnix2DosFilename, wxStripExtension, wxGetTempFileName, wxExpandPath, wxContractPath, wxRealPath, wxCopyAbsolutePath, wxSplitPath were deprecated in favour of wxFileName methods. See docs for more info. +- global wxPendingEvents and wxPendingEventsLocker objects were removed; now you may use + wxEventLoopBase::SuspendProcessingOfPendingEvents instead of locking wxPendingEventsLocker. Major new features in this release ---------------------------------- @@ -472,6 +474,9 @@ All (GUI): - Added wxIMAGE_OPTION_PNG_FILTER and many wxIMAGE_OPTION_PNG_COMPRESSION_* options to wxImage and wxPNGHandler to allow for custom compression levels when saving PNGs - Added GetValue(), GetRange(), GetMessage() functions to wxProgressDialog +- Moved yield functions to wxEventLoopBase and implemented for wxMSW and wxGTK + a selective wxEventLoopBase::YieldFor() function. + Added also wxEventLoopBase::IsYielding to help cure re-entrancy problems with Yield(). wxGTK: diff --git a/include/wx/app.h b/include/wx/app.h index a5b60fed2b..8a6a135df9 100644 --- a/include/wx/app.h +++ b/include/wx/app.h @@ -221,6 +221,10 @@ public: // had been already processed or won't be processed at all, respectively virtual int FilterEvent(wxEvent& event); + // return true if we're running event loop, i.e. if the events can + // (already) be dispatched + static bool IsMainLoopRunning(); + #if wxUSE_EXCEPTIONS // execute the functor to handle the given event // @@ -246,88 +250,35 @@ public: // exit, if you need to really handle the exceptions you need to override // OnExceptionInMainLoop() virtual void OnUnhandledException(); + + // Function called if an uncaught exception is caught inside the main + // event loop: it may return true to continue running the event loop or + // false to stop it (in the latter case it may rethrow the exception as + // well) + virtual bool OnExceptionInMainLoop(); + #endif // wxUSE_EXCEPTIONS - // event processing functions - // -------------------------- - // return true if we're running event loop, i.e. if the events can - // (already) be dispatched - static bool IsMainLoopRunning(); + // wxEventLoop redirections + // ------------------------ - // temporary suspends processing of the pending events virtual void SuspendProcessingOfPendingEvents(); - - // resume processing of the pending events previously stopped because of a - // call to SuspendProcessingOfPendingEvents() virtual void ResumeProcessingOfPendingEvents(); - - // process all events in the wxHandlersWithPendingEvents list -- it is necessary - // to call this function to process posted events. This happens during each - // event loop iteration in GUI mode but if there is no main loop, it may be - // also called directly. virtual void ProcessPendingEvents(); - - // check if there are pending events on global pending event list bool HasPendingEvents() const; - // make sure that idle events are sent again - virtual void WakeUpIdle(); + virtual bool Pending(); + virtual bool Dispatch(); - // execute the main GUI loop, the function returns when the loop ends virtual int MainLoop(); - - // exit the main GUI loop during the next iteration (i.e. it does not - // stop the program immediately!) virtual void ExitMainLoop(); - // returns true if there are unprocessed events in the event queue - virtual bool Pending(); + bool Yield(bool onlyIfNeeded = false); - // process the first event in the event queue (blocks until an event - // appears if there are none currently, use Pending() if this is not - // wanted), returns false if the event loop should stop and true - // otherwise - virtual bool Dispatch(); - - // this virtual function is called when the application - // becomes idle and normally just sends wxIdleEvent to all interested - // parties - // - // it should return true if more idle events are needed, false if not + virtual void WakeUpIdle(); virtual bool ProcessIdle(); -#if wxUSE_EXCEPTIONS - // Function called if an uncaught exception is caught inside the main - // event loop: it may return true to continue running the event loop or - // false to stop it (in the latter case it may rethrow the exception as - // well) - virtual bool OnExceptionInMainLoop(); -#endif // wxUSE_EXCEPTIONS - - // Yield-related hooks - // ------------------- - - // process all currently pending events right now - // - // it is an error to call Yield() recursively unless the value of - // onlyIfNeeded is true - // - // WARNING: this function is dangerous as it can lead to unexpected - // reentrancies (i.e. when called from an event handler it - // may result in calling the same event handler again), use - // with _extreme_ care or, better, don't use at all! - // NOTE: in wxConsoleBase it doesn't do anything, just a hook for GUI wxApp - bool Yield(bool onlyIfNeeded = false) - { return DoYield(onlyIfNeeded, wxEVT_CATEGORY_ALL); } - bool YieldFor(long eventsToProcess) - { return DoYield(true, eventsToProcess); } - virtual bool IsYielding() const - { return false; } - virtual bool IsEventAllowedInsideYield(wxEventCategory WXUNUSED(cat)) const - { return true; } - // no SafeYield hooks since it uses wxWindow which is not available when wxUSE_GUI=0 - // debugging support // ----------------- @@ -395,10 +346,6 @@ protected: // for the first time virtual wxAppTraits *CreateTraits(); - // the real yield function hook: - virtual bool DoYield(bool WXUNUSED(onlyIfNeeded), long WXUNUSED(eventsToProcess)) - { return true; } - // function used for dynamic wxApp creation static wxAppInitializerFunction ms_appInitFn; @@ -424,13 +371,6 @@ protected: // been started yet or has already terminated) wxEventLoopBase *m_mainLoop; - // the array of the handlers with pending events which needs to be processed - // inside ProcessPendingEvents() - // wxEvtHandlerArray m_handlersWithPendingEvents; FIXME: enable this and remove global lists - - // helper array used by ProcessPendingEvents() - // wxEvtHandlerArray m_handlersWithPendingDelayedEvents; FIXME: enable this and remove global lists - friend class WXDLLIMPEXP_FWD_BASE wxEvtHandler; // the application object is a singleton anyhow, there is no sense in @@ -496,15 +436,6 @@ public: virtual bool SafeYield(wxWindow *win, bool onlyIfNeeded); virtual bool SafeYieldFor(wxWindow *win, long eventsToProcess); - // returns true if the main thread is inside a Yield() call - virtual bool IsYielding() const - { return m_isInsideYield; } - - // returns true if events of the given event category should be immediately - // processed inside a wxApp::Yield() call or rather should be queued for - // later processing by the main event loop - virtual bool IsEventAllowedInsideYield(wxEventCategory cat) const; - // this virtual function is called in the GUI mode when the application // becomes idle and normally just sends wxIdleEvent to all interested // parties @@ -627,10 +558,6 @@ protected: // does any of our windows have focus? bool m_isActive; - // Yield() helpers: - bool m_isInsideYield; - long m_eventsToProcessInsideYield; - wxDECLARE_NO_COPY_CLASS(wxAppBase); }; diff --git a/include/wx/cocoa/app.h b/include/wx/cocoa/app.h index 7285f71e65..a0a65f9427 100644 --- a/include/wx/cocoa/app.h +++ b/include/wx/cocoa/app.h @@ -57,7 +57,6 @@ public: // Implement wxAppBase pure virtuals virtual void Exit(); - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); virtual void WakeUpIdle(); virtual bool Initialize(int& argc, wxChar **argv); diff --git a/include/wx/cocoa/evtloop.h b/include/wx/cocoa/evtloop.h index b61f011185..7e0d476809 100644 --- a/include/wx/cocoa/evtloop.h +++ b/include/wx/cocoa/evtloop.h @@ -26,6 +26,7 @@ public: virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp() { } + virtual bool YieldFor(long eventsToProcess); protected: int m_exitcode; diff --git a/include/wx/dfb/app.h b/include/wx/dfb/app.h index b76af79ce3..5407b18c5a 100644 --- a/include/wx/dfb/app.h +++ b/include/wx/dfb/app.h @@ -39,8 +39,6 @@ public: private: wxVideoMode m_videoMode; - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); - DECLARE_DYNAMIC_CLASS(wxApp) }; diff --git a/include/wx/dfb/evtloop.h b/include/wx/dfb/evtloop.h index d6fa511ddc..2a58619ed1 100644 --- a/include/wx/dfb/evtloop.h +++ b/include/wx/dfb/evtloop.h @@ -25,14 +25,12 @@ class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxConsoleEventLoop public: wxGUIEventLoop(); + virtual bool YieldFor(long eventsToProcess); + // returns DirectFB event buffer used by wx static wxIDirectFBEventBufferPtr GetDirectFBEventBuffer(); private: - // wxYield implementation: iterate the loop as long as there are any - // pending events - void Yield(); - static void InitBuffer(); static void CleanUp(); diff --git a/include/wx/event.h b/include/wx/event.h index 073fef666a..215acd74a4 100644 --- a/include/wx/event.h +++ b/include/wx/event.h @@ -772,7 +772,7 @@ enum wxEventPropagation }; // The different categories for a wxEvent; see wxEvent::GetEventCategory. -// NOTE: they are used as OR-combinable flags by wxApp::Yield +// NOTE: they are used as OR-combinable flags by wxEventLoopBase::YieldFor enum wxEventCategory { // this is the category for those events which are generated to update @@ -802,10 +802,10 @@ enum wxEventCategory // implementation only - // used in the implementations of DoYield() + // used in the implementations of wxEventLoopBase::YieldFor wxEVT_CATEGORY_UNKNOWN = 32, - // a special category used as an argument to wxApp::Yield() to indicate that + // a special category used as an argument to wxEventLoopBase::YieldFor to indicate that // Yield() should leave all wxEvents on the queue while emptying the native event queue // (native events will be processed but the wxEvents they generate will be queued) wxEVT_CATEGORY_CLIPBOARD = 64, @@ -817,7 +817,7 @@ enum wxEventCategory // events of the native toolkit and which typically are not-"delayable". wxEVT_CATEGORY_NATIVE_EVENTS = wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT, - // used in wxApp::Yield to specify all event categories should be processed: + // used in wxEventLoopBase::YieldFor to specify all event categories should be processed: wxEVT_CATEGORY_ALL = wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT|wxEVT_CATEGORY_SOCKET| \ wxEVT_CATEGORY_TIMER|wxEVT_CATEGORY_THREAD @@ -864,7 +864,7 @@ public: // for them wouldn't work (it needs to do a copy of the event) virtual wxEvent *Clone() const = 0; - // this function is used to selectively process events in wxApp::YieldFor + // this function is used to selectively process events in wxEventLoopBase::YieldFor // NOTE: by default it returns wxEVT_CATEGORY_UI just because the major // part of wxWidgets events belong to that category. virtual wxEventCategory GetEventCategory() const @@ -1120,7 +1120,7 @@ public: return ev; } - // this is important to avoid that calling wxApp::Yield() thread events + // this is important to avoid that calling wxEventLoopBase::YieldFor thread events // gets processed when this is unwanted: virtual wxEventCategory GetEventCategory() const { return wxEVT_CATEGORY_THREAD; } @@ -3985,24 +3985,6 @@ typedef void (wxEvtHandler::*wxClipboardTextEventFunction)(wxClipboardTextEvent& // Thread events #define EVT_THREAD(id, func) wx__DECLARE_EVT1(wxEVT_COMMAND_THREAD, id, wxThreadEventHandler(func)) -// ---------------------------------------------------------------------------- -// Global data -// ---------------------------------------------------------------------------- - -// list containing event handlers with pending events for them -// -// notice that each event handler should occur at most once in this list -extern WXDLLIMPEXP_BASE wxList *wxHandlersWithPendingEvents; -extern WXDLLIMPEXP_BASE wxList *wxHandlersWithPendingDelayedEvents; -#if wxUSE_THREADS - // this critical section protectes both the lists above - extern WXDLLIMPEXP_BASE wxCriticalSection *wxHandlersWithPendingEventsLocker; -#endif - -// old list names: -#define wxPendingEvents wxHandlersWithPendingEvents -#define wxPendingEventsLocker wxHandlersWithPendingEventsLocker - // ---------------------------------------------------------------------------- // Helper functions // ---------------------------------------------------------------------------- diff --git a/include/wx/evtloop.h b/include/wx/evtloop.h index 7d85458ff8..9ff5edbe64 100644 --- a/include/wx/evtloop.h +++ b/include/wx/evtloop.h @@ -14,6 +14,36 @@ #include "wx/utils.h" + +/* + NOTE ABOUT wxEventLoopBase::YieldFor LOGIC + ------------------------------------------ + + The YieldFor() function helps to avoid re-entrancy problems and problems + caused by out-of-order event processing + (see "wxYield-like problems" and "wxProgressDialog+threading BUG" wx-dev threads). + + The logic behind YieldFor() is simple: it analyzes the queue of the native + events generated by the underlying GUI toolkit and picks out and processes + only those matching the given mask. + + It's important to note that YieldFor() is used to selectively process the + events generated by the NATIVE toolkit. + Events syntethized by wxWidgets code or by user code are instead selectively + processed thanks to the logic built into wxEvtHandler::ProcessPendingEvents(). + In fact, when wxEvtHandler::ProcessPendingEvents gets called from inside a + YieldFor() call, wxEventLoopBase::IsEventAllowedInsideYield is used to decide + if the pending events for that event handler can be processed. + If all the pending events associated with that event handler result as "not processable", + the event handler "delays" itself calling wxEventLoopBase::DelayPendingEventHandler + (so it's moved: m_handlersWithPendingEvents => m_handlersWithPendingDelayedEvents). + Last, wxEventLoopBase::ProcessPendingEvents() before exiting moves the delayed + event handlers back into the list of handlers with pending events + (m_handlersWithPendingDelayedEvents => m_handlersWithPendingEvents) so that + a later call to ProcessPendingEvents() (possibly outside the YieldFor() call) + will process all pending events as usual. +*/ + // ---------------------------------------------------------------------------- // wxEventLoopBase: interface for wxEventLoop // ---------------------------------------------------------------------------- @@ -22,7 +52,7 @@ class WXDLLIMPEXP_BASE wxEventLoopBase { public: // trivial, but needed (because of wxEventLoopBase) ctor - wxEventLoopBase() { } + wxEventLoopBase(); // dtor virtual ~wxEventLoopBase() { } @@ -32,9 +62,18 @@ public: virtual bool IsOk() const { return true; } + // dispatch&processing + // ------------------- + // start the event loop, return the exit code when it is finished virtual int Run() = 0; + // is this event loop running now? + // + // notice that even if this event loop hasn't terminated yet but has just + // spawned a nested (e.g. modal) event loop, this would return false + bool IsRunning() const; + // exit from the loop with the given exit code virtual void Exit(int rc = 0) = 0; @@ -49,6 +88,86 @@ public: // exit the loop or -1 if timeout expired virtual int DispatchTimeout(unsigned long timeout) = 0; + // 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() = 0; + + + // pending events + // -------------- + + // process all events in the wxHandlersWithPendingEvents list -- it is necessary + // to call this function to process posted events. This happens during each + // event loop iteration in GUI mode but if there is no main loop, it may be + // also called directly. + virtual void ProcessPendingEvents(); + + // check if there are pending events on global pending event list + bool HasPendingEvents() const; + + // temporary suspends processing of the pending events + void SuspendProcessingOfPendingEvents(); + + // resume processing of the pending events previously stopped because of a + // call to SuspendProcessingOfPendingEvents() + void ResumeProcessingOfPendingEvents(); + + // called by ~wxEvtHandler to (eventually) remove the handler from the list of + // the handlers with pending events + void RemovePendingEventHandler(wxEvtHandler* toRemove); + + // adds an event handler to the list of the handlers with pending events + void AppendPendingEventHandler(wxEvtHandler* toAppend); + + // moves the event handler from the list of the handlers with pending events + //to the list of the handlers with _delayed_ pending events + void DelayPendingEventHandler(wxEvtHandler* toDelay); + + + // idle handling + // ------------- + + // make sure that idle events are sent again + virtual void WakeUpIdle(); + + // this virtual function is called when the application + // becomes idle and normally just sends wxIdleEvent to all interested + // parties + // + // it should return true if more idle events are needed, false if not + virtual bool ProcessIdle(); + + + // Yield-related hooks + // ------------------- + + // process all currently pending events right now + // + // it is an error to call Yield() recursively unless the value of + // onlyIfNeeded is true + // + // WARNING: this function is dangerous as it can lead to unexpected + // reentrancies (i.e. when called from an event handler it + // may result in calling the same event handler again), use + // with _extreme_ care or, better, don't use at all! + bool Yield(bool onlyIfNeeded = false); + virtual bool YieldFor(long eventsToProcess) = 0; + + // returns true if the main thread is inside a Yield() call + virtual bool IsYielding() const + { return m_isInsideYield; } + + // returns true if events of the given event category should be immediately + // processed inside a wxApp::Yield() call or rather should be queued for + // later processing by the main event loop + virtual bool IsEventAllowedInsideYield(wxEventCategory cat) const + { return (m_eventsToProcessInsideYield & cat) != 0; } + + // no SafeYield hooks since it uses wxWindow which is not available when wxUSE_GUI=0 + + + // active loop + // ----------- // return currently active (running) event loop, may be NULL static wxEventLoopBase *GetActive() { return ms_activeLoop; } @@ -56,15 +175,6 @@ public: // set currently active (running) event loop static void SetActive(wxEventLoopBase* loop) { ms_activeLoop = loop; } - // is this event loop running now? - // - // notice that even if this event loop hasn't terminated yet but has just - // spawned a nested (e.g. modal) event loop, this would return false - bool IsRunning() const; - - // 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() = 0; protected: // this function should be called before the event loop terminates, whether @@ -72,10 +182,25 @@ protected: // an exception thrown from inside the loop) virtual void OnExit() { } - // the pointer to currently active loop static wxEventLoopBase *ms_activeLoop; + // the array of the handlers with pending events which needs to be processed + // inside ProcessPendingEvents() + wxEvtHandlerArray m_handlersWithPendingEvents; + + // helper array used by ProcessPendingEvents() + wxEvtHandlerArray m_handlersWithPendingDelayedEvents; + +#if wxUSE_THREADS + // this critical section protects both the lists above + wxCriticalSection m_handlersWithPendingEventsLocker; +#endif + + // Yield() helpers: + bool m_isInsideYield; + long m_eventsToProcessInsideYield; + wxDECLARE_NO_COPY_CLASS(wxEventLoopBase); }; @@ -161,6 +286,7 @@ public: } } virtual void WakeUp() { } + virtual bool YieldFor(long eventsToProcess); protected: // the pointer to the port specific implementation class diff --git a/include/wx/gtk/app.h b/include/wx/gtk/app.h index 87a2ff8c43..630bf9b04d 100644 --- a/include/wx/gtk/app.h +++ b/include/wx/gtk/app.h @@ -78,10 +78,6 @@ public: bool EventsPending(); bool DoIdle(); -protected: - - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); - private: // true if we're inside an assert modal dialog #ifdef __WXDEBUG__ diff --git a/include/wx/gtk/evtloop.h b/include/wx/gtk/evtloop.h index d19cd557c1..36c62e168e 100644 --- a/include/wx/gtk/evtloop.h +++ b/include/wx/gtk/evtloop.h @@ -26,11 +26,16 @@ public: virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp(); + virtual bool YieldFor(long eventsToProcess); protected: + // the exit code of this event loop int m_exitcode; + // used to temporarily store events in DoYield() + wxArrayPtrVoid m_arrGdkEvents; + wxDECLARE_NO_COPY_CLASS(wxGUIEventLoop); }; diff --git a/include/wx/gtk1/app.h b/include/wx/gtk1/app.h index 32ef7416a0..273d29eda4 100644 --- a/include/wx/gtk1/app.h +++ b/include/wx/gtk1/app.h @@ -70,8 +70,6 @@ private: bool m_isInAssert; #endif // __WXDEBUG__ - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); - DECLARE_DYNAMIC_CLASS(wxApp) }; diff --git a/include/wx/mgl/app.h b/include/wx/mgl/app.h index d771014e8d..40b6e0bf68 100644 --- a/include/wx/mgl/app.h +++ b/include/wx/mgl/app.h @@ -51,8 +51,6 @@ public: private: DECLARE_DYNAMIC_CLASS(wxApp) - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); - wxVideoMode m_displayMode; }; diff --git a/include/wx/motif/app.h b/include/wx/motif/app.h index 8879381460..f021442498 100644 --- a/include/wx/motif/app.h +++ b/include/wx/motif/app.h @@ -65,7 +65,6 @@ public: // Implementation virtual bool Initialize(int& argc, wxChar **argv); virtual void CleanUp(); - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); // Motif-specific WXAppContext GetAppContext() const { return m_appContext; } diff --git a/include/wx/msw/app.h b/include/wx/msw/app.h index ade87d8c30..a83d52ead8 100644 --- a/include/wx/msw/app.h +++ b/include/wx/msw/app.h @@ -78,8 +78,6 @@ public: protected: int m_printMode; // wxPRINT_WINDOWS, wxPRINT_POSTSCRIPT - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); - public: // unregister any window classes registered by GetRegisteredClassName() static void UnregisterWindowClasses(); diff --git a/include/wx/msw/evtloop.h b/include/wx/msw/evtloop.h index 2869b07a92..934e3cfef5 100644 --- a/include/wx/msw/evtloop.h +++ b/include/wx/msw/evtloop.h @@ -40,6 +40,8 @@ protected: #if wxUSE_GUI +WX_DECLARE_OBJARRAY(MSG, wxMSGArray); + class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxMSWEventLoopBase { public: @@ -72,6 +74,7 @@ public: virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp(); + virtual bool YieldFor(long eventsToProcess); protected: virtual void OnNextIteration(); @@ -81,6 +84,8 @@ private: // non NULL) static bool IsChildOfCriticalWindow(wxWindowMSW *win); + // array of messages used for temporary storage by YieldFor() + wxMSGArray m_arrMSG; // critical window or NULL static wxWindowMSW *ms_winCritical; diff --git a/include/wx/os2/app.h b/include/wx/os2/app.h index 93c1dd37d6..f4d6fe2fa0 100644 --- a/include/wx/os2/app.h +++ b/include/wx/os2/app.h @@ -110,8 +110,6 @@ public: // Implementation static bool RegisterWindowClasses(HAB vHab); - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); - public: int m_nCmdShow; HMQ m_hMq; diff --git a/include/wx/osx/app.h b/include/wx/osx/app.h index ad80d008a5..02710e57b1 100644 --- a/include/wx/osx/app.h +++ b/include/wx/osx/app.h @@ -66,7 +66,6 @@ public: // Implementation virtual bool Initialize(int& argc, wxChar **argv); virtual void CleanUp(); - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); // the installed application event handler WXEVENTHANDLERREF MacGetEventHandler() { return m_macEventHandler ; } diff --git a/include/wx/osx/carbon/evtloop.h b/include/wx/osx/carbon/evtloop.h index b55112994f..d704a9c50c 100644 --- a/include/wx/osx/carbon/evtloop.h +++ b/include/wx/osx/carbon/evtloop.h @@ -26,6 +26,7 @@ public: virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp(); + virtual bool YieldFor(long eventsToProcess); private: // dispatch an event and release it diff --git a/include/wx/osx/cocoa/evtloop.h b/include/wx/osx/cocoa/evtloop.h index ed222c2bcb..d0040bd920 100644 --- a/include/wx/osx/cocoa/evtloop.h +++ b/include/wx/osx/cocoa/evtloop.h @@ -22,6 +22,7 @@ public: virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp(); + virtual bool YieldFor(long eventsToProcess); private: double m_sleepTime; diff --git a/include/wx/palmos/app.h b/include/wx/palmos/app.h index 292830482d..0590f5e589 100644 --- a/include/wx/palmos/app.h +++ b/include/wx/palmos/app.h @@ -51,8 +51,6 @@ public: protected: int m_printMode; // wxPRINT_WINDOWS, wxPRINT_POSTSCRIPT - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); - public: // Implementation static bool RegisterWindowClasses(); diff --git a/include/wx/palmos/evtloop.h b/include/wx/palmos/evtloop.h index 36341946e8..07dc850733 100644 --- a/include/wx/palmos/evtloop.h +++ b/include/wx/palmos/evtloop.h @@ -28,6 +28,7 @@ public: virtual bool Dispatch(); virtual int DispatchTimeout(unsigned long timeout); virtual bool IsRunning() const; + virtual bool YieldFor(long eventsToProcess); // MSW-specific methods // -------------------- diff --git a/include/wx/unix/evtloop.h b/include/wx/unix/evtloop.h index 42d16625a9..19bfe8bb51 100644 --- a/include/wx/unix/evtloop.h +++ b/include/wx/unix/evtloop.h @@ -37,6 +37,7 @@ public: virtual int DispatchTimeout(unsigned long timeout); virtual void WakeUp(); virtual bool IsOk() const { return m_dispatcher != NULL; } + virtual bool YieldFor(long WXUNUSED(eventsToProcess)) { return true; } protected: virtual void OnNextIteration(); diff --git a/include/wx/x11/app.h b/include/wx/x11/app.h index 588d62242d..d5de438792 100644 --- a/include/wx/x11/app.h +++ b/include/wx/x11/app.h @@ -63,7 +63,6 @@ public: // Implementation virtual bool Initialize(int& argc, wxChar **argv); virtual void CleanUp(); - virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess); WXWindow GetTopLevelWidget() const { return m_topLevelWidget; } WXColormap GetMainColormap(WXDisplay* display); diff --git a/interface/wx/app.h b/interface/wx/app.h index ad5ef7b621..a67a414d69 100644 --- a/interface/wx/app.h +++ b/interface/wx/app.h @@ -57,31 +57,26 @@ public: /** @name Event-handling + + Note that you should look at wxEvtLoopBase for more event-processing + documentation. */ //@{ /** - Dispatches the next event in the windowing system event queue. - Blocks until an event appears if there are none currently - (use Pending() if this is not wanted). - - This can be used for programming event loops, e.g. - - @code - while (app.Pending()) - Dispatch(); - @endcode - - @return @false if the event loop should stop and @true otherwise. + Called by wxWidgets on creation of the application. Override this if you wish + to provide your own (environment-dependent) main loop. - @see Pending(), wxEventLoopBase + @return 0 under X, and the wParam of the WM_QUIT message under Windows. */ - virtual bool Dispatch(); + virtual int MainLoop(); /** Call this to explicitly exit the main message (event) loop. You should normally exit the main loop (and the application) by deleting the top window. + + This function simply calls wxEvtLoopBase::Exit() on the active loop. */ virtual void ExitMainLoop(); @@ -108,81 +103,6 @@ public: wxEventFunction func, wxEvent& event) const; - /** - Returns @true if called from inside Yield(). - */ - virtual bool IsYielding() const; - - /** - Process all pending events; it is necessary to call this function to - process posted events. - - This happens during each event loop iteration in GUI mode but if there is - no main loop, it may be also called directly. - */ - virtual void ProcessPendingEvents(); - - /** - Called by wxWidgets on creation of the application. Override this if you wish - to provide your own (environment-dependent) main loop. - - @return 0 under X, and the wParam of the WM_QUIT message under Windows. - */ - virtual int MainLoop(); - - /** - Returns @true if unprocessed events are in the window system event queue. - - @see Dispatch() - */ - virtual bool Pending(); - - /** - Yields control to pending messages in the windowing system. - - This can be useful, for example, when a time-consuming process writes to a - text window. Without an occasional yield, the text window will not be updated - properly, and on systems with cooperative multitasking, such as Windows 3.1 - other processes will not respond. - - Caution should be exercised, however, since yielding may allow the - user to perform actions which are not compatible with the current task. - Disabling menu items or whole menus during processing can avoid unwanted - reentrance of code: see ::wxSafeYield for a better function. - You can avoid unwanted reentrancies also using IsYielding(). - - Note that Yield() will not flush the message logs. This is intentional as - calling Yield() is usually done to quickly update the screen and popping up - a message box dialog may be undesirable. If you do wish to flush the log - messages immediately (otherwise it will be done during the next idle loop - iteration), call wxLog::FlushActive. - - Calling Yield() recursively is normally an error and an assert failure is - raised in debug build if such situation is detected. However if the - @a onlyIfNeeded parameter is @true, the method will just silently - return @false instead. - */ - bool Yield(bool onlyIfNeeded = false); - - /** - Works like Yield() with @e onlyIfNeeded == @true, except that it allows - the caller to specify a mask of the ::wxEventCategory values which - indicates which events should be processed and which should instead - be "delayed" (i.e. processed by the main loop later). - - Note that this is a safer alternative to Yield() since it ensures that - only the events you're interested to are processed; i.e. helps to avoid - unwanted reentrancies. - */ - bool YieldFor(long eventsToProcess); - - /** - Returns @true if the given event category is allowed inside - a YieldFor() call (i.e. compares the given category against the - last mask passed to YieldFor()). - */ - virtual bool IsEventAllowedInsideYield(wxEventCategory cat) const; - //@} diff --git a/interface/wx/event.h b/interface/wx/event.h index 030d3415f6..bb19a3c7c7 100644 --- a/interface/wx/event.h +++ b/interface/wx/event.h @@ -23,7 +23,7 @@ enum wxEventPropagation /** The different categories for a wxEvent; see wxEvent::GetEventCategory. - @note They are used as OR-combinable flags by wxApp::Yield. + @note They are used as OR-combinable flags by wxEventLoopBase::YieldFor. */ enum wxEventCategory { @@ -58,8 +58,8 @@ enum wxEventCategory wxEVT_CATEGORY_THREAD = 16, /** - This mask is used in wxApp::Yield to specify that all event categories should - be processed. + This mask is used in wxEventLoopBase::YieldFor to specify that all event + categories should be processed. */ wxEVT_CATEGORY_ALL = wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT|wxEVT_CATEGORY_SOCKET| \ @@ -148,7 +148,7 @@ public: /** Returns a generic category for this event. - This function is used to selectively process events in wxApp::Yield. + This function is used to selectively process events in wxEventLoopBase::YieldFor. */ virtual wxEventCategory GetEventCategory() const; @@ -2537,7 +2537,7 @@ public: @library{wxcore} @category{events} - @see @ref overview_thread, wxApp::YieldFor + @see @ref overview_thread, wxEventLoopBase::YieldFor */ class wxThreadEvent : public wxCommandEvent { @@ -2558,7 +2558,7 @@ public: Returns @c wxEVT_CATEGORY_THREAD. This is important to avoid unwanted processing of thread events - when calling wxApp::YieldFor(). + when calling wxEventLoopBase::YieldFor(). */ virtual wxEventCategory GetEventCategory() const; }; diff --git a/interface/wx/evtloop.h b/interface/wx/evtloop.h index be246731b5..659a6b964e 100644 --- a/interface/wx/evtloop.h +++ b/interface/wx/evtloop.h @@ -18,7 +18,7 @@ @library{wxbase} @category{appmanagement} - @see wxApp + @see wxApp, wxEventLoopActivator */ class wxEventLoopBase { @@ -42,10 +42,9 @@ public: /** - Use this to check whether the event loop was successfully created - before using it - */ - virtual bool IsOk() const; + @name Dispatch and processing + */ + //@{ /** Start the event loop, return the exit code when it is finished. @@ -60,6 +59,21 @@ public: */ virtual int Run() = 0; + /** + Return true if this event loop is currently running. + + Notice that even if this event loop hasn't terminated yet but has just + spawned a nested (e.g. modal) event loop, this method would return + @false. + */ + bool IsRunning() const; + + /** + Use this to check whether the event loop was successfully created + before using it + */ + virtual bool IsOk() const; + /** Exit from the loop with the given exit code. */ @@ -73,13 +87,21 @@ public: virtual bool Pending() const = 0; /** - Dispatch a single event. + Dispatches the next event in the windowing system event queue. + Blocks until an event appears if there are none currently + (use Pending() if this is not wanted). - If there are currently no events in the queue, blocks until an event - becomes available. + This can be used for programming event loops, e.g. - @return @false only if the event loop should terminate. - */ + @code + while (evtloop->Pending()) + evtloop->Dispatch(); + @endcode + + @return @false if the event loop should stop and @true otherwise. + + @see Pending(), wxEventLoopBase + */ virtual bool Dispatch() = 0; /** @@ -100,21 +122,139 @@ public: */ virtual int DispatchTimeout(unsigned long timeout) = 0; - /** - Return true if this event loop is currently running. - - Notice that even if this event loop hasn't terminated yet but has just - spawned a nested (e.g. modal) event loop, this method would return - @false. - */ - bool IsRunning() const; - /** Called by wxWidgets to wake up the event loop even if it is currently blocked inside Dispatch(). */ virtual void WakeUp() = 0; + //@} + + + /** + @name Pending events + */ + //@{ + + /** + Process all pending events; it is necessary to call this function to + process posted events. + + This happens during each event loop iteration in GUI mode but + it may be also called directly. + */ + virtual void ProcessPendingEvents(); + + /** + Returns @true if there are pending events on the internal pending event list. + */ + bool HasPendingEvents() const; + + /** + Temporary suspends processing of the pending events. + + @see ResumeProcessingOfPendingEvents() + */ + void SuspendProcessingOfPendingEvents(); + + /** + Resume processing of the pending events previously stopped because of a + call to SuspendProcessingOfPendingEvents(). + */ + void ResumeProcessingOfPendingEvents(); + + //@} + + + /** + @name Idle handling + */ + //@{ + + /** + Makes sure that idle events are sent again. + */ + virtual void WakeUpIdle(); + + /** + This virtual function is called when the application becomes idle and + normally just sends wxIdleEvent to all interested parties. + + It should return @true if more idle events are needed, @false if not. + */ + virtual bool ProcessIdle(); + + //@} + + + /** + @name Yield-related hooks + */ + //@{ + + /** + Returns @true if called from inside Yield(). + */ + virtual bool IsYielding() const; + + /** + Yields control to pending messages in the windowing system. + + This can be useful, for example, when a time-consuming process writes to a + text window. Without an occasional yield, the text window will not be updated + properly, and on systems with cooperative multitasking, such as Windows 3.1 + other processes will not respond. + + Caution should be exercised, however, since yielding may allow the + user to perform actions which are not compatible with the current task. + Disabling menu items or whole menus during processing can avoid unwanted + reentrance of code: see ::wxSafeYield for a better function. + You can avoid unwanted reentrancies also using IsYielding(). + + Note that Yield() will not flush the message logs. This is intentional as + calling Yield() is usually done to quickly update the screen and popping up + a message box dialog may be undesirable. If you do wish to flush the log + messages immediately (otherwise it will be done during the next idle loop + iteration), call wxLog::FlushActive. + + Calling Yield() recursively is normally an error and an assert failure is + raised in debug build if such situation is detected. However if the + @a onlyIfNeeded parameter is @true, the method will just silently + return @false instead. + */ + bool Yield(bool onlyIfNeeded = false); + + /** + Works like Yield() with @e onlyIfNeeded == @true, except that it allows + the caller to specify a mask of the ::wxEventCategory values which + indicates which events should be processed and which should instead + be "delayed" (i.e. processed by the main loop later). + + Note that this is a safer alternative to Yield() since it ensures that + only the events you're interested to will be processed; i.e. this method + helps to avoid unwanted reentrancies. + + Note that currently only wxMSW and wxGTK do support selective yield of + native events coming from the underlying GUI toolkit. + wxWidgets events posted using wxEvtHandler::AddPendingEvent or + wxEvtHandler::QueueEvent are instead selectively processed by all ports. + + @see wxEvent::GetEventCategory + */ + bool YieldFor(long eventsToProcess); + + /** + Returns @true if the given event category is allowed inside + a YieldFor() call (i.e. compares the given category against the + last mask passed to YieldFor()). + + @see wxEvent::GetEventCategory + */ + virtual bool IsEventAllowedInsideYield(wxEventCategory cat) const; + + //@} + + protected: /** This function is called before the event loop terminates, whether this diff --git a/src/common/appbase.cpp b/src/common/appbase.cpp index d159c9bdd8..8a774ae6a0 100644 --- a/src/common/appbase.cpp +++ b/src/common/appbase.cpp @@ -162,11 +162,6 @@ bool wxAppConsoleBase::Initialize(int& WXUNUSED(argc), wxChar **argv) GetTraits()->SetLocale(); #endif // wxUSE_INTL -#if wxUSE_THREADS - wxHandlersWithPendingEventsLocker = new wxCriticalSection; - wxHandlersWithPendingDelayedEvents = new wxList; -#endif - #ifndef __WXPALMOS__ if ( m_appName.empty() && argv && argv[0] ) { @@ -190,17 +185,6 @@ void wxAppConsoleBase::CleanUp() delete m_mainLoop; m_mainLoop = NULL; } - - delete wxHandlersWithPendingEvents; - wxHandlersWithPendingEvents = NULL; - - delete wxHandlersWithPendingDelayedEvents; - wxHandlersWithPendingDelayedEvents = NULL; - -#if wxUSE_THREADS - delete wxHandlersWithPendingEventsLocker; - wxHandlersWithPendingEventsLocker = NULL; -#endif // wxUSE_THREADS } // ---------------------------------------------------------------------------- @@ -291,7 +275,7 @@ wxAppTraits *wxAppConsoleBase::GetTraitsIfExists() } // ---------------------------------------------------------------------------- -// event processing +// wxEventLoop redirection // ---------------------------------------------------------------------------- int wxAppConsoleBase::MainLoop() @@ -331,81 +315,37 @@ bool wxAppConsoleBase::Dispatch() bool wxAppConsoleBase::HasPendingEvents() const { - wxENTER_CRIT_SECT( *wxHandlersWithPendingEventsLocker ); - - bool has = wxHandlersWithPendingEvents && !wxHandlersWithPendingEvents->IsEmpty(); - - wxLEAVE_CRIT_SECT( *wxHandlersWithPendingEventsLocker ); + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); - return has; + return loop && loop->HasPendingEvents(); } void wxAppConsoleBase::SuspendProcessingOfPendingEvents() { - wxENTER_CRIT_SECT( *wxHandlersWithPendingEventsLocker ); -} + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); -void wxAppConsoleBase::ResumeProcessingOfPendingEvents() -{ - wxLEAVE_CRIT_SECT( *wxHandlersWithPendingEventsLocker ); + if (loop) loop->SuspendProcessingOfPendingEvents(); } -/* static */ -bool wxAppConsoleBase::IsMainLoopRunning() +void wxAppConsoleBase::ResumeProcessingOfPendingEvents() { - const wxAppConsole * const app = GetInstance(); + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); - return app && app->m_mainLoop != NULL; + if (loop) loop->ResumeProcessingOfPendingEvents(); } void wxAppConsoleBase::ProcessPendingEvents() { -#if wxUSE_THREADS - if ( !wxHandlersWithPendingEventsLocker ) - return; -#endif - - wxENTER_CRIT_SECT( *wxHandlersWithPendingEventsLocker ); - - wxCHECK_RET( wxHandlersWithPendingDelayedEvents->IsEmpty(), - "this helper list should be empty" ); - - if (wxHandlersWithPendingEvents) - { - // iterate until the list becomes empty: the handlers remove themselves - // from it when they don't have any more pending events - wxList::compatibility_iterator node = wxHandlersWithPendingEvents->GetFirst(); - while (node) - { - // In ProcessPendingEvents(), new handlers might be added - // and we can safely leave the critical section here. - wxLEAVE_CRIT_SECT( *wxHandlersWithPendingEventsLocker ); - - wxEvtHandler *handler = (wxEvtHandler *)node->GetData(); - handler->ProcessPendingEvents(); - - wxENTER_CRIT_SECT( *wxHandlersWithPendingEventsLocker ); - - // restart as the iterators could have been invalidated - node = wxHandlersWithPendingEvents->GetFirst(); - } - } + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); - // now the wxHandlersWithPendingEvents is surely empty; however some event - // handlers may have moved themselves into wxHandlersWithPendingDelayedEvents - // because of a selective wxYield call in progress. - // Now we need to move them back to wxHandlersWithPendingEvents so the next - // call to this function has the chance of processing them: - if (!wxHandlersWithPendingDelayedEvents->IsEmpty()) - { - if (!wxHandlersWithPendingEvents) - wxHandlersWithPendingEvents = new wxList; + if (loop) loop->ProcessPendingEvents(); +} - WX_APPEND_LIST(wxHandlersWithPendingEvents, wxHandlersWithPendingDelayedEvents); - wxHandlersWithPendingDelayedEvents->Clear(); - } +bool wxAppConsoleBase::Yield(bool onlyIfNeeded) +{ + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); - wxLEAVE_CRIT_SECT( *wxHandlersWithPendingEventsLocker ); + return loop && loop->Yield(onlyIfNeeded); } void wxAppConsoleBase::WakeUpIdle() @@ -416,14 +356,21 @@ void wxAppConsoleBase::WakeUpIdle() bool wxAppConsoleBase::ProcessIdle() { - // process pending wx events before sending idle events - ProcessPendingEvents(); + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); + + return loop && loop->ProcessIdle(); +} + +// ---------------------------------------------------------------------------- +// events +// ---------------------------------------------------------------------------- - wxIdleEvent event; +/* static */ +bool wxAppConsoleBase::IsMainLoopRunning() +{ + const wxAppConsole * const app = GetInstance(); - event.SetEventObject(this); - ProcessEvent(event); - return event.MoreRequested(); + return app && app->m_mainLoop != NULL; } int wxAppConsoleBase::FilterEvent(wxEvent& WXUNUSED(event)) @@ -603,7 +550,6 @@ bool wxAppConsoleBase::CheckBuildOptions(const char *optionsSignature, // normally wxLogFatalError doesn't return return false; } -#undef wxCMP return true; } diff --git a/src/common/appcmn.cpp b/src/common/appcmn.cpp index f70be7b5ad..9a6cb43f66 100644 --- a/src/common/appcmn.cpp +++ b/src/common/appcmn.cpp @@ -40,6 +40,7 @@ #include "wx/msgout.h" #include "wx/thread.h" #include "wx/vidmode.h" +#include "wx/evtloop.h" #ifdef __WXDEBUG__ #if wxUSE_STACKWALKER @@ -78,9 +79,6 @@ wxAppBase::wxAppBase() m_isActive = true; - m_isInsideYield = false; - m_eventsToProcessInsideYield = wxEVT_CATEGORY_ALL; - // We don't want to exit the app if the user code shows a dialog from its // OnInit() -- but this is what would happen if we set m_exitOnFrameDelete // to Yes initially as this dialog would be the last top level window. @@ -326,23 +324,22 @@ void wxAppBase::SetActive(bool active, wxWindow * WXUNUSED(lastFocus)) (void)ProcessEvent(event); } -bool wxAppBase::IsEventAllowedInsideYield(wxEventCategory cat) const -{ - return (m_eventsToProcessInsideYield & cat) != 0; -} - bool wxAppBase::SafeYield(wxWindow *win, bool onlyIfNeeded) { wxWindowDisabler wd(win); - return Yield(onlyIfNeeded); + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); + + return loop && loop->Yield(onlyIfNeeded); } bool wxAppBase::SafeYieldFor(wxWindow *win, long eventsToProcess) { wxWindowDisabler wd(win); - return YieldFor(eventsToProcess); + wxEventLoopBase * const loop = wxEventLoopBase::GetActive(); + + return loop && loop->YieldFor(eventsToProcess); } diff --git a/src/common/event.cpp b/src/common/event.cpp index ce2a081526..7cd4a484c4 100644 --- a/src/common/event.cpp +++ b/src/common/event.cpp @@ -145,16 +145,6 @@ IMPLEMENT_DYNAMIC_CLASS(wxEventTableEntryModule, wxModule) // global variables // ---------------------------------------------------------------------------- -// List containing event handlers with pending events (each handler can occur -// at most once here) -wxList *wxHandlersWithPendingEvents = NULL; -wxList *wxHandlersWithPendingDelayedEvents = NULL; - -#if wxUSE_THREADS - // protects wxHandlersWithPendingEvents list - wxCriticalSection *wxHandlersWithPendingEventsLocker = NULL; -#endif - // common event types are defined here, other event types are defined by the // components which use them @@ -1037,10 +1027,6 @@ void wxEventHashTable::GrowEventTypeTable() // wxEvtHandler // ---------------------------------------------------------------------------- -/* - * Event handler - */ - wxEvtHandler::wxEvtHandler() { m_nextHandler = NULL; @@ -1085,41 +1071,16 @@ wxEvtHandler::~wxEvtHandler() delete entry; } delete m_dynamicEvents; - }; + } if (m_pendingEvents) m_pendingEvents->DeleteContents(true); delete m_pendingEvents; - // Remove us from wxHandlersWithPendingEvents if necessary. - if ( wxHandlersWithPendingEvents ) - { -#if wxUSE_THREADS - if (wxHandlersWithPendingEventsLocker) - wxENTER_CRIT_SECT(*wxHandlersWithPendingEventsLocker); -#endif - - if ( wxHandlersWithPendingEvents->DeleteObject(this) ) - { - // check that we were present only once in the list - wxASSERT_MSG( !wxHandlersWithPendingEvents->Find(this), - "Handler occurs twice in wxHandlersWithPendingEvents list" ); - } - //else: we weren't in this list at all, it's ok - - if ( wxHandlersWithPendingDelayedEvents->DeleteObject(this) ) - { - // check that we were present only once in the list - wxASSERT_MSG( !wxHandlersWithPendingDelayedEvents->Find(this), - "Handler occurs twice in wxHandlersWithPendingDelayedEvents list" ); - } - //else: we weren't in this list at all, it's ok - -#if wxUSE_THREADS - if (wxHandlersWithPendingEventsLocker) - wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker); -#endif - } + // Remove us from the list of the pending events if necessary. + wxEventLoopBase *loop = wxEventLoopBase::GetActive(); + if (loop) + loop->RemovePendingEventHandler(this); // we only delete object data, not untyped if ( m_clientDataType == wxClientData_Object ) @@ -1165,6 +1126,15 @@ void wxEvtHandler::QueueEvent(wxEvent *event) { wxCHECK_RET( event, "NULL event can't be posted" ); + wxEventLoopBase* loop = wxEventLoopBase::GetActive(); + if (!loop) + { + // we need an event loop which manages the list of event handlers with + // pending events... cannot proceed without it! + wxLogDebug("No event loop is running!"); + return; + } + // 1) Add this event to our list of pending events wxENTER_CRIT_SECT( m_pendingEventsLock ); @@ -1176,14 +1146,7 @@ void wxEvtHandler::QueueEvent(wxEvent *event) // 2) Add this event handler to list of event handlers that // have pending events. - wxENTER_CRIT_SECT(*wxHandlersWithPendingEventsLocker); - - if ( !wxHandlersWithPendingEvents ) - wxHandlersWithPendingEvents = new wxList; - if ( !wxHandlersWithPendingEvents->Find(this) ) - wxHandlersWithPendingEvents->Append(this); - - wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker); + loop->AppendPendingEventHandler(this); // only release m_pendingEventsLock now because otherwise there is a race // condition as described in the ticket #9093: we could process the event @@ -1200,6 +1163,15 @@ void wxEvtHandler::QueueEvent(wxEvent *event) void wxEvtHandler::ProcessPendingEvents() { + wxEventLoopBase* loop = wxEventLoopBase::GetActive(); + if (!loop) + { + // we need an event loop which manages the list of event handlers with + // pending events... cannot proceed without it! + wxLogDebug("No event loop is running!"); + return; + } + // we need to process only a single pending event in this call because // each call to ProcessEvent() could result in the destruction of this // same event handler (see the comment at the end of this function) @@ -1215,9 +1187,10 @@ void wxEvtHandler::ProcessPendingEvents() wxEvent* pEvent = static_cast(node->GetData()); // find the first event which can be processed now: - if (wxTheApp && wxTheApp->IsYielding()) + wxEventLoopBase* evtLoop = wxEventLoopBase::GetActive(); + if (evtLoop && evtLoop->IsYielding()) { - while (node && pEvent && !wxTheApp->IsEventAllowedInsideYield(pEvent->GetEventCategory())) + while (node && pEvent && !evtLoop->IsEventAllowedInsideYield(pEvent->GetEventCategory())) { node = node->GetNext(); pEvent = node ? static_cast(node->GetData()) : NULL; @@ -1226,19 +1199,11 @@ void wxEvtHandler::ProcessPendingEvents() if (!node) { // all our events are NOT processable now... signal this: -#if wxUSE_THREADS - if (wxHandlersWithPendingEventsLocker) - wxENTER_CRIT_SECT(*wxHandlersWithPendingEventsLocker); -#endif - // move us from the list of handlers with processable pending events - // to the list of handlers with pending events which needs to be processed later - wxHandlersWithPendingEvents->DeleteObject(this); - if ( !wxHandlersWithPendingDelayedEvents->Find(this) ) - wxHandlersWithPendingDelayedEvents->Append(this); -#if wxUSE_THREADS - if (wxHandlersWithPendingEventsLocker) - wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker); -#endif + loop->DelayPendingEventHandler(this); + + // see the comment at the beginning of evtloop.h header for the + // logic behind YieldFor() and behind DelayPendingEventHandler() + wxLEAVE_CRIT_SECT( m_pendingEventsLock ); return; @@ -1252,19 +1217,11 @@ void wxEvtHandler::ProcessPendingEvents() // same event again. m_pendingEvents->Erase(node); - // if there are no more pending events left, we don't need to stay in this - // list if ( m_pendingEvents->IsEmpty() ) { -#if wxUSE_THREADS - if (wxHandlersWithPendingEventsLocker) - wxENTER_CRIT_SECT(*wxHandlersWithPendingEventsLocker); -#endif - wxHandlersWithPendingEvents->DeleteObject(this); -#if wxUSE_THREADS - if (wxHandlersWithPendingEventsLocker) - wxLEAVE_CRIT_SECT(*wxHandlersWithPendingEventsLocker); -#endif + // if there are no more pending events left, we don't need to + // stay in this list + loop->RemovePendingEventHandler(this); } wxLEAVE_CRIT_SECT( m_pendingEventsLock ); diff --git a/src/common/evtloopcmn.cpp b/src/common/evtloopcmn.cpp index 18f9967c57..26848196fa 100644 --- a/src/common/evtloopcmn.cpp +++ b/src/common/evtloopcmn.cpp @@ -36,6 +36,154 @@ wxEventLoopBase *wxEventLoopBase::ms_activeLoop = NULL; +wxEventLoopBase::wxEventLoopBase() +{ + m_isInsideYield = false; + m_eventsToProcessInsideYield = wxEVT_CATEGORY_ALL; +} + +void wxEventLoopBase::DelayPendingEventHandler(wxEvtHandler* toDelay) +{ + wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker); + + // move the handler from the list of handlers with processable pending events + // to the list of handlers with pending events which needs to be processed later + m_handlersWithPendingEvents.Remove(toDelay); + + if (m_handlersWithPendingDelayedEvents.Index(toDelay) == wxNOT_FOUND) + m_handlersWithPendingDelayedEvents.Add(toDelay); + + wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker); +} + +void wxEventLoopBase::RemovePendingEventHandler(wxEvtHandler* toRemove) +{ + wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker); + + if (m_handlersWithPendingEvents.Index(toRemove) != wxNOT_FOUND) + { + m_handlersWithPendingEvents.Remove(toRemove); + + // check that the handler was present only once in the list + wxASSERT_MSG( m_handlersWithPendingEvents.Index(toRemove) == wxNOT_FOUND, + "Handler occurs twice in the m_handlersWithPendingEvents list!" ); + } + //else: it wasn't in this list at all, it's ok + + if (m_handlersWithPendingDelayedEvents.Index(toRemove) != wxNOT_FOUND) + { + m_handlersWithPendingDelayedEvents.Remove(toRemove); + + // check that the handler was present only once in the list + wxASSERT_MSG( m_handlersWithPendingDelayedEvents.Index(toRemove) == wxNOT_FOUND, + "Handler occurs twice in m_handlersWithPendingDelayedEvents list!" ); + } + //else: it wasn't in this list at all, it's ok + + wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker); +} + +void wxEventLoopBase::AppendPendingEventHandler(wxEvtHandler* toAppend) +{ + wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker); + + if ( m_handlersWithPendingEvents.Index(toAppend) == wxNOT_FOUND ) + m_handlersWithPendingEvents.Add(toAppend); + + wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker); +} + +bool wxEventLoopBase::HasPendingEvents() const +{ + wxENTER_CRIT_SECT(const_cast(this)->m_handlersWithPendingEventsLocker); + + bool has = !m_handlersWithPendingEvents.IsEmpty(); + + wxLEAVE_CRIT_SECT(const_cast(this)->m_handlersWithPendingEventsLocker); + + return has; +} + +void wxEventLoopBase::SuspendProcessingOfPendingEvents() +{ + wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker); +} + +void wxEventLoopBase::ResumeProcessingOfPendingEvents() +{ + wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker); +} + +void wxEventLoopBase::ProcessPendingEvents() +{ + wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker); + + wxCHECK_RET( m_handlersWithPendingDelayedEvents.IsEmpty(), + "this helper list should be empty" ); + + // iterate until the list becomes empty: the handlers remove themselves + // from it when they don't have any more pending events + while (!m_handlersWithPendingEvents.IsEmpty()) + { + // In ProcessPendingEvents(), new handlers might be added + // and we can safely leave the critical section here. + wxENTER_CRIT_SECT(m_handlersWithPendingEventsLocker); + + // NOTE: we always call ProcessPendingEvents() on the first event handler + // with pending events because handlers auto-remove themselves + // from this list (see RemovePendingEventHandler) if they have no + // more pending events. + m_handlersWithPendingEvents[0]->ProcessPendingEvents(); + + wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker); + } + + // now the wxHandlersWithPendingEvents is surely empty; however some event + // handlers may have moved themselves into wxHandlersWithPendingDelayedEvents + // because of a selective wxYield call in progress. + // Now we need to move them back to wxHandlersWithPendingEvents so the next + // call to this function has the chance of processing them: + if (!m_handlersWithPendingDelayedEvents.IsEmpty()) + { + WX_APPEND_ARRAY(m_handlersWithPendingEvents, m_handlersWithPendingDelayedEvents); + m_handlersWithPendingDelayedEvents.Clear(); + } + + wxLEAVE_CRIT_SECT(m_handlersWithPendingEventsLocker); +} + +void wxEventLoopBase::WakeUpIdle() +{ + WakeUp(); +} + +bool wxEventLoopBase::ProcessIdle() +{ + // process pending wx events before sending idle events + ProcessPendingEvents(); + + wxIdleEvent event; + + event.SetEventObject(wxTheApp); + wxTheApp->ProcessEvent(event); + return event.MoreRequested(); +} + +bool wxEventLoopBase::Yield(bool onlyIfNeeded) +{ + if ( m_isInsideYield ) + { + if ( !onlyIfNeeded ) + { + wxFAIL_MSG( wxT("wxYield called recursively" ) ); + } + + return false; + } + + return YieldFor(wxEVT_CATEGORY_ALL); +} + // wxEventLoopManual is unused in the other ports #if defined(__WXMSW__) || defined(__WXMAC__) || defined(__WXDFB__) || (defined(__UNIX__) && wxUSE_BASE) diff --git a/src/dfb/app.cpp b/src/dfb/app.cpp index 141bd1a031..931a9bfbe3 100644 --- a/src/dfb/app.cpp +++ b/src/dfb/app.cpp @@ -162,46 +162,3 @@ void wxApp::WakeUpIdle() wxMutexGuiLeave(); #endif } - -bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess) -{ -#if wxUSE_THREADS - if ( !wxThread::IsMain() ) - return true; // can't process events from other threads -#endif // wxUSE_THREADS - - if ( m_isInsideYield ) - { - if ( !onlyIfNeeded ) - { - wxFAIL_MSG( wxT("wxYield called recursively" ) ); - } - - return false; - } - - m_isInsideYield = true; - m_eventsToProcessInsideYield = eventsToProcess; - -#if wxUSE_LOG - wxLog::Suspend(); -#endif // wxUSE_LOG - - wxEventLoop * const - loop = static_cast(wxEventLoop::GetActive()); - if ( loop ) - loop->Yield(); - - // 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; -} diff --git a/src/dfb/evtloop.cpp b/src/dfb/evtloop.cpp index d18b73a251..eb86834bc5 100644 --- a/src/dfb/evtloop.cpp +++ b/src/dfb/evtloop.cpp @@ -202,8 +202,20 @@ wxIDirectFBEventBufferPtr wxGUIEventLoop::GetDirectFBEventBuffer() // events dispatch and loop handling //----------------------------------------------------------------------------- -void wxGUIEventLoop::Yield() +bool wxGUIEventLoop::YieldFor(long eventsToProcess) { +#if wxUSE_THREADS + if ( !wxThread::IsMain() ) + return true; // can't process events from other threads +#endif // wxUSE_THREADS + + m_isInsideYield = true; + m_eventsToProcessInsideYield = eventsToProcess; + +#if wxUSE_LOG + wxLog::Suspend(); +#endif // wxUSE_LOG + // TODO: implement event filtering using the eventsToProcess mask // process all pending events: @@ -212,4 +224,17 @@ void wxGUIEventLoop::Yield() // handle timers, sockets etc. OnNextIteration(); + + // 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; } diff --git a/src/generic/progdlgg.cpp b/src/generic/progdlgg.cpp index d05fdad6b1..6677a2ea33 100644 --- a/src/generic/progdlgg.cpp +++ b/src/generic/progdlgg.cpp @@ -42,6 +42,7 @@ #endif #include "wx/progdlg.h" +#include "wx/evtloop.h" // --------------------------------------------------------------------------- // macros @@ -402,7 +403,7 @@ wxProgressDialog::Update(int value, const wxString& newmsg, bool *skip) m_msg->SetLabel(_("Done.")); } - wxTheApp->YieldFor(wxEVT_CATEGORY_UI); + wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI); (void)ShowModal(); } @@ -452,7 +453,7 @@ bool wxProgressDialog::DoAfterUpdate(bool *skip) { // we have to yield because not only we want to update the display but // also to process the clicks on the cancel and skip buttons - wxTheApp->YieldFor(wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT); + wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI|wxEVT_CATEGORY_USER_INPUT); Update(); @@ -671,7 +672,7 @@ void wxProgressDialog::UpdateMessage(const wxString &newmsg) Fit(); // adapt to the new label size - wxTheApp->YieldFor(wxEVT_CATEGORY_UI); + wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_UI); } } diff --git a/src/gtk/app.cpp b/src/gtk/app.cpp index a7c984dd3b..18c05dda50 100644 --- a/src/gtk/app.cpp +++ b/src/gtk/app.cpp @@ -44,167 +44,6 @@ wxFORCE_LINK_MODULE(gnome_vfs) #endif -//----------------------------------------------------------------------------- -// global data -//----------------------------------------------------------------------------- - -static GtkWidget *gs_RootWindow = NULL; -static wxArrayPtrVoid g_arrGdkEvents; - -//----------------------------------------------------------------------------- -// wxYield -//----------------------------------------------------------------------------- - -bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess) -{ - if ( m_isInsideYield ) - { - if ( !onlyIfNeeded ) - { - wxFAIL_MSG( wxT("wxYield called recursively" ) ); - } - - return false; - } - -#if wxUSE_THREADS - if ( !wxThread::IsMain() ) - { - // can't call gtk_main_iteration() from other threads like this - 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 - - // NOTE: gtk_main_iteration() doesn't allow us to filter events, so we - // rather use gtk_main_do_event() after filtering the events at - // GDK level - - GdkDisplay* disp = gtk_widget_get_display(gs_RootWindow); - - // gdk_display_get_event() will transform X11 events into GDK events - // and will queue all of them in the display (private) structure; - // finally it will "unqueue" the last one and return it to us - GdkEvent* event = gdk_display_get_event(disp); - while (event) - { - // categorize the GDK event according to wxEventCategory. - // See http://library.gnome.org/devel/gdk/unstable/gdk-Events.html#GdkEventType - // for more info. - - wxEventCategory cat = wxEVT_CATEGORY_UNKNOWN; - switch (event->type) - { - case GDK_SELECTION_REQUEST: - case GDK_SELECTION_NOTIFY: - case GDK_SELECTION_CLEAR: - case GDK_OWNER_CHANGE: - cat = wxEVT_CATEGORY_CLIPBOARD; - break; - - - case GDK_KEY_PRESS: - case GDK_KEY_RELEASE: - case GDK_BUTTON_PRESS: - case GDK_2BUTTON_PRESS: - case GDK_3BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - case GDK_SCROLL: // generated from mouse buttons - case GDK_CLIENT_EVENT: - cat = wxEVT_CATEGORY_USER_INPUT; - break; - - - case GDK_PROXIMITY_IN: - case GDK_PROXIMITY_OUT: - - case GDK_MOTION_NOTIFY: - case GDK_ENTER_NOTIFY: - case GDK_LEAVE_NOTIFY: - case GDK_VISIBILITY_NOTIFY: - case GDK_PROPERTY_NOTIFY: - - case GDK_FOCUS_CHANGE: - case GDK_CONFIGURE: - case GDK_WINDOW_STATE: - case GDK_SETTING: - case GDK_DELETE: - case GDK_DESTROY: - - case GDK_EXPOSE: - case GDK_NO_EXPOSE: - case GDK_MAP: - case GDK_UNMAP: - //case GDK_DAMAGE: - - case GDK_DRAG_ENTER: - case GDK_DRAG_LEAVE: - case GDK_DRAG_MOTION: - case GDK_DRAG_STATUS: - case GDK_DROP_START: - case GDK_DROP_FINISHED: - case GDK_GRAB_BROKEN: - cat = wxEVT_CATEGORY_UI; - break; - - default: - cat = wxEVT_CATEGORY_UNKNOWN; - break; - } - - if (eventsToProcess & cat) - gtk_main_do_event(event); // process it now - else - g_arrGdkEvents.Add(event); // process it later - - // get next event - event = gdk_display_get_event(disp); - } - - if (eventsToProcess != wxEVT_CATEGORY_CLIPBOARD) - { - // 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). But we - // call ProcessIdle() only once since this is not meant for longish - // background jobs (controlled by wxIdleEvent::RequestMore() and the - // return value of Processidle(). - ProcessIdle(); // ProcessIdle() also calls ProcessPendingEvents() - } - //else: if we are inside ~wxClipboardSync() and we call ProcessIdle() and - // the user app contains an UI update handler which calls wxClipboard::IsSupported, - // then we fall into a never-ending loop... - - // put all unprocessed GDK events back in the queue - for (size_t i=0; iYieldFor(wxEVT_CATEGORY_CLIPBOARD); + wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_CLIPBOARD); } // this method must be called by GTK+ callbacks to indicate that we got the diff --git a/src/gtk/evtloop.cpp b/src/gtk/evtloop.cpp index a609bed729..0096351d1e 100644 --- a/src/gtk/evtloop.cpp +++ b/src/gtk/evtloop.cpp @@ -36,6 +36,8 @@ // wxEventLoop implementation // ============================================================================ +extern GtkWidget *wxGetRootWindow(); + // ---------------------------------------------------------------------------- // wxEventLoop running and exiting // ---------------------------------------------------------------------------- @@ -126,4 +128,146 @@ int wxGUIEventLoop::DispatchTimeout(unsigned long timeout) return !quit; } +//----------------------------------------------------------------------------- +// YieldFor +//----------------------------------------------------------------------------- + +bool wxGUIEventLoop::YieldFor(long eventsToProcess) +{ +#if wxUSE_THREADS + if ( !wxThread::IsMain() ) + { + // can't call gtk_main_iteration() from other threads like this + 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 + + // NOTE: gtk_main_iteration() doesn't allow us to filter events, so we + // rather use gtk_main_do_event() after filtering the events at + // GDK level + + GdkDisplay* disp = gtk_widget_get_display(wxGetRootWindow()); + + // gdk_display_get_event() will transform X11 events into GDK events + // and will queue all of them in the display (private) structure; + // finally it will "unqueue" the last one and return it to us + GdkEvent* event = gdk_display_get_event(disp); + while (event) + { + // categorize the GDK event according to wxEventCategory. + // See http://library.gnome.org/devel/gdk/unstable/gdk-Events.html#GdkEventType + // for more info. + + wxEventCategory cat = wxEVT_CATEGORY_UNKNOWN; + switch (event->type) + { + case GDK_SELECTION_REQUEST: + case GDK_SELECTION_NOTIFY: + case GDK_SELECTION_CLEAR: + case GDK_OWNER_CHANGE: + cat = wxEVT_CATEGORY_CLIPBOARD; + break; + + + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + case GDK_BUTTON_PRESS: + case GDK_2BUTTON_PRESS: + case GDK_3BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_SCROLL: // generated from mouse buttons + case GDK_CLIENT_EVENT: + cat = wxEVT_CATEGORY_USER_INPUT; + break; + + + case GDK_PROXIMITY_IN: + case GDK_PROXIMITY_OUT: + + case GDK_MOTION_NOTIFY: + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + case GDK_VISIBILITY_NOTIFY: + case GDK_PROPERTY_NOTIFY: + + case GDK_FOCUS_CHANGE: + case GDK_CONFIGURE: + case GDK_WINDOW_STATE: + case GDK_SETTING: + case GDK_DELETE: + case GDK_DESTROY: + + case GDK_EXPOSE: + case GDK_NO_EXPOSE: + case GDK_MAP: + case GDK_UNMAP: + //case GDK_DAMAGE: + + case GDK_DRAG_ENTER: + case GDK_DRAG_LEAVE: + case GDK_DRAG_MOTION: + case GDK_DRAG_STATUS: + case GDK_DROP_START: + case GDK_DROP_FINISHED: + case GDK_GRAB_BROKEN: + cat = wxEVT_CATEGORY_UI; + break; + + default: + cat = wxEVT_CATEGORY_UNKNOWN; + break; + } + + if (eventsToProcess & cat) + gtk_main_do_event(event); // process it now + else + m_arrGdkEvents.Add(event); // process it later + + // get next event + event = gdk_display_get_event(disp); + } + if (eventsToProcess != wxEVT_CATEGORY_CLIPBOARD) + { + // 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). But we + // call ProcessIdle() only once since this is not meant for longish + // background jobs (controlled by wxIdleEvent::RequestMore() and the + // return value of Processidle(). + ProcessIdle(); // ProcessIdle() also calls ProcessPendingEvents() + } + //else: if we are inside ~wxClipboardSync() and we call ProcessIdle() and + // the user app contains an UI update handler which calls wxClipboard::IsSupported, + // then we fall into a never-ending loop... + + // put all unprocessed GDK events back in the queue + for (size_t i=0; iRemoveIdleTag(); - -#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 - - // TODO: implement event filtering using the eventsToProcess mask - while (gtk_events_pending()) - gtk_main_iteration(); - - // 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). But we - // call ProcessIdle() only once since this is not meant for longish - // background jobs (controlled by wxIdleEvent::RequestMore() and the - // return value of Processidle(). - ProcessIdle(); - -#if wxUSE_LOG - // let the logs be flashed again - wxLog::Resume(); -#endif - - m_isInsideYield = false; - - return true; -} //----------------------------------------------------------------------------- // wxWakeUpIdle diff --git a/src/gtk1/evtloop.cpp b/src/gtk1/evtloop.cpp index 4364bb3122..1a32789eb9 100644 --- a/src/gtk1/evtloop.cpp +++ b/src/gtk1/evtloop.cpp @@ -117,3 +117,52 @@ bool wxGUIEventLoop::Dispatch() return true; } + +//----------------------------------------------------------------------------- +// wxYield +//----------------------------------------------------------------------------- + +bool wxGUIEventLoop::YieldFor(long eventsToProcess) +{ +#if wxUSE_THREADS + if ( !wxThread::IsMain() ) + { + // can't call gtk_main_iteration() from other threads like this + return true; + } +#endif // wxUSE_THREADS + + m_isInsideYield = true; + m_eventsToProcessInsideYield = eventsToProcess; + + // We need to remove idle callbacks or the loop will + // never finish. + wxTheApp->RemoveIdleTag(); + +#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 + + // TODO: implement event filtering using the eventsToProcess mask + while (gtk_events_pending()) + gtk_main_iteration(); + + // 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). But we + // call ProcessIdle() only once since this is not meant for longish + // background jobs (controlled by wxIdleEvent::RequestMore() and the + // return value of Processidle(). + ProcessIdle(); + +#if wxUSE_LOG + // let the logs be flashed again + wxLog::Resume(); +#endif + + m_isInsideYield = false; + + return true; +} diff --git a/src/mgl/app.cpp b/src/mgl/app.cpp index a43beb61f1..eaa8a04256 100644 --- a/src/mgl/app.cpp +++ b/src/mgl/app.cpp @@ -44,56 +44,6 @@ void wxApp::Exit() exit(0); } -//----------------------------------------------------------------------------- -// wxYield -//----------------------------------------------------------------------------- - -bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess) -{ - if ( m_isInsideYield ) - { - if ( !onlyIfNeeded ) - { - wxFAIL_MSG( wxT("wxYield called recursively" ) ); - } - - return false; - } - -#if wxUSE_THREADS - if ( !wxThread::IsMain() ) - { - // can't process events from other threads, MGL is thread-unsafe - return true; - } -#endif // wxUSE_THREADS - - m_isInsideYield = true; - m_eventsToProcessInsideYield = eventsToProcess; - - wxLog::Suspend(); - - wxEventLoopBase * const eventLoop = wxEventLoop::GetActive(); - if ( eventLoop ) - { - // TODO: implement event filtering using the eventsToProcess mask - - while (eventLoop->Pending()) - eventLoop->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 (wxTheApp->ProcessIdle()) { } - - wxLog::Resume(); - - m_isInsideYield = false; - - return true; -} - //----------------------------------------------------------------------------- // wxWakeUpIdle diff --git a/src/mgl/evtloop.cpp b/src/mgl/evtloop.cpp index ff5d985cce..452dbb4e0c 100644 --- a/src/mgl/evtloop.cpp +++ b/src/mgl/evtloop.cpp @@ -184,3 +184,39 @@ bool wxGUIEventLoop::Dispatch() return m_impl->GetKeepLooping(); } + +//----------------------------------------------------------------------------- +// wxYield +//----------------------------------------------------------------------------- + +bool wxGUIEventLoop::YieldFor(long eventsToProcess) +{ +#if wxUSE_THREADS + if ( !wxThread::IsMain() ) + { + // can't process events from other threads, MGL is thread-unsafe + return true; + } +#endif // wxUSE_THREADS + + m_isInsideYield = true; + m_eventsToProcessInsideYield = eventsToProcess; + + wxLog::Suspend(); + + // TODO: implement event filtering using the eventsToProcess mask + + 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 (wxTheApp->ProcessIdle()) { } + + wxLog::Resume(); + + m_isInsideYield = false; + + return true; +} diff --git a/src/motif/app.cpp b/src/motif/app.cpp index 11d8129d70..0a073548e7 100644 --- a/src/motif/app.cpp +++ b/src/motif/app.cpp @@ -468,32 +468,6 @@ void wxApp::SetTopLevelRealizedWidget(WXDisplay* display, WXWidget widget) .m_topLevelRealizedWidget = (Widget)widget; } -// Yield to other processes - -bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess) -{ - if ( m_isInsideYield ) - { - if ( !onlyIfNeeded ) - { - wxFAIL_MSG( wxT("wxYield called recursively" ) ); - } - - return false; - } - - m_isInsideYield = true; - m_eventsToProcessInsideYield = eventsToProcess; - - wxEventLoopGuarantor dummyLoopIfNeeded; - while (wxTheApp && wxTheApp->Pending()) - // TODO: implement event filtering using the eventsToProcess mask - wxTheApp->Dispatch(); - - m_isInsideYield = false; - - return true; -} // ---------------------------------------------------------------------------- // accessors for C modules diff --git a/src/motif/evtloop.cpp b/src/motif/evtloop.cpp index 367982dcb8..cd7800a380 100644 --- a/src/motif/evtloop.cpp +++ b/src/motif/evtloop.cpp @@ -137,6 +137,20 @@ void wxGUIEventLoop::Exit(int rc) ::wxBreakDispatch(); } +bool wxGUIEventLoop::YieldFor(ong eventsToProcess) +{ + m_isInsideYield = true; + m_eventsToProcessInsideYield = eventsToProcess; + + while (wxTheApp && wxTheApp->Pending()) + // TODO: implement event filtering using the eventsToProcess mask + wxTheApp->Dispatch(); + + m_isInsideYield = false; + + return true; +} + // ---------------------------------------------------------------------------- // wxEventLoop message processing dispatching // ---------------------------------------------------------------------------- diff --git a/src/msw/app.cpp b/src/msw/app.cpp index 5273274406..8222bb7060 100644 --- a/src/msw/app.cpp +++ b/src/msw/app.cpp @@ -1011,177 +1011,6 @@ int wxApp::GetShell32Version() #endif // !__WXWINCE__ -// ---------------------------------------------------------------------------- -// Yield to incoming messages -// ---------------------------------------------------------------------------- - -WX_DECLARE_OBJARRAY(MSG, wxMSGArray); - -#include -WX_DEFINE_OBJARRAY(wxMSGArray); - -static wxMSGArray g_arrMSG; - -bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess) -{ - if ( m_isInsideYield ) - { - if ( !onlyIfNeeded ) - { - wxFAIL_MSG( wxT("wxYield called recursively" ) ); - } - - return false; - } - - // set the flag and don't forget to reset it before returning - m_isInsideYield = true; - m_eventsToProcessInsideYield = eventsToProcess; - - wxON_BLOCK_EXIT_SET(m_isInsideYield, false); - -#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(); - - // ensure the logs will be flashed again when we exit - wxON_BLOCK_EXIT0(wxLog::Resume); -#endif // wxUSE_LOG - - // we don't want to process WM_QUIT from here - it should be processed in - // the main event loop in order to stop it - wxEventLoopGuarantor dummyLoopIfNeeded; - MSG msg; - while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) && - msg.message != WM_QUIT ) - { -#if wxUSE_THREADS - wxMutexGuiLeaveOrEnter(); -#endif // wxUSE_THREADS - - if (msg.message == WM_PAINT) - { - // WM_PAINT messages are the last ones of the queue... - break; - } - - // choose a wxEventCategory for this Windows message - wxEventCategory cat; - switch (msg.message) - { - case WM_NCMOUSEMOVE: - case WM_NCLBUTTONDOWN: - case WM_NCLBUTTONUP: - case WM_NCLBUTTONDBLCLK: - case WM_NCRBUTTONDOWN: - case WM_NCRBUTTONUP: - case WM_NCRBUTTONDBLCLK: - case WM_NCMBUTTONDOWN: - case WM_NCMBUTTONUP: - case WM_NCMBUTTONDBLCLK: - - case WM_KEYDOWN: - case WM_KEYUP: - case WM_CHAR: - case WM_DEADCHAR: - case WM_SYSKEYDOWN: - case WM_SYSKEYUP: - case WM_SYSCHAR: - case WM_SYSDEADCHAR: -#ifdef WM_UNICHAR - case WM_UNICHAR: -#endif - case WM_HOTKEY: - case WM_IME_STARTCOMPOSITION: - case WM_IME_ENDCOMPOSITION: - case WM_IME_COMPOSITION: - case WM_COMMAND: - case WM_SYSCOMMAND: - - case WM_IME_SETCONTEXT: - case WM_IME_NOTIFY: - case WM_IME_CONTROL: - case WM_IME_COMPOSITIONFULL: - case WM_IME_SELECT: - case WM_IME_CHAR: - case WM_IME_KEYDOWN: - case WM_IME_KEYUP: - - case WM_MOUSEHOVER: -#ifdef WM_NCMOUSELEAVE - case WM_NCMOUSELEAVE: -#endif - case WM_MOUSELEAVE: - - case WM_CUT: - case WM_COPY: - case WM_PASTE: - case WM_CLEAR: - case WM_UNDO: - - case WM_MOUSEMOVE: - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_MBUTTONDBLCLK: - case WM_MOUSEWHEEL: - cat = wxEVT_CATEGORY_USER_INPUT; - break; - - case WM_TIMER: - cat = wxEVT_CATEGORY_TIMER; - break; - - default: - if (msg.message < WM_USER) - { - // 0;WM_USER-1 is the range of message IDs reserved for use - // by the system. - - // there are too many of these types of messages to handle - // them in this switch - cat = wxEVT_CATEGORY_UI; - } - else - cat = wxEVT_CATEGORY_UNKNOWN; - } - - // should we process this event now? - if (cat & eventsToProcess) - { - if ( !wxTheApp->Dispatch() ) - break; - } - else - { - // remove the message and store it - ::GetMessage(&msg, NULL, 0, 0); - g_arrMSG.Add(msg); - } - } - - // if there are pending events, we must process them. - ProcessPendingEvents(); - - // put back unprocessed events in the queue - DWORD id = GetCurrentThreadId(); - for (size_t i=0; i +WX_DEFINE_OBJARRAY(wxMSGArray); + +bool wxGUIEventLoop::YieldFor(long eventsToProcess) +{ + // set the flag and don't forget to reset it before returning + m_isInsideYield = true; + m_eventsToProcessInsideYield = eventsToProcess; + + wxON_BLOCK_EXIT_SET(m_isInsideYield, false); + +#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(); + + // ensure the logs will be flashed again when we exit + wxON_BLOCK_EXIT0(wxLog::Resume); +#endif // wxUSE_LOG + + // we don't want to process WM_QUIT from here - it should be processed in + // the main event loop in order to stop it + MSG msg; + while ( PeekMessage(&msg, (HWND)0, 0, 0, PM_NOREMOVE) && + msg.message != WM_QUIT ) + { +#if wxUSE_THREADS + wxMutexGuiLeaveOrEnter(); +#endif // wxUSE_THREADS + + if (msg.message == WM_PAINT) + { + // WM_PAINT messages are the last ones of the queue... + break; + } + + // choose a wxEventCategory for this Windows message + wxEventCategory cat; + switch (msg.message) + { + case WM_NCMOUSEMOVE: + case WM_NCLBUTTONDOWN: + case WM_NCLBUTTONUP: + case WM_NCLBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + case WM_NCRBUTTONDBLCLK: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONUP: + case WM_NCMBUTTONDBLCLK: + + case WM_KEYDOWN: + case WM_KEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: +#ifdef WM_UNICHAR + case WM_UNICHAR: +#endif + case WM_HOTKEY: + case WM_IME_STARTCOMPOSITION: + case WM_IME_ENDCOMPOSITION: + case WM_IME_COMPOSITION: + case WM_COMMAND: + case WM_SYSCOMMAND: + + case WM_IME_SETCONTEXT: + case WM_IME_NOTIFY: + case WM_IME_CONTROL: + case WM_IME_COMPOSITIONFULL: + case WM_IME_SELECT: + case WM_IME_CHAR: + case WM_IME_KEYDOWN: + case WM_IME_KEYUP: + + case WM_MOUSEHOVER: +#ifdef WM_NCMOUSELEAVE + case WM_NCMOUSELEAVE: +#endif + case WM_MOUSELEAVE: + + case WM_CUT: + case WM_COPY: + case WM_PASTE: + case WM_CLEAR: + case WM_UNDO: + + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONUP: + case WM_RBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONUP: + case WM_MBUTTONDBLCLK: + case WM_MOUSEWHEEL: + cat = wxEVT_CATEGORY_USER_INPUT; + break; + + case WM_TIMER: + cat = wxEVT_CATEGORY_TIMER; + break; + + default: + if (msg.message < WM_USER) + { + // 0;WM_USER-1 is the range of message IDs reserved for use + // by the system. + + // there are too many of these types of messages to handle + // them in this switch + cat = wxEVT_CATEGORY_UI; + } + else + cat = wxEVT_CATEGORY_UNKNOWN; + } + + // should we process this event now? + if (cat & eventsToProcess) + { + if ( !wxTheApp->Dispatch() ) + break; + } + else + { + // remove the message and store it + ::GetMessage(&msg, NULL, 0, 0); + m_arrMSG.Add(msg); + } + } + + // if there are pending events, we must process them. + ProcessPendingEvents(); + + // put back unprocessed events in the queue + DWORD id = GetCurrentThreadId(); + for (size_t i=0; iDispatch()) - break; - } - - // - // If they are pending events, we must process them. - // - if (wxTheApp) - wxTheApp->ProcessPendingEvents(); - - HandleSockets(); - - // - // Let the logs be flashed again - // - wxLog::Resume(); - m_isInsideYield = false; - - return true; -} // end of wxYield - int wxApp::AddSocketHandler(int handle, int mask, void (*callback)(void*), void * gsock) { diff --git a/src/os2/evtloop.cpp b/src/os2/evtloop.cpp index b054bfce48..9399da2c82 100644 --- a/src/os2/evtloop.cpp +++ b/src/os2/evtloop.cpp @@ -365,3 +365,52 @@ bool wxGUIEventLoop::Dispatch() return true; } + +// +// Yield to incoming messages +// +bool wxGUIEventLoop::YieldFor(long eventsToProcess) +{ + HAB vHab = 0; + QMSG vMsg; + + // + // Disable log flushing from here because a call to wxYield() shouldn't + // normally result in message boxes popping up &c + // + wxLog::Suspend(); + + m_isInsideYield = true; + m_eventsToProcessInsideYield = eventsToProcess; + + // + // We want to go back to the main message loop + // if we see a WM_QUIT. (?) + // + while (::WinPeekMsg(vHab, &vMsg, (HWND)NULL, 0, 0, PM_NOREMOVE) && vMsg.msg != WM_QUIT) + { + // TODO: implement event filtering using the eventsToProcess mask + +#if wxUSE_THREADS + wxMutexGuiLeaveOrEnter(); +#endif // wxUSE_THREADS + if (!wxTheApp->Dispatch()) + break; + } + + // + // If they are pending events, we must process them. + // + if (wxTheApp) + wxTheApp->ProcessPendingEvents(); + + HandleSockets(); + + // + // Let the logs be flashed again + // + wxLog::Resume(); + m_isInsideYield = false; + + return true; +} // end of wxYield diff --git a/src/palmos/app.cpp b/src/palmos/app.cpp index b1f8a3d30f..8b98b53d7e 100644 --- a/src/palmos/app.cpp +++ b/src/palmos/app.cpp @@ -285,13 +285,6 @@ int wxApp::GetComCtl32Version() return 0; } -// Yield to incoming messages - -bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess) -{ - return true; -} - #if wxUSE_EXCEPTIONS // ---------------------------------------------------------------------------- diff --git a/src/palmos/evtloop.cpp b/src/palmos/evtloop.cpp index 5835fb3df8..d9d22bd881 100644 --- a/src/palmos/evtloop.cpp +++ b/src/palmos/evtloop.cpp @@ -144,3 +144,8 @@ void wxGUIEventLoop::WakeUp() return; } +bool wxGUIEventLoop::YieldFor(long eventsToProcess) +{ + return true; +} + diff --git a/src/x11/app.cpp b/src/x11/app.cpp index e37d35ad28..e1a396c53f 100644 --- a/src/x11/app.cpp +++ b/src/x11/app.cpp @@ -767,52 +767,6 @@ void wxApp::Exit() wxAppConsole::Exit(); } -// Yield to other processes - -bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess) -{ - // Sometimes only 2 yields seem - // to do the trick, e.g. in the - // progress dialog - int i; - for (i = 0; i < 2; i++) - { - if ( m_isInsideYield ) - { - if ( !onlyIfNeeded ) - { - wxFAIL_MSG( wxT("wxYield called recursively" ) ); - } - - return false; - } - - m_isInsideYield = true; - m_eventsToProcessInsideYield = eventsToProcess; - - // Make sure we have an event loop object, - // or Pending/Dispatch will fail - wxEventLoopGuarantor dummyLoopIfNeeded; - - // Call dispatch at least once so that sockets - // can be tested - wxTheApp->Dispatch(); - - // TODO: implement event filtering using the eventsToProcess mask - while (wxTheApp && wxTheApp->Pending()) - wxTheApp->Dispatch(); - -#if wxUSE_TIMER - wxGenericTimerImpl::NotifyTimers(); -#endif - ProcessIdle(); - - m_isInsideYield = false; - } - - return true; -} - #ifdef __WXDEBUG__ void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg) diff --git a/src/x11/evtloop.cpp b/src/x11/evtloop.cpp index eab9042841..bfcf4917af 100644 --- a/src/x11/evtloop.cpp +++ b/src/x11/evtloop.cpp @@ -243,3 +243,33 @@ bool wxGUIEventLoop::Dispatch() (void) m_impl->ProcessEvent( &event ); return true; } + +bool wxGUIEventLoop::YieldFor(long eventsToProcess) +{ + // Sometimes only 2 yields seem + // to do the trick, e.g. in the + // progress dialog + int i; + for (i = 0; i < 2; i++) + { + m_isInsideYield = true; + m_eventsToProcessInsideYield = eventsToProcess; + + // Call dispatch at least once so that sockets + // can be tested + wxTheApp->Dispatch(); + + // TODO: implement event filtering using the eventsToProcess mask + while (wxTheApp && wxTheApp->Pending()) + wxTheApp->Dispatch(); + +#if wxUSE_TIMER + wxGenericTimerImpl::NotifyTimers(); +#endif + ProcessIdle(); + + m_isInsideYield = false; + } + + return true; +} -- 2.45.2