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
This commit is contained in:
Francesco Montorsi 2009-02-15 14:25:08 +00:00
parent 145bbf1fdf
commit dde19c2180
50 changed files with 1104 additions and 1070 deletions

View File

@ -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:

View File

@ -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,87 +250,34 @@ public:
// exit, if you need to really handle the exceptions you need to override
// OnExceptionInMainLoop()
virtual void OnUnhandledException();
#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();
// 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();
// 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();
// 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 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
// wxEventLoop redirections
// ------------------------
virtual void SuspendProcessingOfPendingEvents();
virtual void ResumeProcessingOfPendingEvents();
virtual void ProcessPendingEvents();
bool HasPendingEvents() const;
virtual bool Pending();
virtual bool Dispatch();
virtual int MainLoop();
virtual void ExitMainLoop();
bool Yield(bool onlyIfNeeded = false);
virtual void WakeUpIdle();
virtual bool ProcessIdle();
// 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);
};

View File

@ -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);

View File

@ -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;

View File

@ -39,8 +39,6 @@ public:
private:
wxVideoMode m_videoMode;
virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess);
DECLARE_DYNAMIC_CLASS(wxApp)
};

View File

@ -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();

View File

@ -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
// ----------------------------------------------------------------------------

View File

@ -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

View File

@ -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__

View File

@ -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);
};

View File

@ -70,8 +70,6 @@ private:
bool m_isInAssert;
#endif // __WXDEBUG__
virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess);
DECLARE_DYNAMIC_CLASS(wxApp)
};

View File

@ -51,8 +51,6 @@ public:
private:
DECLARE_DYNAMIC_CLASS(wxApp)
virtual bool DoYield(bool onlyIfNeeded, long eventsToProcess);
wxVideoMode m_displayMode;
};

View File

@ -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; }

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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 ; }

View File

@ -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

View File

@ -22,6 +22,7 @@ public:
virtual int DispatchTimeout(unsigned long timeout);
virtual void WakeUp();
virtual bool YieldFor(long eventsToProcess);
private:
double m_sleepTime;

View File

@ -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();

View File

@ -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
// --------------------

View File

@ -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();

View File

@ -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);

View File

@ -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).
Called by wxWidgets on creation of the application. Override this if you wish
to provide your own (environment-dependent) main loop.
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.
@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;
//@}

View File

@ -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;
};

View File

@ -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

View File

@ -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 );
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
bool has = wxHandlersWithPendingEvents && !wxHandlersWithPendingEvents->IsEmpty();
wxLEAVE_CRIT_SECT( *wxHandlersWithPendingEventsLocker );
return has;
return loop && loop->HasPendingEvents();
}
void wxAppConsoleBase::SuspendProcessingOfPendingEvents()
{
wxENTER_CRIT_SECT( *wxHandlersWithPendingEventsLocker );
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
if (loop) loop->SuspendProcessingOfPendingEvents();
}
void wxAppConsoleBase::ResumeProcessingOfPendingEvents()
{
wxLEAVE_CRIT_SECT( *wxHandlersWithPendingEventsLocker );
}
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
/* static */
bool wxAppConsoleBase::IsMainLoopRunning()
{
const wxAppConsole * const app = GetInstance();
return app && app->m_mainLoop != NULL;
if (loop) loop->ResumeProcessingOfPendingEvents();
}
void wxAppConsoleBase::ProcessPendingEvents()
{
#if wxUSE_THREADS
if ( !wxHandlersWithPendingEventsLocker )
return;
#endif
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
wxENTER_CRIT_SECT( *wxHandlersWithPendingEventsLocker );
if (loop) loop->ProcessPendingEvents();
}
wxCHECK_RET( wxHandlersWithPendingDelayedEvents->IsEmpty(),
"this helper list should be empty" );
bool wxAppConsoleBase::Yield(bool onlyIfNeeded)
{
wxEventLoopBase * const loop = wxEventLoopBase::GetActive();
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();
}
}
// 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;
WX_APPEND_LIST(wxHandlersWithPendingEvents, wxHandlersWithPendingDelayedEvents);
wxHandlersWithPendingDelayedEvents->Clear();
}
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();
wxIdleEvent event;
return loop && loop->ProcessIdle();
}
event.SetEventObject(this);
ProcessEvent(event);
return event.MoreRequested();
// ----------------------------------------------------------------------------
// events
// ----------------------------------------------------------------------------
/* static */
bool wxAppConsoleBase::IsMainLoopRunning()
{
const wxAppConsole * const app = GetInstance();
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;
}

View File

@ -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);
}

View File

