Improved idle event processing.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@24870 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
David Elliott
2003-12-15 15:57:42 +00:00
parent 9bfe7ad92d
commit eb537cfb1c
4 changed files with 63 additions and 87 deletions

View File

@@ -33,8 +33,6 @@ public:
// ------------------------------------------------------------------------
public:
inline WX_NSApplication GetNSApplication() { return m_cocoaApp; }
void CocoaInstallRequestedIdleHandler() { if(m_isIdle) CocoaInstallIdleHandler(); }
inline void CocoaRequestIdle() { m_isIdle = true; }
virtual void CocoaDelegate_applicationWillBecomeActive();
virtual void CocoaDelegate_applicationDidBecomeActive();
virtual void CocoaDelegate_applicationWillResignActive();
@@ -43,8 +41,6 @@ protected:
WX_NSApplication m_cocoaApp;
struct objc_object *m_cocoaAppDelegate;
WX_NSThread m_cocoaMainThread;
void CocoaInstallIdleHandler();
bool m_isIdle;
// ------------------------------------------------------------------------
// Implementation
@@ -54,7 +50,7 @@ public:
virtual void Exit();
virtual bool Yield(bool onlyIfNeeded = FALSE);
virtual void WakeUpIdle() { CocoaRequestIdle(); }
virtual void WakeUpIdle();
virtual bool Initialize(int& argc, wxChar **argv);
virtual void CleanUp();

View File

