1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/cocoa/evtloop.mm
3 // Purpose: implementation of wxEventLoop for OS X
4 // Author: Vadim Zeitlin, Stefan Csomor
8 // Copyright: (c) 2006 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // for compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
27 #include "wx/evtloop.h"
31 #include "wx/nonownedwnd.h"
36 #include "wx/osx/private.h"
38 // ============================================================================
39 // wxEventLoop implementation
40 // ============================================================================
44 // in case we want to integrate this
46 static NSUInteger CalculateNSEventMaskFromEventCategory(wxEventCategory cat)
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
51 if ( (cat | wxEVT_CATEGORY_USER_INPUT) && (cat | (~wxEVT_CATEGORY_USER_INPUT) ) )
52 return NSAnyEventMask;
56 if ( cat | wxEVT_CATEGORY_USER_INPUT )
61 NSRightMouseDownMask |
64 NSLeftMouseDraggedMask |
65 NSRightMouseDraggedMask |
69 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
71 NSTabletProximityMask |
73 NSOtherMouseDownMask |
75 NSOtherMouseDraggedMask |
80 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
85 NSEventMaskBeginGesture |
86 NSEventMaskEndGesture |
91 if ( cat | (~wxEVT_CATEGORY_USER_INPUT) )
96 NSApplicationDefinedMask |
106 wxGUIEventLoop::wxGUIEventLoop()
108 m_modalSession = nil;
110 m_modalNestedLevel = 0;
111 m_modalWindow = NULL;
112 m_osxLowLevelWakeUp = false;
115 wxGUIEventLoop::~wxGUIEventLoop()
117 wxASSERT( m_modalSession == nil );
118 wxASSERT( m_dummyWindow == nil );
119 wxASSERT( m_modalNestedLevel == 0 );
122 //-----------------------------------------------------------------------------
123 // events dispatch and loop handling
124 //-----------------------------------------------------------------------------
128 bool wxGUIEventLoop::Pending() const
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)
135 wxMacAutoreleasePool autoreleasepool;
137 return [[NSApplication sharedApplication]
138 nextEventMatchingMask: NSAnyEventMask
140 inMode: NSDefaultRunLoopMode
148 bool wxGUIEventLoop::Dispatch()
153 wxMacAutoreleasePool autoreleasepool;
155 if(NSEvent *event = [NSApp
156 nextEventMatchingMask:NSAnyEventMask
157 untilDate:[NSDate dateWithTimeIntervalSinceNow: m_sleepTime]
158 inMode:NSDefaultRunLoopMode
161 WXEVENTREF formerEvent = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEvent();
162 WXEVENTHANDLERCALLREF formerHandler = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEventHandlerCallRef();
165 wxTheApp->MacSetCurrentEvent(event, NULL);
167 [NSApp sendEvent: event];
170 wxTheApp->MacSetCurrentEvent(formerEvent , formerHandler);
175 wxTheApp->ProcessPendingEvents();
177 if ( wxTheApp->ProcessIdle() )
195 int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
197 wxMacAutoreleasePool autoreleasepool;
199 if ( m_modalSession )
201 NSInteger response = [NSApp runModalSession:(NSModalSession)m_modalSession];
205 case NSRunContinuesResponse:
207 if ( [[NSApplication sharedApplication]
208 nextEventMatchingMask: NSAnyEventMask
210 inMode: NSDefaultRunLoopMode
211 dequeue: NO] != nil )
217 case NSRunStoppedResponse:
218 case NSRunAbortedResponse:
221 wxFAIL_MSG("unknown response code");
228 NSEvent *event = [NSApp
229 nextEventMatchingMask:NSAnyEventMask
230 untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
231 inMode:NSDefaultRunLoopMode
237 [NSApp sendEvent: event];
243 void wxGUIEventLoop::DoRun()
245 wxMacAutoreleasePool autoreleasepool;
249 void wxGUIEventLoop::DoStop()
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
257 void wxGUIEventLoop::WakeUp()
259 // NSEvent* cevent = [NSApp currentEvent];
260 // NSString* mode = [[NSRunLoop mainRunLoop] currentMode];
262 // when already in a mouse event handler, don't add higher level event
263 // if ( cevent != nil && [cevent type] <= NSMouseMoved && )
264 if ( m_osxLowLevelWakeUp /* [NSEventTrackingRunLoopMode isEqualToString:mode] */ )
266 // NSLog(@"event for wakeup %@ in mode %@",cevent,mode);
267 wxCFEventLoop::WakeUp();
271 wxMacAutoreleasePool autoreleasepool;
272 NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
273 location:NSMakePoint(0.0, 0.0)
278 subtype:0 data1:0 data2:0];
279 [NSApp postEvent:event atStart:FALSE];
283 CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
285 NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
286 return [nsloop getCFRunLoop];
290 // TODO move into a evtloop_osx.cpp
292 wxModalEventLoop::wxModalEventLoop(wxWindow *modalWindow)
294 m_modalWindow = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
295 wxASSERT_MSG( m_modalWindow != NULL, "must pass in a toplevel window for modal event loop" );
296 m_modalNativeWindow = m_modalWindow->GetWXWindow();
299 wxModalEventLoop::wxModalEventLoop(WXWindow modalNativeWindow)
301 m_modalWindow = NULL;
302 wxASSERT_MSG( modalNativeWindow != NULL, "must pass in a toplevel window for modal event loop" );
303 m_modalNativeWindow = modalNativeWindow;
306 // END move into a evtloop_osx.cpp
308 void wxModalEventLoop::DoRun()
310 wxMacAutoreleasePool pool;
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])
318 while(NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES])
320 [NSApp sendEvent:event];
324 [NSApp runModalForWindow:m_modalNativeWindow];
327 void wxModalEventLoop::DoStop()
332 void wxGUIEventLoop::BeginModalSession( wxWindow* modalWindow )
334 WXWindow nsnow = nil;
336 if ( m_modalNestedLevel > 0 )
338 wxASSERT_MSG( m_modalWindow == modalWindow, "Nested Modal Sessions must be based on same window");
339 m_modalNestedLevel++;
343 m_modalWindow = modalWindow;
344 m_modalNestedLevel = 1;
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() )
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;
359 NSRect r = NSMakeRect(10, 10, 0, 0);
360 nsnow = [NSPanel alloc];
361 [nsnow initWithContentRect:r
362 styleMask:NSBorderlessWindowMask
363 backing:NSBackingStoreBuffered
366 [nsnow orderOut:nil];
367 m_dummyWindow = nsnow;
369 m_modalSession = [NSApp beginModalSessionForWindow:nsnow];
370 wxASSERT_MSG(m_modalSession != NULL, "modal session couldn't be started");
373 void wxGUIEventLoop::EndModalSession()
375 wxASSERT_MSG(m_modalSession != NULL, "no modal session active");
377 wxASSERT_MSG(m_modalNestedLevel > 0, "incorrect modal nesting level");
379 if ( --m_modalNestedLevel == 0 )
381 [NSApp endModalSession:(NSModalSession)m_modalSession];
382 m_modalSession = nil;
385 [m_dummyWindow release];
395 wxWindowDisabler::wxWindowDisabler(bool disable)
397 m_modalEventLoop = NULL;
398 m_disabled = disable;
403 wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
406 DoDisable(winToSkip);
409 void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
411 // remember the top level windows which were already disabled, so that we
412 // don't reenable them later
413 m_winDisabled = NULL;
415 wxWindowList::compatibility_iterator node;
416 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
418 wxWindow *winTop = node->GetData();
419 if ( winTop == winToSkip )
422 // we don't need to disable the hidden or already disabled windows
423 if ( winTop->IsEnabled() && winTop->IsShown() )
429 if ( !m_winDisabled )
431 m_winDisabled = new wxWindowList;
434 m_winDisabled->Append(winTop);
438 m_modalEventLoop = (wxEventLoop*)wxEventLoopBase::GetActive();
439 if (m_modalEventLoop)
440 m_modalEventLoop->BeginModalSession(winToSkip);
443 wxWindowDisabler::~wxWindowDisabler()
448 if (m_modalEventLoop)
449 m_modalEventLoop->EndModalSession();
451 wxWindowList::compatibility_iterator node;
452 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
454 wxWindow *winTop = node->GetData();
455 if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
459 //else: had been already disabled, don't reenable
462 delete m_winDisabled;