1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        wx/evtloop.h
3 // Purpose:     declares wxEventLoop class
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     01.06.01
7 // Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
8 // Licence:     wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 #ifndef _WX_EVTLOOP_H_
12 #define _WX_EVTLOOP_H_
13 
14 #include "wx/event.h"
15 #include "wx/utils.h"
16 
17 // TODO: implement wxEventLoopSource for MSW (it should wrap a HANDLE and be
18 //       monitored using MsgWaitForMultipleObjects())
19 #if defined(__WXOSX__) || (defined(__UNIX__) && !defined(__WINDOWS__))
20     #define wxUSE_EVENTLOOP_SOURCE 1
21 #else
22     #define wxUSE_EVENTLOOP_SOURCE 0
23 #endif
24 
25 #if wxUSE_EVENTLOOP_SOURCE
26     class wxEventLoopSource;
27     class wxEventLoopSourceHandler;
28 #endif
29 
30 /*
31     NOTE ABOUT wxEventLoopBase::YieldFor LOGIC
32     ------------------------------------------
33 
34     The YieldFor() function helps to avoid re-entrancy problems and problems
35     caused by out-of-order event processing
36     (see "wxYield-like problems" and "wxProgressDialog+threading BUG" wx-dev threads).
37 
38     The logic behind YieldFor() is simple: it analyzes the queue of the native
39     events generated by the underlying GUI toolkit and picks out and processes
40     only those matching the given mask.
41 
42     It's important to note that YieldFor() is used to selectively process the
43     events generated by the NATIVE toolkit.
44     Events syntethized by wxWidgets code or by user code are instead selectively
45     processed thanks to the logic built into wxEvtHandler::ProcessPendingEvents().
46     In fact, when wxEvtHandler::ProcessPendingEvents gets called from inside a
47     YieldFor() call, wxEventLoopBase::IsEventAllowedInsideYield is used to decide
48     if the pending events for that event handler can be processed.
49     If all the pending events associated with that event handler result as "not processable",
50     the event handler "delays" itself calling wxEventLoopBase::DelayPendingEventHandler
51     (so it's moved: m_handlersWithPendingEvents => m_handlersWithPendingDelayedEvents).
52     Last, wxEventLoopBase::ProcessPendingEvents() before exiting moves the delayed
53     event handlers back into the list of handlers with pending events
54     (m_handlersWithPendingDelayedEvents => m_handlersWithPendingEvents) so that
55     a later call to ProcessPendingEvents() (possibly outside the YieldFor() call)
56     will process all pending events as usual.
57 */
58 
59 // ----------------------------------------------------------------------------
60 // wxEventLoopBase: interface for wxEventLoop
61 // ----------------------------------------------------------------------------
62 
63 class WXDLLIMPEXP_BASE wxEventLoopBase
64 {
65 public:
66     // trivial, but needed (because of wxEventLoopBase) ctor
67     wxEventLoopBase();
68 
69     // dtor
~wxEventLoopBase()70     virtual ~wxEventLoopBase() { }
71 
72     // use this to check whether the event loop was successfully created before
73     // using it
IsOk()74     virtual bool IsOk() const { return true; }
75 
76     // returns true if this is the main loop
77     bool IsMain() const;
78 
79 #if wxUSE_EVENTLOOP_SOURCE
80     // create a new event loop source wrapping the given file descriptor and
81     // monitor it for events occurring on this descriptor in all event loops
82     static wxEventLoopSource *
83       AddSourceForFD(int fd, wxEventLoopSourceHandler *handler, int flags);
84 #endif // wxUSE_EVENTLOOP_SOURCE
85 
86     // dispatch&processing
87     // -------------------
88 
89     // start the event loop, return the exit code when it is finished
90     //
91     // notice that wx ports should override DoRun(), this method is virtual
92     // only to allow overriding it in the user code for custom event loops
93     virtual int Run();
94 
95     // is this event loop running now?
96     //
97     // notice that even if this event loop hasn't terminated yet but has just
98     // spawned a nested (e.g. modal) event loop, this would return false
99     bool IsRunning() const;
100 
101     // exit from the loop with the given exit code
102     //
103     // this can be only used to exit the currently running loop, use
104     // ScheduleExit() if this might not be the case
105     virtual void Exit(int rc = 0);
106 
107     // ask the event loop to exit with the given exit code, can be used even if
108     // this loop is not running right now but the loop must have been started,
109     // i.e. Run() should have been already called
110     virtual void ScheduleExit(int rc = 0) = 0;
111 
112     // return true if any events are available
113     virtual bool Pending() const = 0;
114 
115     // dispatch a single event, return false if we should exit from the loop
116     virtual bool Dispatch() = 0;
117 
118     // same as Dispatch() but doesn't wait for longer than the specified (in
119     // ms) timeout, return true if an event was processed, false if we should
120     // exit the loop or -1 if timeout expired
121     virtual int DispatchTimeout(unsigned long timeout) = 0;
122 
123     // implement this to wake up the loop: usually done by posting a dummy event
124     // to it (can be called from non main thread)
125     virtual void WakeUp() = 0;
126 
127 
128     // idle handling
129     // -------------
130 
131         // make sure that idle events are sent again
132     virtual void WakeUpIdle();
133 
134         // this virtual function is called  when the application
135         // becomes idle and by default it forwards to wxApp::ProcessIdle() and
136         // while it can be overridden in a custom event loop, you must call the
137         // base class version to ensure that idle events are still generated
138         //
139         // it should return true if more idle events are needed, false if not
140     virtual bool ProcessIdle();
141 
142 
143     // Yield-related hooks
144     // -------------------
145 
146     // process all currently pending events right now
147     //
148     // it is an error to call Yield() recursively unless the value of
149     // onlyIfNeeded is true
150     //
151     // WARNING: this function is dangerous as it can lead to unexpected
152     //          reentrancies (i.e. when called from an event handler it
153     //          may result in calling the same event handler again), use
154     //          with _extreme_ care or, better, don't use at all!
155     bool Yield(bool onlyIfNeeded = false);
156     virtual bool YieldFor(long eventsToProcess) = 0;
157 
158     // returns true if the main thread is inside a Yield() call
IsYielding()159     virtual bool IsYielding() const
160         { return m_isInsideYield; }
161 
162     // returns true if events of the given event category should be immediately
163     // processed inside a wxApp::Yield() call or rather should be queued for
164     // later processing by the main event loop
IsEventAllowedInsideYield(wxEventCategory cat)165     virtual bool IsEventAllowedInsideYield(wxEventCategory cat) const
166         { return (m_eventsToProcessInsideYield & cat) != 0; }
167 
168     // no SafeYield hooks since it uses wxWindow which is not available when wxUSE_GUI=0
169 
170 
171     // active loop
172     // -----------
173 
174     // return currently active (running) event loop, may be NULL
GetActive()175     static wxEventLoopBase *GetActive() { return ms_activeLoop; }
176 
177     // set currently active (running) event loop
178     static void SetActive(wxEventLoopBase* loop);
179 
180 
181 protected:
182     // real implementation of Run()
183     virtual int DoRun() = 0;
184 
185     // this function should be called before the event loop terminates, whether
186     // this happens normally (because of Exit() call) or abnormally (because of
187     // an exception thrown from inside the loop)
188     virtual void OnExit();
189 
190     // Return true if we're currently inside our Run(), even if another nested
191     // event loop is currently running, unlike IsRunning() (which should have
192     // been really called IsActive() but it's too late to change this now).
IsInsideRun()193     bool IsInsideRun() const { return m_isInsideRun; }
194 
195 
196     // the pointer to currently active loop
197     static wxEventLoopBase *ms_activeLoop;
198 
199     // should we exit the loop?
200     bool m_shouldExit;
201 
202     // YieldFor() helpers:
203     bool m_isInsideYield;
204     long m_eventsToProcessInsideYield;
205 
206 private:
207     // this flag is set on entry into Run() and reset before leaving it
208     bool m_isInsideRun;
209 
210     wxDECLARE_NO_COPY_CLASS(wxEventLoopBase);
211 };
212 
213 #if defined(__WINDOWS__) || defined(__WXMAC__) || defined(__WXDFB__) || (defined(__UNIX__) && !defined(__WXOSX__))
214 
215 // this class can be used to implement a standard event loop logic using
216 // Pending() and Dispatch()
217 //
218 // it also handles idle processing automatically
219 class WXDLLIMPEXP_BASE wxEventLoopManual : public wxEventLoopBase
220 {
221 public:
222     wxEventLoopManual();
223 
224     // sets the "should exit" flag and wakes up the loop so that it terminates
225     // soon
226     virtual void ScheduleExit(int rc = 0);
227 
228 protected:
229     // enters a loop calling OnNextIteration(), Pending() and Dispatch() and
230     // terminating when Exit() is called
231     virtual int DoRun();
232 
233     // may be overridden to perform some action at the start of each new event
234     // loop iteration
OnNextIteration()235     virtual void OnNextIteration() { }
236 
237 
238     // the loop exit code
239     int m_exitcode;
240 
241 private:
242     // process all already pending events and dispatch a new one (blocking
243     // until it appears in the event queue if necessary)
244     //
245     // returns the return value of Dispatch()
246     bool ProcessEvents();
247 
248     wxDECLARE_NO_COPY_CLASS(wxEventLoopManual);
249 };
250 
251 #endif // platforms using "manual" loop
252 
253 // we're moving away from old m_impl wxEventLoop model as otherwise the user
254 // code doesn't have access to platform-specific wxEventLoop methods and this
255 // can sometimes be very useful (e.g. under MSW this is necessary for
256 // integration with MFC) but currently this is not done for all ports yet (e.g.
257 // wxX11) so fall back to the old wxGUIEventLoop definition below for them
258 
259 #if defined(__DARWIN__)
260     // CoreFoundation-based event loop is currently in wxBase so include it in
261     // any case too (although maybe it actually shouldn't be there at all)
262     #include "wx/osx/core/evtloop.h"
263 #endif
264 
265 // include the header defining wxConsoleEventLoop
266 #if defined(__UNIX__) && !defined(__WINDOWS__)
267     #include "wx/unix/evtloop.h"
268 #elif defined(__WINDOWS__)
269     #include "wx/msw/evtloopconsole.h"
270 #endif
271 
272 #if wxUSE_GUI
273 
274 // include the appropriate header defining wxGUIEventLoop
275 
276 #if defined(__WXMSW__)
277     #include "wx/msw/evtloop.h"
278 #elif defined(__WXCOCOA__)
279     #include "wx/cocoa/evtloop.h"
280 #elif defined(__WXOSX__)
281     #include "wx/osx/evtloop.h"
282 #elif defined(__WXDFB__)
283     #include "wx/dfb/evtloop.h"
284 #elif defined(__WXGTK20__)
285     #include "wx/gtk/evtloop.h"
286 #else // other platform
287 
288 #include "wx/stopwatch.h"   // for wxMilliClock_t
289 
290 class WXDLLIMPEXP_FWD_CORE wxEventLoopImpl;
291 
292 class WXDLLIMPEXP_CORE wxGUIEventLoop : public wxEventLoopBase
293 {
294 public:
wxGUIEventLoop()295     wxGUIEventLoop() { m_impl = NULL; }
296     virtual ~wxGUIEventLoop();
297 
298     virtual void ScheduleExit(int rc = 0);
299     virtual bool Pending() const;
300     virtual bool Dispatch();
DispatchTimeout(unsigned long timeout)301     virtual int DispatchTimeout(unsigned long timeout)
302     {
303         // TODO: this is, of course, horribly inefficient and a proper wait with
304         //       timeout should be implemented for all ports natively...
305         const wxMilliClock_t timeEnd = wxGetLocalTimeMillis() + timeout;
306         for ( ;; )
307         {
308             if ( Pending() )
309                 return Dispatch();
310 
311             if ( wxGetLocalTimeMillis() >= timeEnd )
312                 return -1;
313         }
314     }
WakeUp()315     virtual void WakeUp() { }
316     virtual bool YieldFor(long eventsToProcess);
317 
318 protected:
319     virtual int DoRun();
320 
321     // the pointer to the port specific implementation class
322     wxEventLoopImpl *m_impl;
323 
324     wxDECLARE_NO_COPY_CLASS(wxGUIEventLoop);
325 };
326 
327 #endif // platforms
328 
329 #endif // wxUSE_GUI
330 
331 #if wxUSE_GUI
332     // we use a class rather than a typedef because wxEventLoop is
333     // forward-declared in many places
334     class wxEventLoop : public wxGUIEventLoop { };
335 #else // !wxUSE_GUI
336     // we can't define wxEventLoop differently in GUI and base libraries so use
337     // a #define to still allow writing wxEventLoop in the user code
338     #if wxUSE_CONSOLE_EVENTLOOP && (defined(__WINDOWS__) || defined(__UNIX__))
339         #define wxEventLoop wxConsoleEventLoop
340     #else // we still must define it somehow for the code below...
341         #define wxEventLoop wxEventLoopBase
342     #endif
343 #endif
344 
IsRunning()345 inline bool wxEventLoopBase::IsRunning() const { return GetActive() == this; }
346 
347 #if wxUSE_GUI && !defined(__WXOSX__)
348 // ----------------------------------------------------------------------------
349 // wxModalEventLoop
350 // ----------------------------------------------------------------------------
351 
352 // this is a naive generic implementation which uses wxWindowDisabler to
353 // implement modality, we will surely need platform-specific implementations
354 // too, this generic implementation is here only temporarily to see how it
355 // works
356 class WXDLLIMPEXP_CORE wxModalEventLoop : public wxGUIEventLoop
357 {
358 public:
wxModalEventLoop(wxWindow * winModal)359     wxModalEventLoop(wxWindow *winModal)
360     {
361         m_windowDisabler = new wxWindowDisabler(winModal);
362     }
363 
364 protected:
OnExit()365     virtual void OnExit()
366     {
367         delete m_windowDisabler;
368         m_windowDisabler = NULL;
369 
370         wxGUIEventLoop::OnExit();
371     }
372 
373 private:
374     wxWindowDisabler *m_windowDisabler;
375 };
376 
377 #endif //wxUSE_GUI
378 
379 // ----------------------------------------------------------------------------
380 // wxEventLoopActivator: helper class for wxEventLoop implementations
381 // ----------------------------------------------------------------------------
382 
383 // this object sets the wxEventLoop given to the ctor as the currently active
384 // one and unsets it in its dtor, this is especially useful in presence of
385 // exceptions but is more tidy even when we don't use them
386 class wxEventLoopActivator
387 {
388 public:
wxEventLoopActivator(wxEventLoopBase * evtLoop)389     wxEventLoopActivator(wxEventLoopBase *evtLoop)
390     {
391         m_evtLoopOld = wxEventLoopBase::GetActive();
392         wxEventLoopBase::SetActive(evtLoop);
393     }
394 
~wxEventLoopActivator()395     ~wxEventLoopActivator()
396     {
397         // restore the previously active event loop
398         wxEventLoopBase::SetActive(m_evtLoopOld);
399     }
400 
401 private:
402     wxEventLoopBase *m_evtLoopOld;
403 };
404 
405 #if wxUSE_GUI || wxUSE_CONSOLE_EVENTLOOP
406 
407 class wxEventLoopGuarantor
408 {
409 public:
wxEventLoopGuarantor()410     wxEventLoopGuarantor()
411     {
412         m_evtLoopNew = NULL;
413         if (!wxEventLoop::GetActive())
414         {
415             m_evtLoopNew = new wxEventLoop;
416             wxEventLoop::SetActive(m_evtLoopNew);
417         }
418     }
419 
~wxEventLoopGuarantor()420     ~wxEventLoopGuarantor()
421     {
422         if (m_evtLoopNew)
423         {
424             wxEventLoop::SetActive(NULL);
425             delete m_evtLoopNew;
426         }
427     }
428 
429 private:
430     wxEventLoop *m_evtLoopNew;
431 };
432 
433 #endif // wxUSE_GUI || wxUSE_CONSOLE_EVENTLOOP
434 
435 #endif // _WX_EVTLOOP_H_
436