Coverage Report

Created: 2026-09-20 06:25

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