From 16c8ddadb7c7ce5170047791d57cfc15c2c3e75f Mon Sep 17 00:00:00 2001 From: Christopher Lloyd Date: Thu, 4 Nov 2010 15:08:19 -0400 Subject: [PATCH] Fix behavior of NSRunLoop runMode:beforeDate: and AppKit nextEvent* calls to properly run once or with a timeout --- AppKit/NSApplication.m | 34 +++++++++++---- AppKit/NSDisplay.m | 15 +++++-- AppKit/Win32.subproj/Win32Display.m | 21 ++++++++-- AppKit/Win32.subproj/Win32EventInputSource.m | 10 ++++- Foundation/NSRunLoop/NSDelayedPerform.m | 7 +--- Foundation/NSRunLoop/NSInputSourceSet.h | 4 +- Foundation/NSRunLoop/NSInputSourceSet.m | 4 +- Foundation/NSRunLoop/NSRunLoop.m | 42 ++++++++++++++----- Foundation/NSRunLoop/NSRunLoopState.h | 4 +- Foundation/NSRunLoop/NSRunLoopState.m | 38 ++++++++--------- Foundation/NSStream/NSSelectInputSource.h | 2 +- Foundation/NSStream/NSSelectInputSource.m | 8 ++-- Foundation/NSStream/NSSelectInputSourceSet.h | 1 - Foundation/NSStream/NSSelectInputSourceSet.m | 44 ++++++++++---------- 14 files changed, 150 insertions(+), 84 deletions(-) diff --git a/AppKit/NSApplication.m b/AppKit/NSApplication.m index 53c30e1b..c03d7e3c 100755 --- a/AppKit/NSApplication.m +++ b/AppKit/NSApplication.m @@ -527,8 +527,10 @@ id NSApp=nil; } -(NSEvent *)nextEventMatchingMask:(unsigned int)mask untilDate:(NSDate *)untilDate inMode:(NSString *)mode dequeue:(BOOL)dequeue { + NSEvent *nextEvent=nil; + + do { NSAutoreleasePool *pool=[NSAutoreleasePool new]; - NSEvent *nextEvent; NS_DURING [NSClassFromString(@"Win32RunningCopyPipe") performSelector:@selector(createRunningCopyPipe)]; @@ -538,17 +540,26 @@ id NSApp=nil; [self _checkForAppActivation]; [[NSApp windows] makeObjectsPerformSelector:@selector(displayIfNeeded)]; - nextEvent=[_display nextEventMatchingMask:mask untilDate:untilDate inMode:mode dequeue:dequeue]; + nextEvent=[[_display nextEventMatchingMask:mask untilDate:untilDate inMode:mode dequeue:dequeue] retain]; - [_currentEvent release]; - _currentEvent=[nextEvent retain]; + if([nextEvent type]==NSAppKitSystem){ + [nextEvent release]; + nextEvent=nil; + } + NS_HANDLER [self reportException:localException]; NS_ENDHANDLER [pool release]; + }while(nextEvent==nil && [untilDate timeIntervalSinceNow]>0); - return [[_currentEvent retain] autorelease]; + if(nextEvent!=nil){ + [_currentEvent release]; + _currentEvent=[nextEvent retain]; +} + + return [nextEvent autorelease]; } -(NSEvent *)currentEvent { @@ -713,10 +724,14 @@ id NSApp=nil; } -(int)runModalSession:(NSModalSession)session { + + while([session stopCode]==NSRunContinuesResponse) { NSAutoreleasePool *pool=[NSAutoreleasePool new]; - NSDate *future=[NSDate distantFuture]; - NSEvent *event=[self nextEventMatchingMask:NSAnyEventMask - untilDate:future inMode:NSModalPanelRunLoopMode dequeue:YES]; + NSEvent *event=[self nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate date] inMode:NSModalPanelRunLoopMode dequeue:YES]; + + if(event==nil) + break; + NSWindow *window=[event window]; // in theory this could get weird, but all we want is the ESC-cancel keybinding, afaik NSApp doesn't respond to any other doCommandBySelectors... @@ -729,6 +744,7 @@ id NSApp=nil; [[session modalWindow] makeKeyAndOrderFront:self]; [pool release]; + } return [session stopCode]; } @@ -755,7 +771,7 @@ id NSApp=nil; while((result=[NSApp runModalSession:session])==NSRunContinuesResponse) - ; + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; [self endModalSession:session]; diff --git a/AppKit/NSDisplay.m b/AppKit/NSDisplay.m index e4d26f1d..3da85e8e 100755 --- a/AppKit/NSDisplay.m +++ b/AppKit/NSDisplay.m @@ -93,7 +93,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI } -(NSEvent *)nextEventMatchingMask:(unsigned)mask untilDate:(NSDate *)untilDate inMode:(NSString *)mode dequeue:(BOOL)dequeue { - NSEvent *result; + NSEvent *result=nil; _eventMask=mask; @@ -102,15 +102,22 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI [[NSRunLoop currentRunLoop] runMode:mode beforeDate:untilDate]; - if([_eventQueue count]==0) - result=[[[NSEvent alloc] initWithType:NSAppKitSystem location:NSMakePoint(0,0) modifierFlags:0 window:nil] autorelease]; + while(result==nil && [_eventQueue count]>0){ + NSEvent *check=[_eventQueue objectAtIndex:0]; + + if(!(NSEventMaskFromType([check type])&mask)) + [_eventQueue removeObjectAtIndex:0]; else { - result=[[[_eventQueue objectAtIndex:0] retain] autorelease]; + result=[[check retain] autorelease]; if(dequeue) [_eventQueue removeObjectAtIndex:0]; } + } + if(result==nil) + result=[[[NSEvent alloc] initWithType:NSAppKitSystem location:NSMakePoint(0,0) modifierFlags:0 window:nil] autorelease]; + return result; } diff --git a/AppKit/Win32.subproj/Win32Display.m b/AppKit/Win32.subproj/Win32Display.m index e410c65a..79fbe3d7 100755 --- a/AppKit/Win32.subproj/Win32Display.m +++ b/AppKit/Win32.subproj/Win32Display.m @@ -379,13 +379,28 @@ static BOOL CALLBACK monitorEnumerator(HMONITOR hMonitor,HDC hdcMonitor,LPRECT r } -(NSEvent *)nextEventMatchingMask:(unsigned)mask untilDate:(NSDate *)untilDate inMode:(NSString *)mode dequeue:(BOOL)dequeue { - NSEvent *result; + NSEvent *result=nil; [[NSRunLoop currentRunLoop] addInputSource:_eventInputSource forMode:mode]; [self stopWaitCursor]; - result=[super nextEventMatchingMask:mask untilDate:untilDate inMode:mode dequeue:dequeue]; + + while([untilDate timeIntervalSinceNow]>0){ + result=[super nextEventMatchingMask:mask|NSPlatformSpecificDisplayMask untilDate:untilDate inMode:mode dequeue:dequeue]; + + if([result type]==NSPlatformSpecificDisplayEvent){ + Win32Event *win32Event=[(NSEvent_CoreGraphics *)result coreGraphicsEvent]; + MSG msg=[win32Event msg]; + + DispatchMessage(&msg); + result=nil; + } + + if(result!=nil) + break; + } [self startWaitCursor]; - [[NSRunLoop currentRunLoop] removeInputSource:_eventInputSource forMode:mode]; + +// [[NSRunLoop currentRunLoop] removeInputSource:_eventInputSource forMode:mode]; return result; } diff --git a/AppKit/Win32.subproj/Win32EventInputSource.m b/AppKit/Win32.subproj/Win32EventInputSource.m index ae895469..6731176d 100755 --- a/AppKit/Win32.subproj/Win32EventInputSource.m +++ b/AppKit/Win32.subproj/Win32EventInputSource.m @@ -9,7 +9,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI #import #import +#import #import +#import @implementation Win32EventInputSource @@ -23,8 +25,12 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){ NSAutoreleasePool *pool=[NSAutoreleasePool new]; - if(![(Win32Display *)[Win32Display currentDisplay] postMSG:msg]) - DispatchMessage(&msg); + if(![(Win32Display *)[Win32Display currentDisplay] postMSG:msg]){ + Win32Event *cgEvent=[Win32Event eventWithMSG:msg]; + NSEvent *event=[[[NSEvent_CoreGraphics alloc] initWithDisplayEvent:cgEvent] autorelease]; + + [[Win32Display currentDisplay] postEvent:event atStart:NO]; + } [pool release]; return YES; diff --git a/Foundation/NSRunLoop/NSDelayedPerform.m b/Foundation/NSRunLoop/NSDelayedPerform.m index e5b90a10..4611b9e4 100755 --- a/Foundation/NSRunLoop/NSDelayedPerform.m +++ b/Foundation/NSRunLoop/NSDelayedPerform.m @@ -1,12 +1,10 @@ -/* Copyright (c) 2006-2007 Christopher J. W. Lloyd +/* Copyright (c) 2006-2007 Christopher J. W. Lloyd Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -// Original - Christopher Lloyd #import #import @@ -19,8 +17,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI return self; } --(void)dealloc -{ +-(void)dealloc { [_object release]; [_argument release]; [super dealloc]; diff --git a/Foundation/NSRunLoop/NSInputSourceSet.h b/Foundation/NSRunLoop/NSInputSourceSet.h index 57373935..0c74dbb4 100755 --- a/Foundation/NSRunLoop/NSInputSourceSet.h +++ b/Foundation/NSRunLoop/NSInputSourceSet.h @@ -23,9 +23,9 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI -(NSSet *)validInputSources; --(BOOL)immediateInputInMode:(NSString *)mode; +-(BOOL)fireSingleImmediateInputInMode:(NSString *)mode; --(void)changingIntoMode:(NSString *)mode; +-(void)startingInMode:(NSString *)mode; -(void)waitInBackgroundInMode:(NSString *)mode; diff --git a/Foundation/NSRunLoop/NSInputSourceSet.m b/Foundation/NSRunLoop/NSInputSourceSet.m index 2f91b0b9..f2def530 100755 --- a/Foundation/NSRunLoop/NSInputSourceSet.m +++ b/Foundation/NSRunLoop/NSInputSourceSet.m @@ -41,7 +41,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI [_inputSources removeObject:source]; } --(void)changingIntoMode:(NSString *)mode { +-(void)startingInMode:(NSString *)mode { // do nothing } @@ -67,7 +67,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI return _inputSources; } --(BOOL)immediateInputInMode:(NSString *)mode { +-(BOOL)fireSingleImmediateInputInMode:(NSString *)mode { NSEnumerator *state=[[self validInputSources] objectEnumerator]; NSInputSource *check; diff --git a/Foundation/NSRunLoop/NSRunLoop.m b/Foundation/NSRunLoop/NSRunLoop.m index 3fe27534..8c720812 100755 --- a/Foundation/NSRunLoop/NSRunLoop.m +++ b/Foundation/NSRunLoop/NSRunLoop.m @@ -144,16 +144,16 @@ NSString * const NSRunLoopCommonModes=@"kCFRunLoopCommonModes"; -(NSDate *)limitDateForMode:(NSString *)mode { NSRunLoopState *state=[self stateForMode:mode]; - if(![mode isEqualToString:_currentMode]){ + mode=[mode retain]; [_currentMode release]; - _currentMode=[mode retain]; - [state changingIntoMode:mode]; - } + _currentMode=mode; + + [state startingInMode:mode]; if([self _orderedPerforms]) - [self _wakeUp]; + ;//[self _wakeUp]; if([state fireFirstTimer]) - [self _wakeUp]; + ;//[self _wakeUp]; [[NSNotificationQueue defaultQueue] asapProcessMode:mode]; return [state limitDateForMode:mode]; @@ -167,7 +167,7 @@ NSString * const NSRunLoopCommonModes=@"kCFRunLoopCommonModes"; [[NSNotificationQueue defaultQueue] idleProcessMode:mode]; } else { - [state acceptInputForMode:mode beforeDate:date]; + [state waitForSingleInputForMode:mode beforeDate:date]; } } @@ -183,17 +183,39 @@ NSString * const NSRunLoopCommonModes=@"kCFRunLoopCommonModes"; @class NSSelectInputSource, NSSocket; -(BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)date { + BOOL didProcessAnything=NO; + + do { NSAutoreleasePool *pool=[NSAutoreleasePool new]; NSDate *limitDate=[self limitDateForMode:mode]; - if(limitDate!=nil){ + if(limitDate==nil){ + [pool release]; + return didProcessAnything; + } + limitDate=[limitDate earlierDate:date]; - [self acceptInputForMode:mode beforeDate:limitDate]; + + didProcessAnything=YES; + + NSRunLoopState *state=[self stateForMode:mode]; + + if([[NSNotificationQueue defaultQueue] hasIdleNotificationsInMode:mode]){ + + if(![state pollInputForMode:mode]) + [[NSNotificationQueue defaultQueue] idleProcessMode:mode]; } + else { + [state waitForSingleInputForMode:mode beforeDate:limitDate]; + [pool release]; + return YES; + } [pool release]; - return (limitDate!=nil); + }while([date timeIntervalSinceNow]>0); + + return YES; } -(void)runUntilDate:(NSDate *)date { diff --git a/Foundation/NSRunLoop/NSRunLoopState.h b/Foundation/NSRunLoop/NSRunLoopState.h index 4bad6f4b..f114b801 100755 --- a/Foundation/NSRunLoop/NSRunLoopState.h +++ b/Foundation/NSRunLoop/NSRunLoopState.h @@ -20,7 +20,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI -(void)addTimer:(NSTimer *)timer; --(void)changingIntoMode:(NSString *)mode; +-(void)startingInMode:(NSString *)mode; -(BOOL)fireFirstTimer; -(NSDate *)limitDateForMode:(NSString *)mode; @@ -30,7 +30,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI -(void)invalidateTimerWithDelayedPerform:(NSDelayedPerform *)delayed; --(void)acceptInputForMode:(NSString *)mode beforeDate:(NSDate *)date; +-(BOOL)waitForSingleInputForMode:(NSString *)mode beforeDate:(NSDate *)date; -(BOOL)pollInputForMode:(NSString *)mode; diff --git a/Foundation/NSRunLoop/NSRunLoopState.m b/Foundation/NSRunLoop/NSRunLoopState.m index 9eafdf9e..54ffa464 100755 --- a/Foundation/NSRunLoop/NSRunLoopState.m +++ b/Foundation/NSRunLoop/NSRunLoopState.m @@ -47,13 +47,13 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI [_timers addObject:timer]; } --(void)changingIntoMode:(NSString *)mode { +-(void)startingInMode:(NSString *)mode { NSInteger i,count=[_asyncInputSourceSets count]; - [_inputSourceSet changingIntoMode:mode]; + [_inputSourceSet startingInMode:mode]; for(i=0;i