@@ -36,12 +36,15 @@
#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSThread.h>
#import <AppKit/NSEvent.h>
#import <Foundation/NSString.h>
// ========================================================================
// wxPoseAsInitializer
// ========================================================================
wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
static bool sg_needIdle = true;
// ========================================================================
// wxPoserNSApplication
// ========================================================================
@@ -49,6 +52,7 @@ wxPoseAsInitializer *wxPoseAsInitializer::sm_first = NULL;
{
}
- (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag;
- (void)sendEvent: (NSEvent*)anEvent;
@end // wxPoserNSApplication
@@ -56,10 +60,53 @@ WX_IMPLEMENT_POSER(wxPoserNSApplication);
@implementation wxPoserNSApplication : NSApplication
/* NOTE: The old method of idle event handling added the handler using the
[NSRunLoop -performSelector:target:argument:order:modes] which caused
the invocation to occur at the begining of [NSApplication
-nextEventMatchingMask:untilDate:expiration:inMode:dequeue:]. However,
the code would be scheduled for invocation with every iteration of
the event loop. This new method simply overrides the method. The
same caveats apply. In particular, by the time the event loop has
called this method, it usually expects to receive an event. If you
plan on stopping the event loop, it is wise to send an event through
the queue to ensure this method will return.
See wxEventLoop::Exit() for more information.
*/
- (NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)flag
{
// Get the same events except don't block
NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
// If we got one, simply return it
if(event)
return event;
// No events, try doing some idle stuff
if(sg_needIdle && !wxTheApp->IsInAssert() && ([NSDefaultRunLoopMode isEqualToString:mode] || [NSModalPanelRunLoopMode isEqualToString:mode]))
{
sg_needIdle = false;
wxLogDebug("Processing idle events");
while(wxTheApp->ProcessIdle())
{
// Get the same events except don't block
NSEvent *event = [super nextEventMatchingMask:mask untilDate:nil/* equivalent to [NSDate distantPast] */ inMode:mode dequeue:flag];
// If we got one, simply return it
if(event)
return event;
// we didn't get one, do some idle work
wxLogDebug("Looping idle events");
}
// No more idle work requested, block
wxLogDebug("Finished idle processing");
}
else
wxLogDebug("Avoiding idle processing sg_needIdle=%d",sg_needIdle);
return [super nextEventMatchingMask:mask untilDate:expiration inMode:mode dequeue:flag];
}
- (void)sendEvent: (NSEvent*)anEvent
{
wxLogDebug("SendEvent");
wxTheApp->CocoaInstallRequestedIdleHandler();
sg_needIdle = true;
[super sendEvent: anEvent];
}
@@ -72,7 +119,6 @@ WX_IMPLEMENT_POSER(wxPoserNSApplication);
{
}
- (void)doIdle: (id)data;
// Delegate methods
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication;
- (void)applicationWillBecomeActive:(NSNotification *)notification;
@@ -83,38 +129,6 @@ WX_IMPLEMENT_POSER(wxPoserNSApplication);
@implementation wxNSApplicationDelegate : NSObject
- (void)doIdle: (id)data
{
wxASSERT(wxTheApp);
wxASSERT(wxMenuBarManager::GetInstance());
wxLogDebug("doIdle called");
#ifdef __WXDEBUG__
if(wxTheApp->IsInAssert())
{
wxLogDebug("Idle events ignored durring assertion dialog");
}
else
#endif
{
NSRunLoop *rl = [NSRunLoop currentRunLoop];
// runMode: beforeDate returns YES if something was done
while(wxTheApp->ProcessIdle()) // FIXME: AND NO EVENTS ARE PENDING
{
wxLogDebug("Looping for idle events");
#if 1
if( [rl runMode:[rl currentMode] beforeDate:[NSDate distantPast]])
{
wxLogDebug("Found actual work to do");
break;
}
#endif
}
}
wxLogDebug("Idle processing complete, requesting next idle event");
// Add ourself back into the run loop (on next event) if necessary
wxTheApp->CocoaRequestIdle();
}
// NOTE: Terminate means that the event loop does NOT return and thus
// cleanup code doesn't properly execute. Furthermore, wxWindows has its
// own exit on frame delete mechanism.
@@ -209,7 +223,6 @@ wxApp::wxApp()
{
m_topWindow = NULL;
m_isIdle = true;
#if WXWIN_COMPATIBILITY_2_2
m_wantDebugOutput = TRUE;
#endif
@@ -223,34 +236,6 @@ wxApp::wxApp()
m_cocoaAppDelegate = NULL;
}
void wxApp::CocoaInstallIdleHandler()
{
// If we're not the main thread, don't install the idle handler
if(m_cocoaMainThread != [NSThread currentThread])
{
wxLogDebug("Attempt to install idle handler from secondary thread");
return;
}
// If we're supposed to be stopping, don't add more idle events
if(![m_cocoaApp isRunning])
return;
wxLogDebug("wxApp::CocoaInstallIdleHandler");
m_isIdle = false;
// Call doIdle for EVERYTHING dammit
// We'd need Foundation/NSConnection.h for this next constant, do we need it?
[[ NSRunLoop currentRunLoop ] performSelector:@selector(doIdle:) target:m_cocoaAppDelegate argument:NULL order:0 modes:[NSArray arrayWithObjects:NSDefaultRunLoopMode, /* NSConnectionReplyRunLoopMode,*/ NSModalPanelRunLoopMode, /**/NSEventTrackingRunLoopMode,/**/ nil] ];
/* Notes:
In the Mac OS X implementation of Cocoa, the above method schedules
doIdle: to be called from *within* [NSApplication
-nextEventMatchingMask:untilDate:inMode:dequeue:]. That is, no
NSEvent object is generated and control does not return from that
method. In fact, control will only return from that method for the
usual reasons (e.g. a real event is received or the untilDate is reached).
This has implications when trying to stop the event loop and return to
its caller. See wxEventLoop::Exit
*/
}
void wxApp::CocoaDelegate_applicationWillBecomeActive()
{
}
@@ -283,11 +268,6 @@ bool wxApp::OnInitGui()
wxDC::CocoaInitializeTextSystem();
// [ m_cocoaApp setDelegate:m_cocoaApp ];
#if 0
wxLogDebug("Just for kicks");
[ m_cocoaAppDelegate performSelector:@selector(doIdle:) withObject:NULL ];
wxLogDebug("okay.. done now");
#endif
return TRUE;
}
@@ -356,6 +336,14 @@ bool wxApp::Yield(bool onlyIfNeeded)
return true;
}
void wxApp::WakeUpIdle()
{
[m_cocoaApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
location:NSZeroPoint modifierFlags:NSAnyEventMask
timestamp:0 windowNumber:0 context:nil
subtype:0 data1:0 data2:0] atStart:NO];
}
#ifdef __WXDEBUG__
void wxApp::OnAssert(const wxChar *file, int line, const wxChar* cond, const wxChar *msg)
{

View File

@@ -12,6 +12,7 @@
#include "wx/wxprec.h"
#ifndef WX_PRECOMP
#include "wx/log.h"
#include "wx/app.h"
#endif //WX_PRECOMP
#include "wx/evtloop.h"
@@ -88,17 +89,12 @@ void wxEventLoop::Exit(int rc)
NSApplication *cocoaApp = [NSApplication sharedApplication];
wxLogDebug("wxEventLoop::Exit isRunning=%d", (int)[cocoaApp isRunning]);
// This works around a bug in Cocoa.
[NSEvent startPeriodicEventsAfterDelay:0.0 withPeriod:5.0];
wxTheApp->WakeUpIdle();
/* Notes:
This function is most often called during idle time. See
wxApp::CocoaInstallIdleHandler() for an overview of the implications
of idle event time. In short, Cocoa must have at least one real event
in the queue (of which an idle "event" is not) in order for it to
realize that the application has been stopped. The above method
generates the first periodic event immediately, and would generate
further events every 5 seconds if not for the fact that the next
method stops the event loop.
If we're being called from idle time (which occurs while checking the
queue for new events) there may or may not be any events in the queue.
In order to successfully stop the event loop, at least one event must
be processed. To ensure this always happens, WakeUpIdle is called.
If the application was active when closed then this is unnecessary
because it would receive a deactivate event anyway. However, if the

View File

@@ -225,10 +225,6 @@ void wxTopLevelWindowCocoa::CocoaDelegate_windowWillClose(void)
{
m_closed = true;
Destroy();
/* Be SURE that idle events get ran. If the window was not active when
it was closed, then there will be no more events to trigger this and
therefore it must be done here */
wxTheApp->CocoaInstallRequestedIdleHandler();
}
bool wxTopLevelWindowCocoa::CocoaDelegate_windowShouldClose()