fix evtloop.h header dependency
[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:     wxWidgets licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13
14 #include "wx/evtloop.h"
15
16 #ifndef WX_PRECOMP
17     #include "wx/log.h"
18     #include "wx/app.h"
19 #endif //WX_PRECOMP
20
21 #import <AppKit/NSApplication.h>
22 #import <AppKit/NSEvent.h>
23 #import <Foundation/NSRunLoop.h>
24
25 // ========================================================================
26 // wxGUIEventLoop
27 // ========================================================================
28
29 // ----------------------------------------------------------------------------
30 // wxGUIEventLoop running and exiting
31 // ----------------------------------------------------------------------------
32
33 int wxGUIEventLoop::Run()
34 {
35     // event loops are not recursive, you need to create another loop!
36     wxCHECK_MSG( !IsRunning(), -1, _T("can't reenter a message loop") );
37
38     wxEventLoopActivator activate(this);
39
40     [[NSApplication sharedApplication] run];
41
42     OnExit();
43
44     return m_exitcode;
45 }
46
47 void wxGUIEventLoop::Exit(int rc)
48 {
49     wxCHECK_RET( IsRunning(), _T("can't call Exit() if not running") );
50
51     m_exitcode = rc;
52
53     NSApplication *cocoaApp = [NSApplication sharedApplication];
54     wxLogTrace(wxTRACE_COCOA,wxT("wxEventLoop::Exit isRunning=%d"), (int)[cocoaApp isRunning]);
55     wxTheApp->WakeUpIdle();
56     /* Notes:
57     If we're being called from idle time (which occurs while checking the
58     queue for new events) there may or may not be any events in the queue.
59     In order to successfully stop the event loop, at least one event must
60     be processed.  To ensure this always happens, WakeUpIdle is called.
61
62     If the application was active when closed then this is unnecessary
63     because it would receive a deactivate event anyway.  However, if the
64     application was not active when closed, then no events would be
65     added to the queue by Cocoa and thus the application would wait
66     indefinitely for the next event.
67     */
68     [cocoaApp stop: cocoaApp];
69 }
70
71 // ----------------------------------------------------------------------------
72 // wxEventLoop message processing dispatching
73 // ----------------------------------------------------------------------------
74
75 bool wxGUIEventLoop::Pending() const
76 {
77     // a pointer to the event is returned if there is one, or nil if not
78     return [[NSApplication sharedApplication]
79             nextEventMatchingMask: NSAnyEventMask
80             untilDate: nil /* Equivalent to [NSDate distantPast] */
81             inMode: NSDefaultRunLoopMode
82             dequeue: NO];
83 }
84
85 bool wxGUIEventLoop::Dispatch()
86 {
87     // This check is required by wxGTK but probably not really for wxCocoa
88     // Keep it here to encourage developers to write cross-platform code
89     wxCHECK_MSG( IsRunning(), false, _T("can't call Dispatch() if not running") );
90     NSApplication *cocoaApp = [NSApplication sharedApplication];
91     // Block to retrieve an event then send it
92     if(NSEvent *event = [cocoaApp
93                 nextEventMatchingMask:NSAnyEventMask
94                 untilDate:[NSDate distantFuture]
95                 inMode:NSDefaultRunLoopMode
96                 dequeue: YES])
97     {
98         [cocoaApp sendEvent: event];
99     }
100
101     return true;
102 }
103
104 int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
105 {
106     NSApplication *cocoaApp = [NSApplication sharedApplication];
107     NSEvent *event = [cocoaApp
108                 nextEventMatchingMask:NSAnyEventMask
109                 untilDate:[[NSDate alloc] initWithTimeIntervalSinceNow:timeout/1000]
110                 inMode:NSDefaultRunLoopMode
111                 dequeue: YES];
112     if ( !event )
113         return -1;
114
115     [cocoaApp sendEvent: event];
116
117     return true;
118 }
119
120 bool wxGUIEventLoop::YieldFor(long eventsToProcess)
121 {
122 #if wxUSE_LOG
123     // disable log flushing from here because a call to wxYield() shouldn't
124     // normally result in message boxes popping up &c
125     wxLog::Suspend();
126 #endif // wxUSE_LOG
127
128     m_isInsideYield = true;
129     m_eventsToProcessInsideYield = eventsToProcess;
130
131     // Run the event loop until it is out of events
132     while (1)
133     {
134         // TODO: implement event filtering using the eventsToProcess mask
135
136         wxAutoNSAutoreleasePool pool;
137         /*  NOTE: It may be better to use something like
138             NSEventTrackingRunLoopMode since we don't necessarily want all
139             timers/sources/observers to run, only those which would
140             run while tracking events.  However, it should be noted that
141             NSEventTrackingRunLoopMode is in the common set of modes
142             so it may not effectively make much of a difference.
143          */
144         NSEvent *event = [GetNSApplication()
145                 nextEventMatchingMask:NSAnyEventMask
146                 untilDate:[NSDate distantPast]
147                 inMode:NSDefaultRunLoopMode
148                 dequeue: YES];
149         if(!event)
150             break;
151         [GetNSApplication() sendEvent: event];
152     }
153
154     /*
155         Because we just told NSApplication to avoid blocking it will in turn
156         run the CFRunLoop with a timeout of 0 seconds.  In that case, our
157         run loop observer on kCFRunLoopBeforeWaiting never fires because
158         no waiting occurs.  Therefore, no idle events are sent.
159
160         Believe it or not, this is actually desirable because we do not want
161         to process idle from here.  However, we do want to process pending
162         events because some user code expects to do work in a thread while
163         the main thread waits and then notify the main thread by posting
164         an event.
165      */
166     ProcessPendingEvents();
167
168 #if wxUSE_LOG
169     // let the logs be flashed again
170     wxLog::Resume();
171 #endif // wxUSE_LOG
172
173     m_isInsideYield = false;
174
175     return true;
176 }