1/////////////////////////////////////////////////////////////////////////////// 2// Name: src/cocoa/evtloop.mm 3// Purpose: implements wxEventLoop for Cocoa 4// Author: David Elliott 5// Created: 2003/10/02 6// Copyright: (c) 2003 David Elliott <dfe@cox.net> 7// (c) 2013 Rob Bresalier 8// Licence: wxWindows licence 9/////////////////////////////////////////////////////////////////////////////// 10 11#include "wx/wxprec.h" 12 13#include "wx/evtloop.h" 14 15#ifndef WX_PRECOMP 16 #include "wx/log.h" 17 #include "wx/app.h" 18#endif //WX_PRECOMP 19 20#import <AppKit/NSApplication.h> 21#import <AppKit/NSEvent.h> 22#import <Foundation/NSRunLoop.h> 23 24// ======================================================================== 25// wxGUIEventLoop 26// ======================================================================== 27 28// ---------------------------------------------------------------------------- 29// wxGUIEventLoop running and exiting 30// ---------------------------------------------------------------------------- 31 32int wxGUIEventLoop::DoRun() 33{ 34 [[NSApplication sharedApplication] run]; 35 36 OnExit(); 37 38 return m_exitcode; 39} 40 41void wxGUIEventLoop::ScheduleExit(int rc) 42{ 43 wxCHECK_RET( IsInsideRun(), wxT("can't call ScheduleExit() if not started") ); 44 45 m_exitcode = rc; 46 47 NSApplication *cocoaApp = [NSApplication sharedApplication]; 48 wxLogTrace(wxTRACE_COCOA,wxT("wxEventLoop::Exit isRunning=%d"), (int)[cocoaApp isRunning]); 49 wxTheApp->WakeUpIdle(); 50 /* Notes: 51 If we're being called from idle time (which occurs while checking the 52 queue for new events) there may or may not be any events in the queue. 53 In order to successfully stop the event loop, at least one event must 54 be processed. To ensure this always happens, WakeUpIdle is called. 55 56 If the application was active when closed then this is unnecessary 57 because it would receive a deactivate event anyway. However, if the 58 application was not active when closed, then no events would be 59 added to the queue by Cocoa and thus the application would wait 60 indefinitely for the next event. 61 */ 62 [cocoaApp stop: cocoaApp]; 63} 64 65// ---------------------------------------------------------------------------- 66// wxEventLoop message processing dispatching 67// ---------------------------------------------------------------------------- 68 69bool wxGUIEventLoop::Pending() const 70{ 71 // a pointer to the event is returned if there is one, or nil if not 72 return [[NSApplication sharedApplication] 73 nextEventMatchingMask: NSAnyEventMask 74 untilDate: nil /* Equivalent to [NSDate distantPast] */ 75 inMode: NSDefaultRunLoopMode 76 dequeue: NO]; 77} 78 79bool wxGUIEventLoop::Dispatch() 80{ 81 // This check is required by wxGTK but probably not really for wxCocoa 82 // Keep it here to encourage developers to write cross-platform code 83 wxCHECK_MSG( IsRunning(), false, wxT("can't call Dispatch() if not running") ); 84 NSApplication *cocoaApp = [NSApplication sharedApplication]; 85 // Block to retrieve an event then send it 86 if(NSEvent *event = [cocoaApp 87 nextEventMatchingMask:NSAnyEventMask 88 untilDate:[NSDate distantFuture] 89 inMode:NSDefaultRunLoopMode 90 dequeue: YES]) 91 { 92 [cocoaApp sendEvent: event]; 93 } 94 95 return true; 96} 97 98int wxGUIEventLoop::DispatchTimeout(unsigned long timeout) 99{ 100 NSApplication *cocoaApp = [NSApplication sharedApplication]; 101 NSEvent *event = [cocoaApp 102 nextEventMatchingMask:NSAnyEventMask 103 untilDate:[[NSDate alloc] initWithTimeIntervalSinceNow:timeout/1000] 104 inMode:NSDefaultRunLoopMode 105 dequeue: YES]; 106 if ( !event ) 107 return -1; 108 109 [cocoaApp sendEvent: event]; 110 111 return true; 112} 113 114bool wxGUIEventLoop::YieldFor(long eventsToProcess) 115{ 116#if wxUSE_LOG 117 // disable log flushing from here because a call to wxYield() shouldn't 118 // normally result in message boxes popping up &c 119 wxLog::Suspend(); 120#endif // wxUSE_LOG 121 122 m_isInsideYield = true; 123 m_eventsToProcessInsideYield = eventsToProcess; 124 125 // Run the event loop until it is out of events 126 while (1) 127 { 128 // TODO: implement event filtering using the eventsToProcess mask 129 130 wxAutoNSAutoreleasePool pool; 131 /* NOTE: It may be better to use something like 132 NSEventTrackingRunLoopMode since we don't necessarily want all 133 timers/sources/observers to run, only those which would 134 run while tracking events. However, it should be noted that 135 NSEventTrackingRunLoopMode is in the common set of modes 136 so it may not effectively make much of a difference. 137 */ 138 NSEvent *event = [GetNSApplication() 139 nextEventMatchingMask:NSAnyEventMask 140 untilDate:[NSDate distantPast] 141 inMode:NSDefaultRunLoopMode 142 dequeue: YES]; 143 if(!event) 144 break; 145 [GetNSApplication() sendEvent: event]; 146 } 147 148 /* 149 Because we just told NSApplication to avoid blocking it will in turn 150 run the CFRunLoop with a timeout of 0 seconds. In that case, our 151 run loop observer on kCFRunLoopBeforeWaiting never fires because 152 no waiting occurs. Therefore, no idle events are sent. 153 154 Believe it or not, this is actually desirable because we do not want 155 to process idle from here. However, we do want to process pending 156 events because some user code expects to do work in a thread while 157 the main thread waits and then notify the main thread by posting 158 an event. 159 */ 160 if (wxTheApp) 161 wxTheApp->ProcessPendingEvents(); 162 163#if wxUSE_LOG 164 // let the logs be flashed again 165 wxLog::Resume(); 166#endif // wxUSE_LOG 167 168 m_isInsideYield = false; 169 170 return true; 171} 172