1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/cocoa/evtloop.mm
3 // Purpose: implementation of wxEventLoop for OS X
4 // Author: Vadim Zeitlin, Stefan Csomor
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 ///////////////////////////////////////////////////////////////////////////////
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 // ============================================================================
43 static NSUInteger CalculateNSEventMaskFromEventCategory(wxEventCategory cat)
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
48 if ( (cat | wxEVT_CATEGORY_USER_INPUT) && (cat | (~wxEVT_CATEGORY_USER_INPUT) ) )
49 return NSAnyEventMask;
53 if ( cat | wxEVT_CATEGORY_USER_INPUT )
58 NSRightMouseDownMask |
61 NSLeftMouseDraggedMask |
62 NSRightMouseDraggedMask |
66 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
68 NSTabletProximityMask |
70 NSOtherMouseDownMask |
72 NSOtherMouseDraggedMask |
77 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
82 NSEventMaskBeginGesture |
83 NSEventMaskEndGesture |
88 if ( cat | (~wxEVT_CATEGORY_USER_INPUT) )
93 NSApplicationDefinedMask |
102 wxGUIEventLoop::wxGUIEventLoop()
104 m_modalSession = nil;
108 wxGUIEventLoop::~wxGUIEventLoop()
110 wxASSERT( m_modalSession == nil );
111 wxASSERT( m_dummyWindow == nil );
114 //-----------------------------------------------------------------------------
115 // events dispatch and loop handling
116 //-----------------------------------------------------------------------------
120 bool wxGUIEventLoop::Pending() const
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;
129 return [[NSApplication sharedApplication]
130 nextEventMatchingMask: NSAnyEventMask
132 inMode: NSDefaultRunLoopMode
139 bool wxGUIEventLoop::Dispatch()
144 wxMacAutoreleasePool autoreleasepool;
146 if(NSEvent *event = [NSApp
147 nextEventMatchingMask:NSAnyEventMask
148 untilDate:[NSDate dateWithTimeIntervalSinceNow: m_sleepTime]
149 inMode:NSDefaultRunLoopMode
152 WXEVENTREF formerEvent = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEvent();
153 WXEVENTHANDLERCALLREF formerHandler = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEventHandlerCallRef();
156 wxTheApp->MacSetCurrentEvent(event, NULL);
158 [NSApp sendEvent: event];
161 wxTheApp->MacSetCurrentEvent(formerEvent , formerHandler);
166 wxTheApp->ProcessPendingEvents();
168 if ( wxTheApp->ProcessIdle() )
186 int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
188 wxMacAutoreleasePool autoreleasepool;
190 if ( m_modalSession )
192 NSInteger response = [NSApp runModalSession:(NSModalSession)m_modalSession];
196 case NSRunContinuesResponse:
198 if ( [[NSApplication sharedApplication]
199 nextEventMatchingMask: NSAnyEventMask
201 inMode: NSDefaultRunLoopMode
202 dequeue: NO] != nil )
208 case NSRunStoppedResponse:
209 case NSRunAbortedResponse:
212 wxFAIL_MSG("unknown response code");
219 NSEvent *event = [NSApp
220 nextEventMatchingMask:NSAnyEventMask
221 untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
222 inMode:NSDefaultRunLoopMode
228 [NSApp sendEvent: event];
234 void wxGUIEventLoop::DoRun()
236 wxMacAutoreleasePool autoreleasepool;
240 void wxGUIEventLoop::DoStop()
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)
251 subtype:0 data1:0 data2:0];
252 [NSApp postEvent:event atStart:FALSE];
255 CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
257 NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
258 return [nsloop getCFRunLoop];
262 // TODO move into a evtloop_osx.cpp
264 wxModalEventLoop::wxModalEventLoop(wxWindow *modalWindow)
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();
271 wxModalEventLoop::wxModalEventLoop(WXWindow modalNativeWindow)
273 m_modalWindow = NULL;
274 wxASSERT_MSG( modalNativeWindow != NULL, "must pass in a toplevel window for modal event loop" );
275 m_modalNativeWindow = modalNativeWindow;
278 // END move into a evtloop_osx.cpp
280 void wxModalEventLoop::DoRun()
282 wxMacAutoreleasePool pool;
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])
290 while(NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES])
292 [NSApp sendEvent:event];
296 [NSApp runModalForWindow:m_modalNativeWindow];
299 void wxModalEventLoop::DoStop()
304 void wxGUIEventLoop::BeginModalSession( wxWindow* modalWindow )
306 WXWindow nsnow = nil;
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;
316 NSRect r = NSMakeRect(10, 10, 0, 0);
317 nsnow = [NSPanel alloc];
318 [nsnow initWithContentRect:r
319 styleMask:NSBorderlessWindowMask
320 backing:NSBackingStoreBuffered
323 [nsnow orderOut:nil];
324 m_dummyWindow = nsnow;
326 m_modalSession = [NSApp beginModalSessionForWindow:nsnow];
329 void wxGUIEventLoop::EndModalSession()
331 wxASSERT_MSG(m_modalSession != NULL, "no modal session active");
332 [NSApp endModalSession:(NSModalSession)m_modalSession];
333 m_modalSession = nil;
336 [m_dummyWindow release];
345 wxWindowDisabler::wxWindowDisabler(bool disable)
347 m_modalEventLoop = NULL;
348 m_disabled = disable;
353 wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
356 DoDisable(winToSkip);
359 void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
361 // remember the top level windows which were already disabled, so that we
362 // don't reenable them later
363 m_winDisabled = NULL;
365 wxWindowList::compatibility_iterator node;
366 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
368 wxWindow *winTop = node->GetData();
369 if ( winTop == winToSkip )
372 // we don't need to disable the hidden or already disabled windows
373 if ( winTop->IsEnabled() && winTop->IsShown() )
379 if ( !m_winDisabled )
381 m_winDisabled = new wxWindowList;
384 m_winDisabled->Append(winTop);
388 m_modalEventLoop = (wxEventLoop*)wxEventLoopBase::GetActive();
389 m_modalEventLoop->BeginModalSession(winToSkip);
392 wxWindowDisabler::~wxWindowDisabler()
397 m_modalEventLoop->EndModalSession();
399 wxWindowList::compatibility_iterator node;
400 for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
402 wxWindow *winTop = node->GetData();
403 if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
407 //else: had been already disabled, don't reenable
410 delete m_winDisabled;