// Author: Vadim Zeitlin, Stefan Csomor
// Modified by:
// Created: 2006-01-12
-// RCS-ID: $Id: evtloop.cpp 54845 2008-07-30 14:52:41Z SC $
+// RCS-ID: $Id$
// Copyright: (c) 2006 Vadim Zeitlin <vadim@wxwindows.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
#ifndef WX_PRECOMP
#include "wx/app.h"
+ #include "wx/nonownedwnd.h"
#endif // WX_PRECOMP
#include "wx/log.h"
// wxEventLoop implementation
// ============================================================================
-/*
-static int CalculateNSEventMaskFromEventCategory(wxEventCategory cat)
-{
- NSLeftMouseDownMask |
- NSLeftMouseUpMask |
- NSRightMouseDownMask |
- NSRightMouseUpMask = 1 << NSRightMouseUp,
- NSMouseMovedMask = 1 << NSMouseMoved,
- NSLeftMouseDraggedMask = 1 << NSLeftMouseDragged,
- NSRightMouseDraggedMask = 1 << NSRightMouseDragged,
- NSMouseEnteredMask = 1 << NSMouseEntered,
- NSMouseExitedMask = 1 << NSMouseExited,
- NSScrollWheelMask = 1 << NSScrollWheel,
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
- NSTabletPointMask = 1 << NSTabletPoint,
- NSTabletProximityMask = 1 << NSTabletProximity,
-#endif
- NSOtherMouseDownMask = 1 << NSOtherMouseDown,
- NSOtherMouseUpMask = 1 << NSOtherMouseUp,
- NSOtherMouseDraggedMask = 1 << NSOtherMouseDragged,
-
-
-
- NSKeyDownMask = 1 << NSKeyDown,
- NSKeyUpMask = 1 << NSKeyUp,
- NSFlagsChangedMask = 1 << NSFlagsChanged,
-
- NSAppKitDefinedMask = 1 << NSAppKitDefined,
- NSSystemDefinedMask = 1 << NSSystemDefined,
- NSApplicationDefinedMask = 1 << NSApplicationDefined,
- NSPeriodicMask = 1 << NSPeriodic,
- NSCursorUpdateMask = 1 << NSCursorUpdate,
-
- NSAnyEventMask = 0xffffffffU
-}
-*/
+#if 0
-wxGUIEventLoop::wxGUIEventLoop()
-{
- m_sleepTime = 0.0;
-}
+// in case we want to integrate this
-void wxGUIEventLoop::WakeUp()
+static NSUInteger CalculateNSEventMaskFromEventCategory(wxEventCategory cat)
{
- extern void wxMacWakeUp();
-
- wxMacWakeUp();
-}
+ // the masking system doesn't really help, as only the lowlevel UI events
+ // are split in a useful way, all others are way to broad
+
+ if ( (cat | wxEVT_CATEGORY_USER_INPUT) && (cat | (~wxEVT_CATEGORY_USER_INPUT) ) )
+ return NSAnyEventMask;
+
+ NSUInteger mask = 0;
-//-----------------------------------------------------------------------------
-// event loop sources operations
-//-----------------------------------------------------------------------------
+ if ( cat | wxEVT_CATEGORY_USER_INPUT )
+ {
+ mask |=
+ NSLeftMouseDownMask |
+ NSLeftMouseUpMask |
+ NSRightMouseDownMask |
+ NSRightMouseUpMask |
+ NSMouseMovedMask |
+ NSLeftMouseDraggedMask |
+ NSRightMouseDraggedMask |
+ NSMouseEnteredMask |
+ NSMouseExitedMask |
+ NSScrollWheelMask |
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+ NSTabletPointMask |
+ NSTabletProximityMask |
+#endif
+ NSOtherMouseDownMask |
+ NSOtherMouseUpMask |
+ NSOtherMouseDraggedMask |
+ NSKeyDownMask |
+ NSKeyUpMask |
+ NSFlagsChangedMask |
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
-extern "C"
-{
-struct wx_cffd_data
-{
- wxEventLoopSourceHandler* handler;
- int flags;
-};
-
-static void wx_cffiledescriptor_callback(CFFileDescriptorRef cffd,
- CFOptionFlags flags, void* ctxData)
-{
- wxLogTrace(wxTRACE_EVT_SOURCE, "CFFileDescriptor Callback");
-
- wx_cffd_data* data = static_cast<wx_cffd_data*>(ctxData);
- wxEventLoopSourceHandler* handler = data->handler;
- if (flags & kCFFileDescriptorReadCallBack)
- handler->OnReadWaiting();
- if (flags & kCFFileDescriptorWriteCallBack)
- handler->OnWriteWaiting();
-
- // reenable callbacks
- if (data->flags & wxEVENT_SOURCE_INPUT)
- CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
- if (data->flags & wxEVENT_SOURCE_OUTPUT)
- CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorWriteCallBack);
-}
+ NSEventMaskGesture |
+ NSEventMaskMagnify |
+ NSEventMaskSwipe |
+ NSEventMaskRotate |
+ NSEventMaskBeginGesture |
+ NSEventMaskEndGesture |
+#endif
+ 0;
+ }
+
+ if ( cat | (~wxEVT_CATEGORY_USER_INPUT) )
+ {
+ mask |=
+ NSAppKitDefinedMask |
+ NSSystemDefinedMask |
+ NSApplicationDefinedMask |
+ NSPeriodicMask |
+ NSCursorUpdateMask;
+ }
+
+ return mask;
}
-wxMacEventLoopSource* wxGUIEventLoop::CreateSource(int fd,
- wxEventLoopSourceHandler* handler,
- int flags) const
-{
- wxMacEventLoopSource* source = new wxMacEventLoopSource();
- // FIXME this is currently a leak :-)
- wx_cffd_data* data = new wx_cffd_data;
- data->handler = handler;
- data->flags = flags;
- CFFileDescriptorContext ctx = { 0, data, NULL, NULL, NULL };
- CFFileDescriptorRef cffd = CFFileDescriptorCreate(kCFAllocatorDefault, fd,
- true, wx_cffiledescriptor_callback, &ctx);
-
- if (flags & wxEVENT_SOURCE_INPUT)
- CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorReadCallBack);
- if (flags & wxEVENT_SOURCE_OUTPUT)
- CFFileDescriptorEnableCallBacks(cffd, kCFFileDescriptorWriteCallBack);
-
- source->SetResource(
- CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, cffd, 0));
- source->SetHandler(handler);
- source->SetFlags(flags);
- return source;
-}
-#elif
-wxMacEventLoopSource* wxGUIEventLoop::CreateSource(int fd,
- wxEventLoopSourceHandler* handler,
- int flags) const
-{
- return NULL;
-}
#endif
-bool wxGUIEventLoop::DoAddSource(wxAbstractEventLoopSource* src)
+wxGUIEventLoop::wxGUIEventLoop()
{
- Source* source = dynamic_cast<Source*>(src);
- wxCHECK_MSG( source, false, "Invalid source type" );
-
- wxLogTrace(wxTRACE_EVT_SOURCE,
- "wxGUIEventLoop::AddSource() source=%d",
- source->GetResource());
-
- NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
- CFRunLoopRef cfloop = [nsloop getCFRunLoop];
- CFRunLoopAddSource(cfloop, source->GetResource(), kCFRunLoopDefaultMode);
-
- return true;
+ m_modalSession = nil;
+ m_dummyWindow = nil;
}
-bool wxGUIEventLoop::DoRemoveSource(wxAbstractEventLoopSource* src)
+wxGUIEventLoop::~wxGUIEventLoop()
{
- Source* source = dynamic_cast<Source*>(src);
- wxCHECK_MSG( source, false, "Invalid source type" );
-
- wxLogTrace(wxTRACE_EVT_SOURCE,
- "wxGUIEventLoop::RemoveSource() source=%d",
- source->GetResource());
-
- NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
- CFRunLoopRef cfloop = [nsloop getCFRunLoop];
- CFRunLoopRemoveSource(cfloop, source->GetResource(), kCFRunLoopDefaultMode);
-
- return true;
+ wxASSERT( m_modalSession == nil );
+ wxASSERT( m_dummyWindow == nil );
}
//-----------------------------------------------------------------------------
// events dispatch and loop handling
//-----------------------------------------------------------------------------
+#if 0
+
bool wxGUIEventLoop::Pending() const
{
+#if 0
+ // this code doesn't reliably detect pending events
+ // so better return true and have the dispatch deal with it
+ // as otherwise we end up in a tight loop when idle events are responded
+ // to by RequestMore(true)
wxMacAutoreleasePool autoreleasepool;
- // a pointer to the event is returned if there is one, or nil if not
+
return [[NSApplication sharedApplication]
nextEventMatchingMask: NSAnyEventMask
untilDate: nil
inMode: NSDefaultRunLoopMode
- dequeue: NO];
+ dequeue: NO] != nil;
+#else
+ return true;
+#endif
}
bool wxGUIEventLoop::Dispatch()
inMode:NSDefaultRunLoopMode
dequeue: YES])
{
+ WXEVENTREF formerEvent = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEvent();
+ WXEVENTHANDLERCALLREF formerHandler = wxTheApp == NULL ? NULL : wxTheApp->MacGetCurrentEventHandlerCallRef();
+
+ if (wxTheApp)
+ wxTheApp->MacSetCurrentEvent(event, NULL);
m_sleepTime = 0.0;
[NSApp sendEvent: event];
+
+ if (wxTheApp)
+ wxTheApp->MacSetCurrentEvent(formerEvent , formerHandler);
}
else
{
return true;
}
-bool wxGUIEventLoop::YieldFor(long eventsToProcess)
+#endif
+
+int wxGUIEventLoop::DoDispatchTimeout(unsigned long timeout)
{
-#if wxUSE_THREADS
- // Yielding from a non-gui thread needs to bail out, otherwise we end up
- // possibly sending events in the thread too.
- if ( !wxThread::IsMain() )
+ wxMacAutoreleasePool autoreleasepool;
+
+ if ( m_modalSession )
{
- return true;
+ NSInteger response = [NSApp runModalSession:(NSModalSession)m_modalSession];
+
+ switch (response)
+ {
+ case NSRunContinuesResponse:
+ {
+ if ( [[NSApplication sharedApplication]
+ nextEventMatchingMask: NSAnyEventMask
+ untilDate: nil
+ inMode: NSDefaultRunLoopMode
+ dequeue: NO] != nil )
+ return 1;
+
+ return -1;
+ }
+
+ case NSRunStoppedResponse:
+ case NSRunAbortedResponse:
+ return -1;
+ default:
+ wxFAIL_MSG("unknown response code");
+ break;
+ }
+ return -1;
+ }
+ else
+ {
+ NSEvent *event = [NSApp
+ nextEventMatchingMask:NSAnyEventMask
+ untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
+ inMode:NSDefaultRunLoopMode
+ dequeue: YES];
+
+ if ( event == nil )
+ return -1;
+
+ [NSApp sendEvent: event];
+
+ return 1;
}
-#endif // wxUSE_THREADS
+}
- m_isInsideYield = true;
- m_eventsToProcessInsideYield = eventsToProcess;
+void wxGUIEventLoop::DoRun()
+{
+ wxMacAutoreleasePool autoreleasepool;
+ [NSApp run];
+}
-#if wxUSE_LOG
- // disable log flushing from here because a call to wxYield() shouldn't
- // normally result in message boxes popping up &c
- wxLog::Suspend();
-#endif // wxUSE_LOG
+void wxGUIEventLoop::DoStop()
+{
+ [NSApp stop:0];
+ // only calling stop: is not enough when called from a runloop-observer,
+ // therefore add a dummy event, to make sure the runloop gets another round
+ NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined
+ location:NSMakePoint(0.0, 0.0)
+ modifierFlags:0
+ timestamp:0
+ windowNumber:0
+ context:nil
+ subtype:0 data1:0 data2:0];
+ [NSApp postEvent:event atStart:FALSE];
+}
- // process all pending events:
- while ( Pending() )
- Dispatch();
+CFRunLoopRef wxGUIEventLoop::CFGetCurrentRunLoop() const
+{
+ NSRunLoop* nsloop = [NSRunLoop currentRunLoop];
+ return [nsloop getCFRunLoop];
+}
- // it's necessary to call ProcessIdle() to update the frames sizes which
- // might have been changed (it also will update other things set from
- // OnUpdateUI() which is a nice (and desired) side effect)
- while ( ProcessIdle() ) {}
- // if there are pending events, we must process them.
- if (wxTheApp)
- wxTheApp->ProcessPendingEvents();
+// TODO move into a evtloop_osx.cpp
-#if wxUSE_LOG
- wxLog::Resume();
-#endif // wxUSE_LOG
- m_isInsideYield = false;
+wxModalEventLoop::wxModalEventLoop(wxWindow *modalWindow)
+{
+ m_modalWindow = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
+ wxASSERT_MSG( m_modalWindow != NULL, "must pass in a toplevel window for modal event loop" );
+ m_modalNativeWindow = m_modalWindow->GetWXWindow();
+}
- return true;
+wxModalEventLoop::wxModalEventLoop(WXWindow modalNativeWindow)
+{
+ m_modalWindow = NULL;
+ wxASSERT_MSG( modalNativeWindow != NULL, "must pass in a toplevel window for modal event loop" );
+ m_modalNativeWindow = modalNativeWindow;
}
-int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
+// END move into a evtloop_osx.cpp
+
+void wxModalEventLoop::DoRun()
{
- wxMacAutoreleasePool autoreleasepool;
+ wxMacAutoreleasePool pool;
- NSEvent *event = [NSApp
- nextEventMatchingMask:NSAnyEventMask
- untilDate:[NSDate dateWithTimeIntervalSinceNow: timeout/1000]
- inMode:NSDefaultRunLoopMode
- dequeue: YES];
- if ( !event )
- return -1;
+ // If the app hasn't started, flush the event queue
+ // If we don't do this, the Dock doesn't get the message that
+ // the app has started so will refuse to activate it.
+ [NSApplication sharedApplication];
+ if (![NSApp isRunning])
+ {
+ while(NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES])
+ {
+ [NSApp sendEvent:event];
+ }
+ }
+
+ [NSApp runModalForWindow:m_modalNativeWindow];
+}
- [NSApp sendEvent: event];
+void wxModalEventLoop::DoStop()
+{
+ [NSApp stopModal];
+}
- return true;
+void wxGUIEventLoop::BeginModalSession( wxWindow* modalWindow )
+{
+ WXWindow nsnow = nil;
+
+ if ( modalWindow )
+ {
+ // we must show now, otherwise beginModalSessionForWindow does it but it
+ // also would do a centering of the window before overriding all our position
+ if ( !modalWindow->IsShownOnScreen() )
+ modalWindow->Show();
+
+ wxNonOwnedWindow* now = dynamic_cast<wxNonOwnedWindow*> (modalWindow);
+ wxASSERT_MSG( now != NULL, "must pass in a toplevel window for modal event loop" );
+ nsnow = now ? now->GetWXWindow() : nil;
+ }
+ else
+ {
+ NSRect r = NSMakeRect(10, 10, 0, 0);
+ nsnow = [NSPanel alloc];
+ [nsnow initWithContentRect:r
+ styleMask:NSBorderlessWindowMask
+ backing:NSBackingStoreBuffered
+ defer:YES
+ ];
+ [nsnow orderOut:nil];
+ m_dummyWindow = nsnow;
+ }
+ m_modalSession = [NSApp beginModalSessionForWindow:nsnow];
+}
+
+void wxGUIEventLoop::EndModalSession()
+{
+ wxASSERT_MSG(m_modalSession != NULL, "no modal session active");
+ [NSApp endModalSession:(NSModalSession)m_modalSession];
+ m_modalSession = nil;
+ if ( m_dummyWindow )
+ {
+ [m_dummyWindow release];
+ m_dummyWindow = nil;
+ }
+}
+
+//
+//
+//
+
+wxWindowDisabler::wxWindowDisabler(bool disable)
+{
+ m_modalEventLoop = NULL;
+ m_disabled = disable;
+ if ( disable )
+ DoDisable();
+}
+
+wxWindowDisabler::wxWindowDisabler(wxWindow *winToSkip)
+{
+ m_disabled = true;
+ DoDisable(winToSkip);
+}
+
+void wxWindowDisabler::DoDisable(wxWindow *winToSkip)
+{
+ // remember the top level windows which were already disabled, so that we
+ // don't reenable them later
+ m_winDisabled = NULL;
+
+ wxWindowList::compatibility_iterator node;
+ for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
+ {
+ wxWindow *winTop = node->GetData();
+ if ( winTop == winToSkip )
+ continue;
+
+ // we don't need to disable the hidden or already disabled windows
+ if ( winTop->IsEnabled() && winTop->IsShown() )
+ {
+ winTop->Disable();
+ }
+ else
+ {
+ if ( !m_winDisabled )
+ {
+ m_winDisabled = new wxWindowList;
+ }
+
+ m_winDisabled->Append(winTop);
+ }
+ }
+
+ m_modalEventLoop = (wxEventLoop*)wxEventLoopBase::GetActive();
+ m_modalEventLoop->BeginModalSession(winToSkip);
}
+
+wxWindowDisabler::~wxWindowDisabler()
+{
+ if ( !m_disabled )
+ return;
+
+ m_modalEventLoop->EndModalSession();
+
+ wxWindowList::compatibility_iterator node;
+ for ( node = wxTopLevelWindows.GetFirst(); node; node = node->GetNext() )
+ {
+ wxWindow *winTop = node->GetData();
+ if ( !m_winDisabled || !m_winDisabled->Find(winTop) )
+ {
+ winTop->Enable();
+ }
+ //else: had been already disabled, don't reenable
+ }
+
+ delete m_winDisabled;
+}
+