]> git.saurik.com Git - wxWidgets.git/blame - src/osx/cocoa/evtloop.mm
notify the event loop that synthesized events are on the queue, wait for them to...
[wxWidgets.git] / src / osx / cocoa / evtloop.mm
CommitLineData
b503b036
SC
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
a9a4f229 7// RCS-ID: $Id$
b503b036
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
1e04d2bf 27#include "wx/evtloop.h"
b503b036
SC
28
29#ifndef WX_PRECOMP
30 #include "wx/app.h"
bf06fbce 31 #include "wx/nonownedwnd.h"
b503b036
SC
32#endif // WX_PRECOMP
33
f965a844
RR
34#include "wx/log.h"
35
b503b036
SC
36#include "wx/osx/private.h"
37
38// ============================================================================
39// wxEventLoop implementation
40// ============================================================================
41
e9e8b381
SC
42#if 0
43
44// in case we want to integrate this
11fed901 45
902ddbfd
SC
46static NSUInteger CalculateNSEventMaskFromEventCategory(wxEventCategory cat)
47{
48 // the masking system doesn't really help, as only the lowlevel UI events
49 // are split in a useful way, all others are way to broad
50
51 if ( (cat | wxEVT_CATEGORY_USER_INPUT) && (cat | (~wxEVT_CATEGORY_USER_INPUT) ) )
52 return NSAnyEventMask;
53
54 NSUInteger mask = 0;
11fed901 55
902ddbfd
SC
56 if ( cat | wxEVT_CATEGORY_USER_INPUT )
57 {
58 mask |=
59 NSLeftMouseDownMask |
60 NSLeftMouseUpMask |
61 NSRightMouseDownMask |
62 NSRightMouseUpMask |
63 NSMouseMovedMask |
64 NSLeftMouseDraggedMask |
65 NSRightMouseDraggedMask |
66 NSMouseEnteredMask |
67 NSMouseExitedMask |
68 NSScrollWheelMask |
69#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
70 NSTabletPointMask |
71 NSTabletProximityMask |
72#endif
73 NSOtherMouseDownMask |
74 NSOtherMouseUpMask |
75 NSOtherMouseDraggedMask |
76
77 NSKeyDownMask |
78 NSKeyUpMask |
79 NSFlagsChangedMask |
80#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
81 NSEventMaskGesture |
82 NSEventMaskMagnify |
83 NSEventMaskSwipe |
84 NSEventMaskRotate |
85 NSEventMaskBeginGesture |
86 NSEventMaskEndGesture |
87#endif
88 0;
89 }
90
91 if ( cat | (~wxEVT_CATEGORY_USER_INPUT) )
92 {
93 mask |=
94 NSAppKitDefinedMask |
95 NSSystemDefinedMask |
96 NSApplicationDefinedMask |
97 NSPeriodicMask |
98 NSCursorUpdateMask;
99 }
100
101 return mask;
102}
11fed901 103
e9e8b381 104#endif
11fed901 105
902ddbfd
SC
106wxGUIEventLoop::wxGUIEventLoop()
107{
108 m_modalSession = nil;
109 m_dummyWindow = nil;
0aff141c
SC
110 m_modalNestedLevel = 0;
111 m_modalWindow = NULL;
4b0a48db 112 m_osxLowLevelWakeUp = false;
11fed901 113}
11fed901 114
902ddbfd 115wxGUIEventLoop::~wxGUIEventLoop()
b503b036 116{
902ddbfd
SC
117 wxASSERT( m_modalSession == nil );
118 wxASSERT( m_dummyWindow == nil );
0aff141c 119 wxASSERT( m_modalNestedLevel == 0 );
6b8ef0b3
VZ
120}
121
122//-----------------------------------------------------------------------------
123// events dispatch and loop handling
124//-----------------------------------------------------------------------------
125
0056673c
SC
126#if 0
127
b503b036
SC
128bool wxGUIEventLoop::Pending() const
129{
a765eef3
SC
130#if 0
131 // this code doesn't reliably detect pending events
132 // so better return true and have the dispatch deal with it
133 // as otherwise we end up in a tight loop when idle events are responded
134 // to by RequestMore(true)
dbeddfb9 135 wxMacAutoreleasePool autoreleasepool;
a765eef3 136
b503b036
SC
137 return [[NSApplication sharedApplication]
138 nextEventMatchingMask: NSAnyEventMask
139 untilDate: nil
140 inMode: NSDefaultRunLoopMode
a765eef3
SC
141 dequeue: NO] != nil;
142#else
143 return true;
144#endif
b503b036
SC
145}
146
50d4763f 147
b503b036
SC
148bool wxGUIEventLoop::Dispatch()
149{
150 if ( !wxTheApp )
151 return false;
152
153 wxMacAutoreleasePool autoreleasepool;
154
155 if(NSEvent *event = [NSApp
156 nextEventMatchingMask:NSAnyEventMask
157 untilDate:[NSDate dateWithTimeIntervalSinceNow: m_sleepTime]
158 inMode:NSDefaultRunLoopMode
159 dequeue: YES])
160 {
0b6f851f
SC
161 WXEVENTREF formerEvent = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEvent();
162 WXEVENTHANDLERCALLREF formerHandler = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEventHandlerCallRef();
163
7dab9892
KO
164 if (wxTheApp)
165 wxTheApp->MacSetCurrentEvent(event, NULL);
b503b036
SC
166 m_sleepTime = 0.0;
167 [NSApp sendEvent: event];
0b6f851f
SC
168
169 if (wxTheApp)
170 wxTheApp->MacSetCurrentEvent(formerEvent , formerHandler);
b503b036
SC
171 }
172 else
173 {
b0a9bfc8
SC
174 if (wxTheApp)
175 wxTheApp->ProcessPendingEvents();
176
b503b036
SC
177 if ( wxTheApp->ProcessIdle() )
178 m_sleepTime = 0.0 ;
179 else
180 {
181 m_sleepTime = 1.0;
182#if wxUSE_THREADS
183 wxMutexGuiLeave();
184 wxMilliSleep(20);
185 wxMutexGuiEnter();
186#endif
187 }
188 }
189
190 return true;
191}
91407318 192
0056673c 193#endif
f965a844 194
0056673c 195int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
91407318
VZ
196{
197 wxMacAutoreleasePool autoreleasepool;
198
902ddbfd
SC
199 if ( m_modalSession )
200 {
201 NSInteger response = [NSApp runModalSession:(NSModalSession)m_modalSession];
202
203 switch (response)
204 {
205 case NSRunContinuesResponse:
206 {
207 if ( [[NSApplication sharedApplication]
208 nextEventMatchingMask: NSAnyEventMask
209 untilDate: nil
210 inMode: NSDefaultRunLoopMode
211 dequeue: NO] != nil )
212 return 1;
213
214 return -1;
215 }
216
217 case NSRunStoppedResponse:
218 case NSRunAbortedResponse:
219 return -1;
220 default:
221 wxFAIL_MSG("unknown response code");
902ddbfd
SC
222 break;
223 }
a624c97f 224 return -1;
902ddbfd
SC
225 }
226 else
227 {
228 NSEvent *event = [NSApp
229 nextEventMatchingMask:NSAnyEventMask
230 untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
231 inMode:NSDefaultRunLoopMode
232 dequeue: YES];
233
234 if ( event == nil )
235 return -1;
91407318 236
902ddbfd 237 [NSApp sendEvent: event];
91407318 238
902ddbfd
SC
239 return 1;
240 }
91407318 241}
80eee837
SC
242
243void wxGUIEventLoop::DoRun()
244{
245 wxMacAutoreleasePool autoreleasepool;
246 [NSApp run];
247}
248
249void wxGUIEventLoop::DoStop()
250{
2e5f9929
SC
251 // only calling stop: is not enough when called from a runloop-observer,
252 // therefore add a dummy event, to make sure the runloop gets another round
85a74f93
SC
253 [NSApp stop:0];
254 WakeUp();
255}
256
257void wxGUIEventLoop::WakeUp()
258{
843ac6c8 259 // NSEvent* cevent = [NSApp currentEvent];
4b0a48db 260 // NSString* mode = [[NSRunLoop mainRunLoop] currentMode];
50d4763f
SC
261
262 // when already in a mouse event handler, don't add higher level event
843ac6c8 263 // if ( cevent != nil && [cevent type] <= NSMouseMoved && )
4b0a48db 264 if ( m_osxLowLevelWakeUp /* [NSEventTrackingRunLoopMode isEqualToString:mode] */ )
50d4763f 265 {
843ac6c8 266 // NSLog(@"event for wakeup %@ in mode %@",cevent,mode);
50d4763f
SC
267 wxCFEventLoop::WakeUp();
268 }
269 else
270 {
271 wxMacAutoreleasePool autoreleasepool;
272 NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
2e5f9929
SC
273 location:NSMakePoint(0.0, 0.0)
274 modifierFlags:0
275 timestamp:0
276 windowNumber:0
277 context:nil
278 subtype:0 data1:0 data2:0];
50d4763f
SC
279 [NSApp postEvent:event atStart:FALSE];
280 }
80eee837
SC
281}
282
7934e447
SC
283CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
284{
285 NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
286 return [nsloop getCFRunLoop];
287}
288
289
cfb0ef70
SC
290// TODO move into a evtloop_osx.cpp
291
292wxModalEventLoop::wxModalEventLoop(wxWindow *modalWindow)
2439f1d9 293{
cfb0ef70 294 m_modalWindow = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
2439f1d9 295 wxASSERT_MSG( m_modalWindow != NULL, "must pass in a toplevel window for modal event loop" );
cfb0ef70 296 m_modalNativeWindow = m_modalWindow->GetWXWindow();
2439f1d9
SC
297}
298
cfb0ef70
SC
299wxModalEventLoop::wxModalEventLoop(WXWindow modalNativeWindow)
300{
301 m_modalWindow = NULL;
302 wxASSERT_MSG( modalNativeWindow != NULL, "must pass in a toplevel window for modal event loop" );
303 m_modalNativeWindow = modalNativeWindow;
304}
305
306// END move into a evtloop_osx.cpp
307
80eee837
SC
308void wxModalEventLoop::DoRun()
309{
310 wxMacAutoreleasePool pool;
311
312 // If the app hasn't started, flush the event queue
313 // If we don't do this, the Dock doesn't get the message that
314 // the app has started so will refuse to activate it.
315 [NSApplication sharedApplication];
316 if (![NSApp isRunning])
317 {
318 while(NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES])
319 {
320 [NSApp sendEvent:event];
321 }
322 }
323
cfb0ef70 324 [NSApp runModalForWindow:m_modalNativeWindow];
80eee837
SC
325}
326
327void wxModalEventLoop::DoStop()
328{
f834239f 329 [NSApp abortModal];
80eee837
SC
330}
331
902ddbfd
SC
332void wxGUIEventLoop::BeginModalSession( wxWindow* modalWindow )
333{
334 WXWindow nsnow = nil;
0aff141c
SC
335
336 if ( m_modalNestedLevel > 0 )
337 {
338 wxASSERT_MSG( m_modalWindow == modalWindow, "Nested Modal Sessions must be based on same window");
339 m_modalNestedLevel++;
340 return;
341 }
342
343 m_modalWindow = modalWindow;
344 m_modalNestedLevel = 1;
902ddbfd
SC
345
346 if ( modalWindow )
347 {
b64af07b
SC
348 // we must show now, otherwise beginModalSessionForWindow does it but it
349 // also would do a centering of the window before overriding all our position
350 if ( !modalWindow->IsShownOnScreen() )
351 modalWindow->Show();
352
902ddbfd
SC
353 wxNonOwnedWindow* now = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
354 wxASSERT_MSG( now != NULL, "must pass in a toplevel window for modal event loop" );
355 nsnow = now ? now->GetWXWindow() : nil;
356 }
357 else
358 {
359 NSRect r = NSMakeRect(10, 10, 0, 0);
360 nsnow = [NSPanel alloc];
361 [nsnow initWithContentRect:r
362 styleMask:NSBorderlessWindowMask
363 backing:NSBackingStoreBuffered
364 defer:YES
365 ];
366 [nsnow orderOut:nil];
367 m_dummyWindow = nsnow;
368 }
369 m_modalSession = [NSApp beginModalSessionForWindow:nsnow];
203ec424 370 wxASSERT_MSG(m_modalSession != NULL, "modal session couldn't be started");
902ddbfd
SC
371}
372
373void wxGUIEventLoop::EndModalSession()
374{
375 wxASSERT_MSG(m_modalSession != NULL, "no modal session active");
0aff141c
SC
376
377 wxASSERT_MSG(m_modalNestedLevel > 0, "incorrect modal nesting level");
378
379 if ( --m_modalNestedLevel == 0 )
902ddbfd 380 {
0aff141c
SC
381 [NSApp endModalSession:(NSModalSession)m_modalSession];
382 m_modalSession = nil;
383 if ( m_dummyWindow )
384 {
385 [m_dummyWindow release];
386 m_dummyWindow = nil;
387 }
902ddbfd
SC
388 }
389}
390
391//
392//
393//
394
395wxWindowDisabler::wxWindowDisabler(bool disable)
396{
397 m_modalEventLoop = NULL;
398 m_disabled = disable;
399 if ( disable )
400 DoDisable();
401}
402
403wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
404{
405 m_disabled = true;
406 DoDisable(winToSkip);
407}
408
409void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
410{
411 // remember the top level windows which were already disabled, so that we
412 // don't reenable them later
413 m_winDisabled = NULL;
414
415 wxWindowList::compatibility_iterator node;
416 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
417 {
418 wxWindow *winTop = node->GetData();
419 if ( winTop == winToSkip )
420 continue;
421
422 // we don't need to disable the hidden or already disabled windows
423 if ( winTop->IsEnabled() && winTop->IsShown() )
424 {
425 winTop->Disable();
426 }
427 else
428 {
429 if ( !m_winDisabled )
430 {
431 m_winDisabled = new wxWindowList;
432 }
433
434 m_winDisabled->Append(winTop);
435 }
436 }
437
438 m_modalEventLoop = (wxEventLoop*)wxEventLoopBase::GetActive();
85fb0a0a
RD
439 if (m_modalEventLoop)
440 m_modalEventLoop->BeginModalSession(winToSkip);
902ddbfd
SC
441}
442
443wxWindowDisabler::~wxWindowDisabler()
444{
445 if ( !m_disabled )
446 return;
447
85fb0a0a
RD
448 if (m_modalEventLoop)
449 m_modalEventLoop->EndModalSession();
902ddbfd
SC
450
451 wxWindowList::compatibility_iterator node;
452 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
453 {
454 wxWindow *winTop = node->GetData();
455 if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
456 {
457 winTop->Enable();
458 }
459 //else: had been already disabled, don't reenable
460 }
461
462 delete m_winDisabled;
1788a5f9 463}