]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/evtloop.mm
e558a54e255f58bd6e49db7b1ad3c1c952068429
[wxWidgets.git] / src / osx / cocoa / evtloop.mm
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/cocoa/evtloop.mm
3 // Purpose: implementation of wxEventLoop for OS X
4 // Author: Vadim Zeitlin, Stefan Csomor
5 // Modified by:
6 // Created: 2006-01-12
7 // RCS-ID: $Id: evtloop.cpp 54845 2008-07-30 14:52:41Z SC $
8 // Copyright: (c) 2006 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/evtloop.h"
28
29 #ifndef WX_PRECOMP
30 #include "wx/app.h"
31 #include "wx/nonownedwnd.h"
32 #endif // WX_PRECOMP
33
34 #include "wx/log.h"
35
36 #include "wx/osx/private.h"
37
38 // ============================================================================
39 // wxEventLoop implementation
40 // ============================================================================
41
42
43 static NSUInteger CalculateNSEventMaskFromEventCategory(wxEventCategory cat)
44 {
45 // the masking system doesn't really help, as only the lowlevel UI events
46 // are split in a useful way, all others are way to broad
47
48 if ( (cat | wxEVT_CATEGORY_USER_INPUT) && (cat | (~wxEVT_CATEGORY_USER_INPUT) ) )
49 return NSAnyEventMask;
50
51 NSUInteger mask = 0;
52
53 if ( cat | wxEVT_CATEGORY_USER_INPUT )
54 {
55 mask |=
56 NSLeftMouseDownMask |
57 NSLeftMouseUpMask |
58 NSRightMouseDownMask |
59 NSRightMouseUpMask |
60 NSMouseMovedMask |
61 NSLeftMouseDraggedMask |
62 NSRightMouseDraggedMask |
63 NSMouseEnteredMask |
64 NSMouseExitedMask |
65 NSScrollWheelMask |
66 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
67 NSTabletPointMask |
68 NSTabletProximityMask |
69 #endif
70 NSOtherMouseDownMask |
71 NSOtherMouseUpMask |
72 NSOtherMouseDraggedMask |
73
74 NSKeyDownMask |
75 NSKeyUpMask |
76 NSFlagsChangedMask |
77 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
78 NSEventMaskGesture |
79 NSEventMaskMagnify |
80 NSEventMaskSwipe |
81 NSEventMaskRotate |
82 NSEventMaskBeginGesture |
83 NSEventMaskEndGesture |
84 #endif
85 0;
86 }
87
88 if ( cat | (~wxEVT_CATEGORY_USER_INPUT) )
89 {
90 mask |=
91 NSAppKitDefinedMask |
92 NSSystemDefinedMask |
93 NSApplicationDefinedMask |
94 NSPeriodicMask |
95 NSCursorUpdateMask;
96 }
97
98 return mask;
99 }
100
101
102 wxGUIEventLoop::wxGUIEventLoop()
103 {
104 m_modalSession = nil;
105 m_dummyWindow = nil;
106 }
107
108 wxGUIEventLoop::~wxGUIEventLoop()
109 {
110 wxASSERT( m_modalSession == nil );
111 wxASSERT( m_dummyWindow == nil );
112 }
113
114 //-----------------------------------------------------------------------------
115 // events dispatch and loop handling
116 //-----------------------------------------------------------------------------
117
118 #if 0
119
120 bool wxGUIEventLoop::Pending() const
121 {
122 #if 0
123 // this code doesn't reliably detect pending events
124 // so better return true and have the dispatch deal with it
125 // as otherwise we end up in a tight loop when idle events are responded
126 // to by RequestMore(true)
127 wxMacAutoreleasePool autoreleasepool;
128
129 return [[NSApplication sharedApplication]
130 nextEventMatchingMask: NSAnyEventMask
131 untilDate: nil
132 inMode: NSDefaultRunLoopMode
133 dequeue: NO] != nil;
134 #else
135 return true;
136 #endif
137 }
138
139 bool wxGUIEventLoop::Dispatch()
140 {
141 if ( !wxTheApp )
142 return false;
143
144 wxMacAutoreleasePool autoreleasepool;
145
146 if(NSEvent *event = [NSApp
147 nextEventMatchingMask:NSAnyEventMask
148 untilDate:[NSDate dateWithTimeIntervalSinceNow: m_sleepTime]
149 inMode:NSDefaultRunLoopMode
150 dequeue: YES])
151 {
152 WXEVENTREF formerEvent = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEvent();
153 WXEVENTHANDLERCALLREF formerHandler = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEventHandlerCallRef();
154
155 if (wxTheApp)
156 wxTheApp->MacSetCurrentEvent(event, NULL);
157 m_sleepTime = 0.0;
158 [NSApp sendEvent: event];
159
160 if (wxTheApp)
161 wxTheApp->MacSetCurrentEvent(formerEvent , formerHandler);
162 }
163 else
164 {
165 if (wxTheApp)
166 wxTheApp->ProcessPendingEvents();
167
168 if ( wxTheApp->ProcessIdle() )
169 m_sleepTime = 0.0 ;
170 else
171 {
172 m_sleepTime = 1.0;
173 #if wxUSE_THREADS
174 wxMutexGuiLeave();
175 wxMilliSleep(20);
176 wxMutexGuiEnter();
177 #endif
178 }
179 }
180
181 return true;
182 }
183
184 #endif
185
186 int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
187 {
188 wxMacAutoreleasePool autoreleasepool;
189
190 if ( m_modalSession )
191 {
192 NSInteger response = [NSApp runModalSession:(NSModalSession)m_modalSession];
193
194 switch (response)
195 {
196 case NSRunContinuesResponse:
197 {
198 if ( [[NSApplication sharedApplication]
199 nextEventMatchingMask: NSAnyEventMask
200 untilDate: nil
201 inMode: NSDefaultRunLoopMode
202 dequeue: NO] != nil )
203 return 1;
204
205 return -1;
206 }
207
208 case NSRunStoppedResponse:
209 case NSRunAbortedResponse:
210 return -1;
211 default:
212 wxFAIL_MSG("unknown response code");
213 return -1;
214 break;
215 }
216 }
217 else
218 {
219 NSEvent *event = [NSApp
220 nextEventMatchingMask:NSAnyEventMask
221 untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
222 inMode:NSDefaultRunLoopMode
223 dequeue: YES];
224
225 if ( event == nil )
226 return -1;
227
228 [NSApp sendEvent: event];
229
230 return 1;
231 }
232 }
233
234 void wxGUIEventLoop::DoRun()
235 {
236 wxMacAutoreleasePool autoreleasepool;
237 [NSApp run];
238 }
239
240 void wxGUIEventLoop::DoStop()
241 {
242 [NSApp stop:0];
243 // only calling stop: is not enough when called from a runloop-observer,
244 // therefore add a dummy event, to make sure the runloop gets another round
245 NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
246 location:NSMakePoint(0.0, 0.0)
247 modifierFlags:0
248 timestamp:0
249 windowNumber:0
250 context:nil
251 subtype:0 data1:0 data2:0];
252 [NSApp postEvent:event atStart:FALSE];
253 }
254
255 CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
256 {
257 NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
258 return [nsloop getCFRunLoop];
259 }
260
261
262 // TODO move into a evtloop_osx.cpp
263
264 wxModalEventLoop::wxModalEventLoop(wxWindow *modalWindow)
265 {
266 m_modalWindow = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
267 wxASSERT_MSG( m_modalWindow != NULL, "must pass in a toplevel window for modal event loop" );
268 m_modalNativeWindow = m_modalWindow->GetWXWindow();
269 }
270
271 wxModalEventLoop::wxModalEventLoop(WXWindow modalNativeWindow)
272 {
273 m_modalWindow = NULL;
274 wxASSERT_MSG( modalNativeWindow != NULL, "must pass in a toplevel window for modal event loop" );
275 m_modalNativeWindow = modalNativeWindow;
276 }
277
278 // END move into a evtloop_osx.cpp
279
280 void wxModalEventLoop::DoRun()
281 {
282 wxMacAutoreleasePool pool;
283
284 // If the app hasn't started, flush the event queue
285 // If we don't do this, the Dock doesn't get the message that
286 // the app has started so will refuse to activate it.
287 [NSApplication sharedApplication];
288 if (![NSApp isRunning])
289 {
290 while(NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES])
291 {
292 [NSApp sendEvent:event];
293 }
294 }
295
296 [NSApp runModalForWindow:m_modalNativeWindow];
297 }
298
299 void wxModalEventLoop::DoStop()
300 {
301 [NSApp stopModal];
302 }
303
304 void wxGUIEventLoop::BeginModalSession( wxWindow* modalWindow )
305 {
306 WXWindow nsnow = nil;
307
308 if ( modalWindow )
309 {
310 wxNonOwnedWindow* now = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
311 wxASSERT_MSG( now != NULL, "must pass in a toplevel window for modal event loop" );
312 nsnow = now ? now->GetWXWindow() : nil;
313 }
314 else
315 {
316 NSRect r = NSMakeRect(10, 10, 0, 0);
317 nsnow = [NSPanel alloc];
318 [nsnow initWithContentRect:r
319 styleMask:NSBorderlessWindowMask
320 backing:NSBackingStoreBuffered
321 defer:YES
322 ];
323 [nsnow orderOut:nil];
324 m_dummyWindow = nsnow;
325 }
326 m_modalSession = [NSApp beginModalSessionForWindow:nsnow];
327 }
328
329 void wxGUIEventLoop::EndModalSession()
330 {
331 wxASSERT_MSG(m_modalSession != NULL, "no modal session active");
332 [NSApp endModalSession:(NSModalSession)m_modalSession];
333 m_modalSession = nil;
334 if ( m_dummyWindow )
335 {
336 [m_dummyWindow release];
337 m_dummyWindow = nil;
338 }
339 }
340
341 //
342 //
343 //
344
345 wxWindowDisabler::wxWindowDisabler(bool disable)
346 {
347 m_modalEventLoop = NULL;
348 m_disabled = disable;
349 if ( disable )
350 DoDisable();
351 }
352
353 wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
354 {
355 m_disabled = true;
356 DoDisable(winToSkip);
357 }
358
359 void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
360 {
361 // remember the top level windows which were already disabled, so that we
362 // don't reenable them later
363 m_winDisabled = NULL;
364
365 wxWindowList::compatibility_iterator node;
366 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
367 {
368 wxWindow *winTop = node->GetData();
369 if ( winTop == winToSkip )
370 continue;
371
372 // we don't need to disable the hidden or already disabled windows
373 if ( winTop->IsEnabled() && winTop->IsShown() )
374 {
375 winTop->Disable();
376 }
377 else
378 {
379 if ( !m_winDisabled )
380 {
381 m_winDisabled = new wxWindowList;
382 }
383
384 m_winDisabled->Append(winTop);
385 }
386 }
387
388 m_modalEventLoop = (wxEventLoop*)wxEventLoopBase::GetActive();
389 m_modalEventLoop->BeginModalSession(winToSkip);
390 }
391
392 wxWindowDisabler::~wxWindowDisabler()
393 {
394 if ( !m_disabled )
395 return;
396
397 m_modalEventLoop->EndModalSession();
398
399 wxWindowList::compatibility_iterator node;
400 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
401 {
402 wxWindow *winTop = node->GetData();
403 if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
404 {
405 winTop->Enable();
406 }
407 //else: had been already disabled, don't reenable
408 }
409
410 delete m_winDisabled;
411 }
412