1 ///////////////////////////////////////////////////////////////////////////////
 
   2 // Name:        cocoa/evtloop.mm
 
   3 // Purpose:     implements wxEventLoop for Cocoa
 
   4 // Author:      David Elliott
 
   8 // Copyright:   (c) 2003 David Elliott <dfe@cox.net>
 
   9 // License:     wxWidgets licence
 
  10 ///////////////////////////////////////////////////////////////////////////////
 
  12 #include "wx/wxprec.h"
 
  14 #include "wx/evtloop.h"
 
  21 #import <AppKit/NSApplication.h>
 
  22 #import <AppKit/NSEvent.h>
 
  23 #import <Foundation/NSRunLoop.h>
 
  25 // ========================================================================
 
  27 // ========================================================================
 
  29 // ----------------------------------------------------------------------------
 
  30 // wxGUIEventLoop running and exiting
 
  31 // ----------------------------------------------------------------------------
 
  33 int wxGUIEventLoop::Run()
 
  35     // event loops are not recursive, you need to create another loop!
 
  36     wxCHECK_MSG( !IsRunning(), -1, wxT("can't reenter a message loop") );
 
  38     wxEventLoopActivator activate(this);
 
  40     [[NSApplication sharedApplication] run];
 
  47 void wxGUIEventLoop::Exit(int rc)
 
  49     wxCHECK_RET( IsRunning(), wxT("can't call Exit() if not running") );
 
  53     NSApplication *cocoaApp = [NSApplication sharedApplication];
 
  54     wxLogTrace(wxTRACE_COCOA,wxT("wxEventLoop::Exit isRunning=%d"), (int)[cocoaApp isRunning]);
 
  55     wxTheApp->WakeUpIdle();
 
  57     If we're being called from idle time (which occurs while checking the
 
  58     queue for new events) there may or may not be any events in the queue.
 
  59     In order to successfully stop the event loop, at least one event must
 
  60     be processed.  To ensure this always happens, WakeUpIdle is called.
 
  62     If the application was active when closed then this is unnecessary
 
  63     because it would receive a deactivate event anyway.  However, if the
 
  64     application was not active when closed, then no events would be
 
  65     added to the queue by Cocoa and thus the application would wait
 
  66     indefinitely for the next event.
 
  68     [cocoaApp stop: cocoaApp];
 
  71 // ----------------------------------------------------------------------------
 
  72 // wxEventLoop message processing dispatching
 
  73 // ----------------------------------------------------------------------------
 
  75 bool wxGUIEventLoop::Pending() const
 
  77     // a pointer to the event is returned if there is one, or nil if not
 
  78     return [[NSApplication sharedApplication]
 
  79             nextEventMatchingMask: NSAnyEventMask
 
  80             untilDate: nil /* Equivalent to [NSDate distantPast] */
 
  81             inMode: NSDefaultRunLoopMode
 
  85 bool wxGUIEventLoop::Dispatch()
 
  87     // This check is required by wxGTK but probably not really for wxCocoa
 
  88     // Keep it here to encourage developers to write cross-platform code
 
  89     wxCHECK_MSG( IsRunning(), false, wxT("can't call Dispatch() if not running") );
 
  90     NSApplication *cocoaApp = [NSApplication sharedApplication];
 
  91     // Block to retrieve an event then send it
 
  92     if(NSEvent *event = [cocoaApp
 
  93                 nextEventMatchingMask:NSAnyEventMask
 
  94                 untilDate:[NSDate distantFuture]
 
  95                 inMode:NSDefaultRunLoopMode
 
  98         [cocoaApp sendEvent: event];
 
 104 int wxGUIEventLoop::DispatchTimeout(unsigned long timeout)
 
 106     NSApplication *cocoaApp = [NSApplication sharedApplication];
 
 107     NSEvent *event = [cocoaApp
 
 108                 nextEventMatchingMask:NSAnyEventMask
 
 109                 untilDate:[[NSDate alloc] initWithTimeIntervalSinceNow:timeout/1000]
 
 110                 inMode:NSDefaultRunLoopMode
 
 115     [cocoaApp sendEvent: event];
 
 120 bool wxGUIEventLoop::YieldFor(long eventsToProcess)
 
 123     // disable log flushing from here because a call to wxYield() shouldn't
 
 124     // normally result in message boxes popping up &c
 
 128     m_isInsideYield = true;
 
 129     m_eventsToProcessInsideYield = eventsToProcess;
 
 131     // Run the event loop until it is out of events
 
 134         // TODO: implement event filtering using the eventsToProcess mask
 
 136         wxAutoNSAutoreleasePool pool;
 
 137         /*  NOTE: It may be better to use something like
 
 138             NSEventTrackingRunLoopMode since we don't necessarily want all
 
 139             timers/sources/observers to run, only those which would
 
 140             run while tracking events.  However, it should be noted that
 
 141             NSEventTrackingRunLoopMode is in the common set of modes
 
 142             so it may not effectively make much of a difference.
 
 144         NSEvent *event = [GetNSApplication()
 
 145                 nextEventMatchingMask:NSAnyEventMask
 
 146                 untilDate:[NSDate distantPast]
 
 147                 inMode:NSDefaultRunLoopMode
 
 151         [GetNSApplication() sendEvent: event];
 
 155         Because we just told NSApplication to avoid blocking it will in turn
 
 156         run the CFRunLoop with a timeout of 0 seconds.  In that case, our
 
 157         run loop observer on kCFRunLoopBeforeWaiting never fires because
 
 158         no waiting occurs.  Therefore, no idle events are sent.
 
 160         Believe it or not, this is actually desirable because we do not want
 
 161         to process idle from here.  However, we do want to process pending
 
 162         events because some user code expects to do work in a thread while
 
 163         the main thread waits and then notify the main thread by posting
 
 167         wxTheApp->ProcessPendingEvents();
 
 170     // let the logs be flashed again
 
 174     m_isInsideYield = false;