@ -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<wxEvent *>(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<wxEvent *>(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 );

View File

@ -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<wxEventLoopBase*>(this)->m_handlersWithPendingEventsLocker);
bool has = !m_handlersWithPendingEvents.IsEmpty();
wxLEAVE_CRIT_SECT(const_cast<wxEventLoopBase*>(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)

View File

@ -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 *>(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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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; i<g_arrGdkEvents.GetCount(); i++)
{
GdkEvent* ev = (GdkEvent*)g_arrGdkEvents[i];
// NOTE: gdk_display_put_event makes a copy of the event passed to it
gdk_display_put_event(disp, ev);
gdk_event_free(ev);
}
g_arrGdkEvents.Clear();
#if wxUSE_LOG
// let the logs be flashed again
wxLog::Resume();
#endif
m_isInsideYield = false;
return true;
}
//-----------------------------------------------------------------------------
// local functions
//-----------------------------------------------------------------------------
@ -324,12 +163,14 @@ bool wxApp::DoIdle()
GtkWidget* wxGetRootWindow()
{
if (gs_RootWindow == NULL)
static GtkWidget *s_RootWindow = NULL;
if (s_RootWindow == NULL)
{
gs_RootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
gtk_widget_realize( gs_RootWindow );
s_RootWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
gtk_widget_realize( s_RootWindow );
}
return gs_RootWindow;
return s_RootWindow;
}
//-----------------------------------------------------------------------------

View File

@ -32,6 +32,7 @@
#include "wx/scopedarray.h"
#include "wx/scopeguard.h"
#include "wx/evtloop.h"
#include "wx/gtk/private.h"
@ -75,7 +76,7 @@ public:
~wxClipboardSync()
{
while (ms_clipboard)
wxTheApp->YieldFor(wxEVT_CATEGORY_CLIPBOARD);
wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_CLIPBOARD);
}
// this method must be called by GTK+ callbacks to indicate that we got the

View File

@ -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; i<m_arrGdkEvents.GetCount(); i++)
{
GdkEvent* ev = (GdkEvent*)m_arrGdkEvents[i];
// NOTE: gdk_display_put_event makes a copy of the event passed to it
gdk_display_put_event(disp, ev);
gdk_event_free(ev);
}
m_arrGdkEvents.Clear();
#if wxUSE_LOG
// let the logs be flashed again
wxLog::Resume();
#endif
m_isInsideYield = false;
return true;
}

View File

@ -99,64 +99,6 @@ void wxapp_install_idle_handler();
static wxMutex gs_idleTagsMutex;
#endif
//-----------------------------------------------------------------------------
// 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;
// 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;
}
//-----------------------------------------------------------------------------
// wxWakeUpIdle

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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
// ----------------------------------------------------------------------------

View File

@ -1011,177 +1011,6 @@ int wxApp::GetShell32Version()
#endif // !__WXWINCE__
// ----------------------------------------------------------------------------
// Yield to incoming messages
// ----------------------------------------------------------------------------
WX_DECLARE_OBJARRAY(MSG, wxMSGArray);
#include <wx/arrimpl.cpp>
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<g_arrMSG.GetCount(); i++)
{
PostThreadMessage(id, g_arrMSG[i].message,
g_arrMSG[i].wParam, g_arrMSG[i].lParam);
}
g_arrMSG.Clear();
return true;
}
#if wxUSE_EXCEPTIONS
// ----------------------------------------------------------------------------

View File

@ -35,6 +35,7 @@
#include "wx/thread.h"
#include "wx/except.h"
#include "wx/msw/private.h"
#include "wx/scopeguard.h"
#if wxUSE_GUI
#include "wx/tooltip.h"
@ -358,6 +359,164 @@ void wxGUIEventLoop::WakeUp()
::PostMessage(NULL, WM_NULL, 0, 0);
}
// ----------------------------------------------------------------------------
// Yield to incoming messages
// ----------------------------------------------------------------------------
#include <wx/arrimpl.cpp>
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; i<m_arrMSG.GetCount(); i++)
{
PostThreadMessage(id, m_arrMSG[i].message,
m_arrMSG[i].wParam, m_arrMSG[i].lParam);
}
m_arrMSG.Clear();
return true;
}
#else // !wxUSE_GUI

View File

@ -504,66 +504,6 @@ void wxApp::OnQueryEndSession( wxCloseEvent& rEvent )
}
} // end of wxApp::OnQueryEndSession
//
// Yield to incoming messages
//
bool wxApp::DoYield(bool onlyIfNeeded, long eventsToProcess)
{
if ( m_isInsideYield )
{
if ( !onlyIfNeeded )
{
wxFAIL_MSG( _T("wxYield() called recursively") );
}
return false;
}
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. (?)
//
wxEventLoopGuarantor dummyLoopIfNeeded;
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
int wxApp::AddSocketHandler(int handle, int mask,
void (*callback)(void*), void * gsock)
{

View File

@ -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

View File

@ -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
// ----------------------------------------------------------------------------

View File

@ -144,3 +144,8 @@ void wxGUIEventLoop::WakeUp()
return;
}
bool wxGUIEventLoop::YieldFor(long eventsToProcess)
{
return true;
}

View File

@ -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)

View File

@ -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;
}