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