Improved idle event processing.
[wxWidgets.git] / src / cocoa / evtloop.mm
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        cocoa/evtloop.mm
3 // Purpose:     implements wxEventLoop for Cocoa
4 // Author:      David Elliott
5 // Modified by:
6 // Created:     2003/10/02
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2003 David Elliott <dfe@cox.net>
9 // License:     wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13 #ifndef WX_PRECOMP
14     #include "wx/log.h"
15     #include "wx/app.h"
16 #endif //WX_PRECOMP
17
18 #include "wx/evtloop.h"
19
20 #import <AppKit/NSApplication.h>
21 #import <AppKit/NSEvent.h>
22 #import <Foundation/NSRunLoop.h>
23
24 // ========================================================================
25 // wxEventLoopImpl
26 // ========================================================================
27
28 class WXDLLEXPORT wxEventLoopImpl
29 {
30 public:
31     // ctor
32     wxEventLoopImpl() { SetExitCode(0); }
33
34     // set/get the exit code
35     void SetExitCode(int exitcode) { m_exitcode = exitcode; }
36     int GetExitCode() const { return m_exitcode; }
37
38 private:
39     // the exit code of the event loop
40     int m_exitcode;
41 };
42
43 // ========================================================================
44 // wxEventLoop
45 // ========================================================================
46
47 // ----------------------------------------------------------------------------
48 // wxEventLoop running and exiting
49 // ----------------------------------------------------------------------------
50
51 wxEventLoop *wxEventLoop::ms_activeLoop = NULL;
52
53 wxEventLoop::~wxEventLoop()
54 {
55     wxASSERT_MSG( !m_impl, _T("should have been deleted in Run()") );
56 }
57
58 bool wxEventLoop::IsRunning() const
59 {
60     return m_impl;
61 }
62
63 int wxEventLoop::Run()
64 {
65     // event loops are not recursive, you need to create another loop!
66     wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
67
68     wxEventLoop *oldLoop = ms_activeLoop;
69     ms_activeLoop = this;
70
71     m_impl = new wxEventLoopImpl;
72
73     [[NSApplication sharedApplication] run];
74
75     int exitcode = m_impl->GetExitCode();
76     delete m_impl;
77     m_impl = NULL;
78
79     ms_activeLoop = oldLoop;
80
81     return exitcode;
82 }
83
84 void wxEventLoop::Exit(int rc)
85 {
86     wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
87
88     m_impl->SetExitCode(rc);
89
90     NSApplication *cocoaApp = [NSApplication sharedApplication];
91     wxLogDebug("wxEventLoop::Exit isRunning=%d", (int)[cocoaApp isRunning]);
92     wxTheApp->WakeUpIdle();
93     /* Notes:
94     If we're being called from idle time (which occurs while checking the
95     queue for new events) there may or may not be any events in the queue.
96     In order to successfully stop the event loop, at least one event must
97     be processed.  To ensure this always happens, WakeUpIdle is called.
98
99     If the application was active when closed then this is unnecessary
100     because it would receive a deactivate event anyway.  However, if the
101     application was not active when closed, then no events would be
102     added to the queue by Cocoa and thus the application would wait
103     indefinitely for the next event.
104     */
105     [cocoaApp stop: cocoaApp];
106 }
107
108 // ----------------------------------------------------------------------------
109 // wxEventLoop message processing dispatching
110 // ----------------------------------------------------------------------------
111
112 bool wxEventLoop::Pending() const
113 {
114     // a pointer to the event is returned if there is one, or nil if not
115     return [[NSApplication sharedApplication]
116             nextEventMatchingMask: NSAnyEventMask
117             untilDate: nil /* Equivalent to [NSDate distantPast] */
118             inMode: NSDefaultRunLoopMode
119             dequeue: NO];
120 }
121
122 bool wxEventLoop::Dispatch()
123 {
124     // This check is required by wxGTK but probably not really for wxCocoa
125     // Keep it here to encourage developers to write cross-platform code
126     wxCHECK_MSG( IsRunning(), false, _T("can't call Dispatch() if not running") );
127     NSApplication *cocoaApp = [NSApplication sharedApplication];
128     // Block to retrieve an event then send it
129     if(NSEvent *event = [cocoaApp
130                 nextEventMatchingMask:NSAnyEventMask
131                 untilDate:[NSDate distantFuture]
132                 inMode:NSDefaultRunLoopMode
133                 dequeue: YES])
134     {
135         [cocoaApp sendEvent: event];
136         return true;
137     }
138     return false;
139 }
140