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;
112 wxGUIEventLoop::~wxGUIEventLoop()
114 wxASSERT( m_modalSession == nil );
115 wxASSERT( m_dummyWindow == nil );
118 //-----------------------------------------------------------------------------
119 // events dispatch and loop handling
120 //-----------------------------------------------------------------------------
124 bool wxGUIEventLoop::Pending() const
127 // this code doesn't reliably detect pending events
128 // so better return true and have the dispatch deal with it
129 // as otherwise we end up in a tight loop when idle events are responded
130 // to by RequestMore(true)
131 wxMacAutoreleasePool autoreleasepool;
133 return [[NSApplication sharedApplication]
134 nextEventMatchingMask: NSAnyEventMask
136 inMode: NSDefaultRunLoopMode
143 bool wxGUIEventLoop::Dispatch()
148 wxMacAutoreleasePool autoreleasepool;
150 if(NSEvent *event = [NSApp
151 nextEventMatchingMask:NSAnyEventMask
152 untilDate:[NSDate dateWithTimeIntervalSinceNow: m_sleepTime]
153 inMode:NSDefaultRunLoopMode
156 WXEVENTREF formerEvent = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEvent();
157 WXEVENTHANDLERCALLREF formerHandler = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEventHandlerCallRef();
160 wxTheApp->MacSetCurrentEvent(event, NULL);
162 [NSApp sendEvent: event];
165 wxTheApp->MacSetCurrentEvent(formerEvent , formerHandler);
170 wxTheApp->ProcessPendingEvents();
172 if ( wxTheApp->ProcessIdle() )
190 int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
192 wxMacAutoreleasePool autoreleasepool;
194 if ( m_modalSession )
196 NSInteger response = [NSApp runModalSession:(NSModalSession)m_modalSession];
200 case NSRunContinuesResponse:
202 if ( [[NSApplication sharedApplication]
203 nextEventMatchingMask: NSAnyEventMask
205 inMode: NSDefaultRunLoopMode
206 dequeue: NO] != nil )
212 case NSRunStoppedResponse:
213 case NSRunAbortedResponse:
216 wxFAIL_MSG("unknown response code");
223 NSEvent *event = [NSApp
224 nextEventMatchingMask:NSAnyEventMask
225 untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
226 inMode:NSDefaultRunLoopMode
232 [NSApp sendEvent: event];
238 void wxGUIEventLoop::DoRun()
240 wxMacAutoreleasePool autoreleasepool;
244 void wxGUIEventLoop::DoStop()
247 // only calling stop: is not enough when called from a runloop-observer,
248 // therefore add a dummy event, to make sure the runloop gets another round
249 NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
250 location:NSMakePoint(0.0, 0.0)
255 subtype:0 data1:0 data2:0];
256 [NSApp postEvent:event atStart:FALSE];
259 CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
261 NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
262 return [nsloop getCFRunLoop];
266 // TODO move into a evtloop_osx.cpp
268 wxModalEventLoop::wxModalEventLoop(wxWindow *modalWindow)
270 m_modalWindow = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
271 wxASSERT_MSG( m_modalWindow != NULL, "must pass in a toplevel window for modal event loop" );
272 m_modalNativeWindow = m_modalWindow->GetWXWindow();
275 wxModalEventLoop::wxModalEventLoop(WXWindow modalNativeWindow)
277 m_modalWindow = NULL;
278 wxASSERT_MSG( modalNativeWindow != NULL, "must pass in a toplevel window for modal event loop" );
279 m_modalNativeWindow = modalNativeWindow;
282 // END move into a evtloop_osx.cpp
284 void wxModalEventLoop::DoRun()
286 wxMacAutoreleasePool pool;
288 // If the app hasn't started, flush the event queue
289 // If we don't do this, the Dock doesn't get the message that
290 // the app has started so will refuse to activate it.
291 [NSApplication sharedApplication];
292 if (![NSApp isRunning])
294 while(NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES])
296 [NSApp sendEvent:event];
300 [NSApp runModalForWindow:m_modalNativeWindow];
303 void wxModalEventLoop::DoStop()
308 void wxGUIEventLoop::BeginModalSession( wxWindow* modalWindow )
310 WXWindow nsnow = nil;
314 // we must show now, otherwise beginModalSessionForWindow does it but it
315 // also would do a centering of the window before overriding all our position
316 if ( !modalWindow->IsShownOnScreen() )
319 wxNonOwnedWindow* now = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
320 wxASSERT_MSG( now != NULL, "must pass in a toplevel window for modal event loop" );
321 nsnow = now ? now->GetWXWindow() : nil;
325 NSRect r = NSMakeRect(10, 10, 0, 0);
326 nsnow = [NSPanel alloc];
327 [nsnow initWithContentRect:r
328 styleMask:NSBorderlessWindowMask
329 backing:NSBackingStoreBuffered
332 [nsnow orderOut:nil];
333 m_dummyWindow = nsnow;
335 m_modalSession = [NSApp beginModalSessionForWindow:nsnow];
338 void wxGUIEventLoop::EndModalSession()
340 wxASSERT_MSG(m_modalSession != NULL, "no modal session active");
341 [NSApp endModalSession:(NSModalSession)m_modalSession];
342 m_modalSession = nil;
345 [m_dummyWindow release];
354 wxWindowDisabler::wxWindowDisabler(bool disable)
356 m_modalEventLoop = NULL;
357 m_disabled = disable;
362 wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
365 DoDisable(winToSkip);
368 void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
370 // remember the top level windows which were already disabled, so that we
371 // don't reenable them later
372 m_winDisabled = NULL;
374 wxWindowList::compatibility_iterator node;
375 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
377 wxWindow *winTop = node->GetData();
378 if ( winTop == winToSkip )
381 // we don't need to disable the hidden or already disabled windows
382 if ( winTop->IsEnabled() && winTop->IsShown() )
388 if ( !m_winDisabled )
390 m_winDisabled = new wxWindowList;
393 m_winDisabled->Append(winTop);
397 m_modalEventLoop = (wxEventLoop*)wxEventLoopBase::GetActive();
398 m_modalEventLoop->BeginModalSession(winToSkip);
401 wxWindowDisabler::~wxWindowDisabler()
406 m_modalEventLoop->EndModalSession();
408 wxWindowList::compatibility_iterator node;
409 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
411 wxWindow *winTop = node->GetData();
412 if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
416 //else: had been already disabled, don't reenable
419 delete m_winDisabled;