2002-01-30 15:28:50 +00:00
|
|
|
|
/** Implementation of object for waiting on several input sources
|
|
|
|
|
NSRunLoop.m
|
|
|
|
|
|
1999-04-20 16:28:04 +00:00
|
|
|
|
Copyright (C) 1996-1999 Free Software Foundation, Inc.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
Original by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
|
|
|
|
Created: March 1996
|
|
|
|
|
OPENSTEP version by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
|
Created: August 1997
|
|
|
|
|
|
1996-05-12 00:56:10 +00:00
|
|
|
|
This file is part of the GNUstep Base Library.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1996-04-10 18:20:36 +00:00
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
1996-04-10 18:20:36 +00:00
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Library General Public License for more details.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1996-04-10 18:20:36 +00:00
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
|
License along with this library; if not, write to the Free
|
2005-05-22 03:32:16 +00:00
|
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
|
2001-12-18 16:54:15 +00:00
|
|
|
|
|
|
|
|
|
<title>NSRunLoop class reference</title>
|
|
|
|
|
$Date$ $Revision$
|
1997-09-01 21:59:51 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2003-06-07 01:24:41 +00:00
|
|
|
|
#include "config.h"
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/preface.h"
|
2003-06-07 01:24:41 +00:00
|
|
|
|
#include "Foundation/NSMapTable.h"
|
|
|
|
|
#include "Foundation/NSDate.h"
|
|
|
|
|
#include "Foundation/NSValue.h"
|
|
|
|
|
#include "Foundation/NSAutoreleasePool.h"
|
|
|
|
|
#include "Foundation/NSPort.h"
|
|
|
|
|
#include "Foundation/NSTimer.h"
|
|
|
|
|
#include "Foundation/NSNotificationQueue.h"
|
|
|
|
|
#include "Foundation/NSRunLoop.h"
|
|
|
|
|
#include "Foundation/NSThread.h"
|
|
|
|
|
#include "Foundation/NSDebug.h"
|
2005-10-30 10:42:42 +00:00
|
|
|
|
#include "GSRunLoopCtxt.h"
|
|
|
|
|
#include "GSRunLoopWatcher.h"
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#include <sys/types.h>
|
2000-06-06 16:50:52 +00:00
|
|
|
|
#endif
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_SYS_TIME_H
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#include <sys/time.h>
|
2000-06-06 16:50:52 +00:00
|
|
|
|
#endif
|
2005-02-23 22:17:54 +00:00
|
|
|
|
#ifdef HAVE_POLL_F
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#include <poll.h>
|
|
|
|
|
#endif
|
2002-05-02 21:22:06 +00:00
|
|
|
|
#ifdef HAVE_UNISTD_H
|
2002-02-20 06:42:05 +00:00
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#endif
|
2000-06-06 16:50:52 +00:00
|
|
|
|
#include <time.h>
|
1997-09-01 21:59:51 +00:00
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <string.h> /* for memset() */
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
2002-06-06 14:02:59 +00:00
|
|
|
|
NSString * const NSDefaultRunLoopMode = @"NSDefaultRunLoopMode";
|
|
|
|
|
|
1999-04-20 16:28:04 +00:00
|
|
|
|
static NSDate *theFuture = nil;
|
1999-04-19 14:29:52 +00:00
|
|
|
|
|
2001-06-21 04:49:20 +00:00
|
|
|
|
extern BOOL GSCheckTasks();
|
1999-04-19 14:29:52 +00:00
|
|
|
|
|
1999-04-20 16:28:04 +00:00
|
|
|
|
|
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
/*
|
|
|
|
|
* The GSRunLoopPerformer class is used to hold information about
|
|
|
|
|
* messages which are due to be sent to objects once each runloop
|
|
|
|
|
* iteration has passed.
|
|
|
|
|
*/
|
|
|
|
|
@interface GSRunLoopPerformer: NSObject
|
|
|
|
|
{
|
|
|
|
|
@public
|
|
|
|
|
SEL selector;
|
|
|
|
|
id target;
|
|
|
|
|
id argument;
|
|
|
|
|
unsigned order;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) fire;
|
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)target
|
|
|
|
|
argument: (id)argument
|
|
|
|
|
order: (unsigned int)order;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation GSRunLoopPerformer
|
|
|
|
|
|
|
|
|
|
- (void) fire
|
|
|
|
|
{
|
|
|
|
|
[target performSelector: selector withObject: argument];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)aTarget
|
|
|
|
|
argument: (id)anArgument
|
|
|
|
|
order: (unsigned int)theOrder
|
|
|
|
|
{
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self)
|
|
|
|
|
{
|
|
|
|
|
selector = aSelector;
|
|
|
|
|
target = aTarget;
|
|
|
|
|
argument = anArgument;
|
|
|
|
|
order = theOrder;
|
|
|
|
|
}
|
|
|
|
|
return self;
|
1999-04-28 23:02:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@end
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-04-02 14:27:40 +00:00
|
|
|
|
|
|
|
|
|
@interface NSRunLoop (TimedPerformers)
|
|
|
|
|
- (NSMutableArray*) _timedPerformers;
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSRunLoop (TimedPerformers)
|
|
|
|
|
- (NSMutableArray*) _timedPerformers
|
|
|
|
|
{
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return _timedPerformers;
|
1998-04-02 14:27:40 +00:00
|
|
|
|
}
|
|
|
|
|
@end
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
/*
|
2002-01-16 14:00:59 +00:00
|
|
|
|
* The GSTimedPerformer class is used to hold information about
|
|
|
|
|
* messages which are due to be sent to objects at a particular time.
|
1997-09-01 21:59:51 +00:00
|
|
|
|
*/
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@interface GSTimedPerformer: NSObject <GCFinalization>
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1999-09-14 10:03:02 +00:00
|
|
|
|
@public
|
1998-10-29 08:46:30 +00:00
|
|
|
|
SEL selector;
|
|
|
|
|
id target;
|
|
|
|
|
id argument;
|
|
|
|
|
NSTimer *timer;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) fire;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)target
|
|
|
|
|
argument: (id)argument
|
|
|
|
|
delay: (NSTimeInterval)delay;
|
2003-01-31 19:06:51 +00:00
|
|
|
|
- (void) invalidate;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
@end
|
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
@implementation GSTimedPerformer
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
|
{
|
1999-03-02 08:58:30 +00:00
|
|
|
|
[self gcFinalize];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
TEST_RELEASE(timer);
|
1999-03-02 08:58:30 +00:00
|
|
|
|
RELEASE(target);
|
|
|
|
|
RELEASE(argument);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
[super dealloc];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
- (void) fire
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
DESTROY(timer);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
[target performSelector: selector withObject: argument];
|
2001-06-21 13:36:13 +00:00
|
|
|
|
[[[NSRunLoop currentRunLoop] _timedPerformers]
|
2000-09-08 09:14:29 +00:00
|
|
|
|
removeObjectIdenticalTo: self];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-03-02 08:58:30 +00:00
|
|
|
|
- (void) gcFinalize
|
|
|
|
|
{
|
2003-01-31 19:06:51 +00:00
|
|
|
|
[self invalidate];
|
1999-03-02 08:58:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-09-14 10:03:02 +00:00
|
|
|
|
- (id) initWithSelector: (SEL)aSelector
|
|
|
|
|
target: (id)aTarget
|
|
|
|
|
argument: (id)anArgument
|
1999-09-14 13:50:24 +00:00
|
|
|
|
delay: (NSTimeInterval)delay
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1998-10-29 08:46:30 +00:00
|
|
|
|
self = [super init];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
if (self != nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
|
|
|
|
selector = aSelector;
|
1999-03-02 08:58:30 +00:00
|
|
|
|
target = RETAIN(aTarget);
|
|
|
|
|
argument = RETAIN(anArgument);
|
2002-01-16 14:00:59 +00:00
|
|
|
|
timer = [[NSTimer allocWithZone: NSDefaultMallocZone()]
|
2002-08-27 13:40:42 +00:00
|
|
|
|
initWithFireDate: nil
|
|
|
|
|
interval: delay
|
|
|
|
|
target: self
|
|
|
|
|
selector: @selector(fire)
|
|
|
|
|
userInfo: nil
|
|
|
|
|
repeats: NO];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return self;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2003-01-31 19:06:51 +00:00
|
|
|
|
|
|
|
|
|
- (void) invalidate
|
|
|
|
|
{
|
|
|
|
|
if (timer != nil)
|
|
|
|
|
{
|
|
|
|
|
[timer invalidate];
|
|
|
|
|
DESTROY(timer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1998-04-02 14:27:40 +00:00
|
|
|
|
@end
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
1998-04-02 14:27:40 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/*
|
|
|
|
|
* Setup for inline operation of arrays.
|
|
|
|
|
*/
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
|
#ifndef GSI_ARRAY_TYPES
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#define GSI_ARRAY_TYPES GSUNION_OBJ
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#if GS_WITH_GC == 0
|
2002-01-31 07:20:16 +00:00
|
|
|
|
#define GSI_ARRAY_RELEASE(A, X) [(X).obj release]
|
|
|
|
|
#define GSI_ARRAY_RETAIN(A, X) [(X).obj retain]
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#else
|
2002-01-31 07:20:16 +00:00
|
|
|
|
#define GSI_ARRAY_RELEASE(A, X)
|
|
|
|
|
#define GSI_ARRAY_RETAIN(A, X)
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#endif
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2003-07-31 23:49:32 +00:00
|
|
|
|
#include "GNUstepBase/GSIArray.h"
|
2005-02-23 16:05:09 +00:00
|
|
|
|
#endif
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
|
|
|
|
static NSComparisonResult aSort(GSIArrayItem i0, GSIArrayItem i1)
|
|
|
|
|
{
|
2005-02-22 11:22:44 +00:00
|
|
|
|
return [((GSRunLoopWatcher *)(i0.obj))->_date
|
2002-01-30 15:28:50 +00:00
|
|
|
|
compare: ((GSRunLoopWatcher *)(i1.obj))->_date];
|
1998-04-02 14:27:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@implementation NSObject (TimedPerformers)
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/*
|
2002-08-27 14:24:54 +00:00
|
|
|
|
* Cancels any perform operations set up for the specified target
|
|
|
|
|
* in the current run loop.
|
|
|
|
|
*/
|
|
|
|
|
+ (void) cancelPreviousPerformRequestsWithTarget: (id)target
|
|
|
|
|
{
|
|
|
|
|
NSMutableArray *perf = [[NSRunLoop currentRunLoop] _timedPerformers];
|
|
|
|
|
unsigned count = [perf count];
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
GSTimedPerformer *array[count];
|
|
|
|
|
|
|
|
|
|
IF_NO_GC(RETAIN(target));
|
|
|
|
|
[perf getObjects: array];
|
|
|
|
|
while (count-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSTimedPerformer *p = array[count];
|
|
|
|
|
|
|
|
|
|
if (p->target == target)
|
|
|
|
|
{
|
2003-01-31 19:06:51 +00:00
|
|
|
|
[p invalidate];
|
2002-08-27 14:24:54 +00:00
|
|
|
|
[perf removeObjectAtIndex: count];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RELEASE(target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/*
|
2002-08-27 14:24:54 +00:00
|
|
|
|
* Cancels any perform operations set up for the specified target
|
2002-08-27 15:04:44 +00:00
|
|
|
|
* in the current loop, but only if the value of aSelector and argument
|
2003-01-31 19:06:51 +00:00
|
|
|
|
* with which the performs were set up match those supplied.<br />
|
|
|
|
|
* Matching of the argument may be either by pointer equality or by
|
|
|
|
|
* use of the [NSObject-isEqual:] method.
|
2002-08-27 14:24:54 +00:00
|
|
|
|
*/
|
2002-01-30 15:28:50 +00:00
|
|
|
|
+ (void) cancelPreviousPerformRequestsWithTarget: (id)target
|
|
|
|
|
selector: (SEL)aSelector
|
|
|
|
|
object: (id)arg
|
|
|
|
|
{
|
|
|
|
|
NSMutableArray *perf = [[NSRunLoop currentRunLoop] _timedPerformers];
|
|
|
|
|
unsigned count = [perf count];
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
GSTimedPerformer *array[count];
|
|
|
|
|
|
|
|
|
|
IF_NO_GC(RETAIN(target));
|
|
|
|
|
IF_NO_GC(RETAIN(arg));
|
|
|
|
|
[perf getObjects: array];
|
|
|
|
|
while (count-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSTimedPerformer *p = array[count];
|
|
|
|
|
|
|
|
|
|
if (p->target == target && sel_eq(p->selector, aSelector)
|
2003-01-31 19:06:51 +00:00
|
|
|
|
&& (p->argument == arg || [p->argument isEqual: arg]))
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
2003-01-31 19:06:51 +00:00
|
|
|
|
[p invalidate];
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[perf removeObjectAtIndex: count];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RELEASE(arg);
|
|
|
|
|
RELEASE(target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) performSelector: (SEL)aSelector
|
|
|
|
|
withObject: (id)argument
|
|
|
|
|
afterDelay: (NSTimeInterval)seconds
|
|
|
|
|
{
|
|
|
|
|
NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
|
|
|
|
GSTimedPerformer *item;
|
|
|
|
|
|
|
|
|
|
item = [[GSTimedPerformer alloc] initWithSelector: aSelector
|
|
|
|
|
target: self
|
|
|
|
|
argument: argument
|
|
|
|
|
delay: seconds];
|
|
|
|
|
[[loop _timedPerformers] addObject: item];
|
|
|
|
|
RELEASE(item);
|
|
|
|
|
[loop addTimer: item->timer forMode: NSDefaultRunLoopMode];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) performSelector: (SEL)aSelector
|
|
|
|
|
withObject: (id)argument
|
|
|
|
|
afterDelay: (NSTimeInterval)seconds
|
|
|
|
|
inModes: (NSArray*)modes
|
|
|
|
|
{
|
|
|
|
|
unsigned count = [modes count];
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
NSRunLoop *loop = [NSRunLoop currentRunLoop];
|
|
|
|
|
NSString *marray[count];
|
|
|
|
|
GSTimedPerformer *item;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
item = [[GSTimedPerformer alloc] initWithSelector: aSelector
|
|
|
|
|
target: self
|
|
|
|
|
argument: argument
|
|
|
|
|
delay: seconds];
|
|
|
|
|
[[loop _timedPerformers] addObject: item];
|
|
|
|
|
RELEASE(item);
|
|
|
|
|
[modes getObjects: marray];
|
2002-01-30 08:57:00 +00:00
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[loop addTimer: item->timer forMode: marray[i]];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@interface NSRunLoop (Private)
|
|
|
|
|
|
|
|
|
|
- (void) _addWatcher: (GSRunLoopWatcher*)item
|
|
|
|
|
forMode: (NSString*)mode;
|
|
|
|
|
- (void) _checkPerformers: (GSRunLoopCtxt*)context;
|
|
|
|
|
- (GSRunLoopWatcher*) _getWatcher: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode;
|
|
|
|
|
- (void) _removeWatcher: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode;
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
@implementation NSRunLoop (Private)
|
|
|
|
|
|
|
|
|
|
/* Add a watcher to the list for the specified mode. Keep the list in
|
|
|
|
|
limit-date order. */
|
|
|
|
|
- (void) _addWatcher: (GSRunLoopWatcher*) item forMode: (NSString*)mode
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
GSIArray watchers;
|
|
|
|
|
id obj;
|
|
|
|
|
|
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context == nil)
|
|
|
|
|
{
|
|
|
|
|
context = [[GSRunLoopCtxt alloc] initWithMode: mode extra: _extra];
|
|
|
|
|
NSMapInsert(_contextMap, context->mode, context);
|
|
|
|
|
RELEASE(context);
|
|
|
|
|
}
|
|
|
|
|
watchers = context->watchers;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If the receiver or its delegate (if any) respond to
|
|
|
|
|
* 'limitDateForMode: ' then we ask them for the limit date for
|
|
|
|
|
* this watcher.
|
|
|
|
|
*/
|
|
|
|
|
obj = item->receiver;
|
|
|
|
|
if ([obj respondsToSelector: @selector(limitDateForMode:)])
|
|
|
|
|
{
|
|
|
|
|
NSDate *d = [obj limitDateForMode: mode];
|
|
|
|
|
|
|
|
|
|
item->_date = RETAIN(d);
|
|
|
|
|
}
|
|
|
|
|
else if ([obj respondsToSelector: @selector(delegate)])
|
|
|
|
|
{
|
|
|
|
|
obj = [obj delegate];
|
|
|
|
|
if (obj != nil && [obj respondsToSelector: @selector(limitDateForMode:)])
|
|
|
|
|
{
|
|
|
|
|
NSDate *d = [obj limitDateForMode: mode];
|
|
|
|
|
|
|
|
|
|
item->_date = RETAIN(d);
|
|
|
|
|
}
|
|
|
|
|
else
|
2002-10-03 16:21:06 +00:00
|
|
|
|
{
|
|
|
|
|
item->_date = RETAIN(theFuture);
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2002-10-03 16:21:06 +00:00
|
|
|
|
{
|
|
|
|
|
item->_date = RETAIN(theFuture);
|
|
|
|
|
}
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIArrayInsertSorted(watchers, (GSIArrayItem)((id)item), aSort);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) _checkPerformers: (GSRunLoopCtxt*)context
|
|
|
|
|
{
|
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
GSIArray performers = context->performers;
|
|
|
|
|
unsigned count = GSIArrayCount(performers);
|
|
|
|
|
|
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopPerformer *array[count];
|
|
|
|
|
NSMapEnumerator enumerator;
|
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
void *mode;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copy the array - because we have to cancel the requests
|
|
|
|
|
* before firing.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
array[i] = RETAIN(GSIArrayItemAtIndex(performers, i).obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Remove the requests that we are about to fire from all modes.
|
|
|
|
|
*/
|
|
|
|
|
enumerator = NSEnumerateMapTable(_contextMap);
|
|
|
|
|
while (NSNextMapEnumeratorPair(&enumerator, &mode, (void**)&context))
|
|
|
|
|
{
|
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
GSIArray performers = context->performers;
|
|
|
|
|
unsigned tmpCount = GSIArrayCount(performers);
|
|
|
|
|
|
|
|
|
|
while (tmpCount--)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopPerformer *p;
|
|
|
|
|
|
|
|
|
|
p = GSIArrayItemAtIndex(performers, tmpCount).obj;
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (p == array[i])
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(performers, tmpCount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-05-28 05:23:36 +00:00
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Finally, fire the requests.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
[array[i] fire];
|
|
|
|
|
RELEASE(array[i]);
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Locates a runloop watcher matching the specified data and type in this
|
|
|
|
|
* runloop. If the mode is nil, either the currentMode is used (if the
|
|
|
|
|
* loop is running) or NSDefaultRunLoopMode is used.
|
|
|
|
|
*/
|
2001-05-10 10:15:38 +00:00
|
|
|
|
- (GSRunLoopWatcher*) _getWatcher: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
2001-05-10 10:15:38 +00:00
|
|
|
|
|
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
mode = [self currentMode];
|
2002-01-16 11:38:46 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
2001-05-10 10:15:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context != nil)
|
2001-05-10 10:15:38 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray watchers = context->watchers;
|
|
|
|
|
unsigned i = GSIArrayCount(watchers);
|
2001-05-10 10:15:38 +00:00
|
|
|
|
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *info;
|
|
|
|
|
|
|
|
|
|
info = GSIArrayItemAtIndex(watchers, i).obj;
|
|
|
|
|
if (info->type == type && info->data == data)
|
|
|
|
|
{
|
|
|
|
|
return info;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Removes a runloop watcher matching the specified data and type in this
|
|
|
|
|
* runloop. If the mode is nil, either the currentMode is used (if the
|
|
|
|
|
* loop is running) or NSDefaultRunLoopMode is used.
|
|
|
|
|
*/
|
2001-05-10 10:15:38 +00:00
|
|
|
|
- (void) _removeWatcher: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
2001-05-10 10:15:38 +00:00
|
|
|
|
|
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
mode = [self currentMode];
|
2002-01-16 11:38:46 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
2001-05-10 10:15:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context != nil)
|
2001-05-10 10:15:38 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray watchers = context->watchers;
|
2001-05-10 10:15:38 +00:00
|
|
|
|
unsigned i = GSIArrayCount(watchers);
|
|
|
|
|
|
|
|
|
|
while (i-- > 0)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *info;
|
|
|
|
|
|
|
|
|
|
info = GSIArrayItemAtIndex(watchers, i).obj;
|
|
|
|
|
if (info->type == type && info->data == data)
|
|
|
|
|
{
|
|
|
|
|
info->_invalidated = YES;
|
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
|
|
|
|
@implementation NSRunLoop(GNUstepExtensions)
|
|
|
|
|
|
|
|
|
|
- (void) addEvent: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
watcher: (id<RunLoopEvents>)watcher
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
GSRunLoopWatcher *info;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
mode = [self currentMode];
|
2002-01-16 11:38:46 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
info = [self _getWatcher: data type: type forMode: mode];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2003-07-28 16:44:24 +00:00
|
|
|
|
if (info != nil && (id)info->receiver == (id)watcher)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
|
|
|
|
/* Increment usage count for this watcher. */
|
1998-11-12 08:41:44 +00:00
|
|
|
|
info->count++;
|
1997-09-09 15:30:24 +00:00
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Remove any existing handler for another watcher. */
|
|
|
|
|
[self _removeWatcher: data type: type forMode: mode];
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/* Create new object to hold information. */
|
|
|
|
|
info = [[GSRunLoopWatcher alloc] initWithType: type
|
|
|
|
|
receiver: watcher
|
|
|
|
|
data: data];
|
|
|
|
|
/* Add the object to the array for the mode. */
|
|
|
|
|
[self _addWatcher: info forMode: mode];
|
|
|
|
|
RELEASE(info); /* Now held in array. */
|
|
|
|
|
}
|
2002-01-30 08:57:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
- (void) removeEvent: (void*)data
|
|
|
|
|
type: (RunLoopEventType)type
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
all: (BOOL)removeAll
|
2002-01-30 08:57:00 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = [self currentMode];
|
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (removeAll)
|
|
|
|
|
{
|
|
|
|
|
[self _removeWatcher: data type: type forMode: mode];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopWatcher *info;
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
info = [self _getWatcher: data type: type forMode: mode];
|
2005-02-22 11:22:44 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (info)
|
|
|
|
|
{
|
|
|
|
|
if (info->count == 0)
|
|
|
|
|
{
|
|
|
|
|
[self _removeWatcher: data type: type forMode: mode];
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
info->count--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-01-30 08:57:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
@end
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
|
extern SEL wRelSel;
|
|
|
|
|
extern SEL wRetSel;
|
|
|
|
|
extern IMP wRelImp;
|
|
|
|
|
extern IMP wRetImp;
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* <p><code>NSRunLoop</code> instances handle various utility tasks that must
|
|
|
|
|
* be performed repetitively in an application, such as processing input
|
|
|
|
|
* events, listening for distributed objects communications, firing
|
|
|
|
|
* [NSTimer]s, and sending notifications and other messages
|
|
|
|
|
* asynchronously.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>In general, there is one run loop per thread in an application, which
|
|
|
|
|
* may always be obtained through the <code>+currentRunLoop</code> method,
|
|
|
|
|
* however unless you are using the AppKit and the [NSApplication] class, the
|
|
|
|
|
* run loop will not be started unless you explicitly send it a
|
|
|
|
|
* <code>-run</code> message.</p>
|
|
|
|
|
*
|
|
|
|
|
* <p>At any given point, a run loop operates in a single <em>mode</em>, usually
|
|
|
|
|
* <code>NSDefaultRunLoopMode</code>. Other options include
|
|
|
|
|
* <code>NSConnectionReplyMode</code>, and certain modes used by the AppKit.</p>
|
|
|
|
|
*/
|
2002-01-30 15:28:50 +00:00
|
|
|
|
@implementation NSRunLoop
|
2002-01-30 08:57:00 +00:00
|
|
|
|
|
1999-04-19 14:29:52 +00:00
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if (self == [NSRunLoop class])
|
|
|
|
|
{
|
|
|
|
|
[self currentRunLoop];
|
|
|
|
|
theFuture = RETAIN([NSDate distantFuture]);
|
2000-10-30 18:00:27 +00:00
|
|
|
|
eventSel = @selector(receivedEvent:type:extra:forMode:);
|
1999-04-19 14:29:52 +00:00
|
|
|
|
#if GS_WITH_GC == 0
|
2000-10-30 18:00:27 +00:00
|
|
|
|
wRelSel = @selector(release);
|
|
|
|
|
wRetSel = @selector(retain);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
wRelImp = [[GSRunLoopWatcher class] instanceMethodForSelector: wRelSel];
|
|
|
|
|
wRetImp = [[GSRunLoopWatcher class] instanceMethodForSelector: wRetSel];
|
1999-04-19 14:29:52 +00:00
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns the run loop instance for the current thread.
|
|
|
|
|
*/
|
1999-03-02 08:58:30 +00:00
|
|
|
|
+ (NSRunLoop*) currentRunLoop
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2003-04-17 06:20:17 +00:00
|
|
|
|
extern NSRunLoop *GSRunLoopForThread();
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-10-30 07:45:59 +00:00
|
|
|
|
return GSRunLoopForThread(nil);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1996-04-10 18:20:36 +00:00
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
/* This is the designated initializer. */
|
1999-03-02 08:58:30 +00:00
|
|
|
|
- (id) init
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
self = [super init];
|
|
|
|
|
if (self != nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_contextStack = [NSMutableArray new];
|
|
|
|
|
_contextMap = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
|
|
|
|
NSObjectMapValueCallBacks, 0);
|
2002-01-16 14:00:59 +00:00
|
|
|
|
_timedPerformers = [[NSMutableArray alloc] initWithCapacity: 8];
|
2005-02-23 22:17:54 +00:00
|
|
|
|
#ifdef HAVE_POLL_F
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_extra = objc_malloc(sizeof(pollextra));
|
|
|
|
|
memset(_extra, '\0', sizeof(pollextra));
|
|
|
|
|
#endif
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
return self;
|
1996-04-10 18:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) dealloc
|
1999-03-02 08:58:30 +00:00
|
|
|
|
{
|
|
|
|
|
[self gcFinalize];
|
|
|
|
|
[super dealloc];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) gcFinalize
|
1996-04-10 18:20:36 +00:00
|
|
|
|
{
|
2005-02-23 22:17:54 +00:00
|
|
|
|
#ifdef HAVE_POLL_F
|
2002-01-31 22:28:27 +00:00
|
|
|
|
if (_extra != 0)
|
|
|
|
|
{
|
|
|
|
|
pollextra *e = (pollextra*)_extra;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|
2002-01-31 22:28:27 +00:00
|
|
|
|
if (e->index != 0)
|
|
|
|
|
objc_free(e->index);
|
|
|
|
|
objc_free(e);
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
#endif
|
|
|
|
|
RELEASE(_contextStack);
|
2002-01-31 22:28:27 +00:00
|
|
|
|
if (_contextMap != 0)
|
|
|
|
|
{
|
|
|
|
|
NSFreeMapTable(_contextMap);
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
RELEASE(_timedPerformers);
|
1996-04-10 18:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns the current mode of this runloop. If the runloop is not running
|
|
|
|
|
* then this method returns nil.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (NSString*) currentMode
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
return _currentMode;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-08-27 13:40:42 +00:00
|
|
|
|
/**
|
|
|
|
|
* Adds a timer to the loop in the specified mode.<br />
|
|
|
|
|
* Timers are removed automatically when they are invalid.<br />
|
|
|
|
|
*/
|
2001-12-17 14:31:42 +00:00
|
|
|
|
- (void) addTimer: (NSTimer*)timer
|
1997-09-01 21:59:51 +00:00
|
|
|
|
forMode: (NSString*)mode
|
1996-04-10 18:20:36 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
GSIArray timers;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context == nil)
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = [[GSRunLoopCtxt alloc] initWithMode: mode extra: _extra];
|
|
|
|
|
NSMapInsert(_contextMap, context->mode, context);
|
|
|
|
|
RELEASE(context);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
timers = context->timers;
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIArrayInsertSorted(timers, (GSIArrayItem)((id)timer), aSort);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
/**
|
2003-10-28 11:10:23 +00:00
|
|
|
|
* Fires timers whose fire date has passed, and checks timers and limit dates
|
|
|
|
|
* for input sources, determining the earliest time that anything watched for
|
|
|
|
|
* becomes useless. Returns that date/time.
|
2002-01-16 14:00:59 +00:00
|
|
|
|
*/
|
1999-04-21 20:16:25 +00:00
|
|
|
|
- (NSDate*) limitDateForMode: (NSString*)mode
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2004-11-19 09:41:34 +00:00
|
|
|
|
extern NSTimer *GSHousekeeper(void);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context = NSMapGet(_contextMap, mode);
|
|
|
|
|
NSDate *when = nil;
|
2002-01-28 15:16:33 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (context != nil)
|
2002-01-30 08:57:00 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopWatcher *min_watcher = nil;
|
|
|
|
|
NSString *savedMode = _currentMode;
|
|
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
|
|
|
|
|
|
|
|
|
_currentMode = mode;
|
|
|
|
|
NS_DURING
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray timers = context->timers;
|
|
|
|
|
GSIArray watchers = context->watchers;
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
while (GSIArrayCount(timers) != 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2004-11-19 09:41:34 +00:00
|
|
|
|
NSTimer *min_timer = GSIArrayItemAtIndex(timers, 0).obj;
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
if (timerInvalidated(min_timer) == YES)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(timers, 0);
|
|
|
|
|
min_timer = nil;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2004-11-19 09:41:34 +00:00
|
|
|
|
if (when == nil)
|
|
|
|
|
{
|
|
|
|
|
when = [timerDate(min_timer) copy];
|
|
|
|
|
}
|
2002-01-16 14:00:59 +00:00
|
|
|
|
if ([timerDate(min_timer) timeIntervalSinceNow] > 0)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSIArrayRemoveItemAtIndexNoRelease(timers, 0);
|
|
|
|
|
/* Firing will also increment its fireDate, if it is repeating. */
|
|
|
|
|
[min_timer fire];
|
|
|
|
|
if (timerInvalidated(min_timer) == NO)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayInsertSortedNoRetain(timers,
|
2005-10-07 09:57:51 +00:00
|
|
|
|
(GSIArrayItem)((id)min_timer), aSort);
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RELEASE(min_timer);
|
|
|
|
|
}
|
|
|
|
|
GSNotifyASAP(); /* Post notifications. */
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/* Is this right? At the moment we invalidate and discard watchers
|
|
|
|
|
whose limit-dates have passed. */
|
2002-01-16 14:00:59 +00:00
|
|
|
|
while (GSIArrayCount(watchers) != 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
min_watcher = GSIArrayItemAtIndex(watchers, 0).obj;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
if (min_watcher->_invalidated == YES)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSIArrayRemoveItemAtIndex(watchers, 0);
|
|
|
|
|
min_watcher = nil;
|
|
|
|
|
continue;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2002-01-16 14:00:59 +00:00
|
|
|
|
|
|
|
|
|
if ([min_watcher->_date timeIntervalSinceNow] > 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
break;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1998-10-29 08:46:30 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
id obj;
|
|
|
|
|
NSDate *nxt = nil;
|
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
/*
|
2002-01-16 14:00:59 +00:00
|
|
|
|
* If the receiver or its delegate wants to know about
|
|
|
|
|
* timeouts - inform it and give it a chance to set a
|
|
|
|
|
* revised limit date.
|
1998-10-29 08:46:30 +00:00
|
|
|
|
*/
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSIArrayRemoveItemAtIndexNoRelease(watchers, 0);
|
|
|
|
|
obj = min_watcher->receiver;
|
2005-02-22 11:22:44 +00:00
|
|
|
|
if ([obj respondsToSelector:
|
2002-01-16 14:00:59 +00:00
|
|
|
|
@selector(timedOutEvent:type:forMode:)])
|
|
|
|
|
{
|
|
|
|
|
nxt = [obj timedOutEvent: min_watcher->data
|
|
|
|
|
type: min_watcher->type
|
2002-01-30 15:28:50 +00:00
|
|
|
|
forMode: mode];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
else if ([obj respondsToSelector: @selector(delegate)])
|
|
|
|
|
{
|
|
|
|
|
obj = [obj delegate];
|
2005-02-22 11:22:44 +00:00
|
|
|
|
if (obj != nil && [obj respondsToSelector:
|
2002-01-16 14:00:59 +00:00
|
|
|
|
@selector(timedOutEvent:type:forMode:)])
|
|
|
|
|
{
|
|
|
|
|
nxt = [obj timedOutEvent: min_watcher->data
|
|
|
|
|
type: min_watcher->type
|
2002-01-30 15:28:50 +00:00
|
|
|
|
forMode: mode];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (nxt && [nxt timeIntervalSinceNow] > 0.0)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If the watcher has been given a revised limit date -
|
|
|
|
|
* re-insert it into the queue in the correct place.
|
|
|
|
|
*/
|
|
|
|
|
ASSIGN(min_watcher->_date, nxt);
|
|
|
|
|
GSIArrayInsertSortedNoRetain(watchers,
|
2005-10-07 09:57:51 +00:00
|
|
|
|
(GSIArrayItem)((id)min_watcher), aSort);
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If the watcher is now useless - invalidate and
|
|
|
|
|
* release it.
|
|
|
|
|
*/
|
|
|
|
|
min_watcher->_invalidated = YES;
|
|
|
|
|
RELEASE(min_watcher);
|
|
|
|
|
}
|
|
|
|
|
min_watcher = nil;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2003-04-17 06:20:17 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If there is nothing being watched, and no valid timers
|
2004-11-05 03:52:22 +00:00
|
|
|
|
* other than the housekeeper, we set when to nil so
|
2003-04-17 06:20:17 +00:00
|
|
|
|
* that the housekeeper timer does not keep the runloop
|
|
|
|
|
* active. It's a special case set up in NSThread.m
|
|
|
|
|
*/
|
2004-11-19 09:41:34 +00:00
|
|
|
|
if (min_watcher == nil && when != nil)
|
2003-04-17 06:20:17 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned count = GSIArrayCount(timers);
|
|
|
|
|
|
|
|
|
|
while (count-- > 1)
|
|
|
|
|
{
|
|
|
|
|
NSTimer *tmp = GSIArrayItemAtIndex(timers, 0).obj;
|
2004-11-19 09:41:34 +00:00
|
|
|
|
|
2003-04-17 06:20:17 +00:00
|
|
|
|
if (timerInvalidated(tmp) == YES)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(timers, count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (GSIArrayCount(timers) == 1)
|
|
|
|
|
{
|
2004-11-19 09:41:34 +00:00
|
|
|
|
DESTROY(when);
|
2003-04-17 06:20:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_currentMode = savedMode;
|
2002-01-29 10:27:41 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
|
|
|
|
_currentMode = savedMode;
|
|
|
|
|
[localException raise];
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
RELEASE(arp);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
/*
|
2004-11-05 03:52:22 +00:00
|
|
|
|
* If there are timers, when is already set to the limit date of the
|
|
|
|
|
* earliest of them (and retained!).
|
2002-01-30 15:28:50 +00:00
|
|
|
|
* If there are watchers, set the limit date to that of the earliest
|
|
|
|
|
* watcher (or leave it as the date of the earliest timer if that is
|
|
|
|
|
* before the watchers limit).
|
|
|
|
|
*/
|
2004-11-19 09:41:34 +00:00
|
|
|
|
if (when != nil)
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (min_watcher != nil
|
|
|
|
|
&& [min_watcher->_date compare: when] == NSOrderedAscending)
|
|
|
|
|
{
|
2004-11-05 03:52:22 +00:00
|
|
|
|
RELEASE(when);
|
2002-01-30 15:28:50 +00:00
|
|
|
|
when = min_watcher->_date;
|
|
|
|
|
}
|
2004-11-05 03:52:22 +00:00
|
|
|
|
else
|
2004-11-19 09:41:34 +00:00
|
|
|
|
{
|
|
|
|
|
AUTORELEASE(when);
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
}
|
|
|
|
|
else if (min_watcher != nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
1999-04-20 16:28:04 +00:00
|
|
|
|
when = min_watcher->_date;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
2005-10-11 19:09:26 +00:00
|
|
|
|
#if defined(__MINGW32__)
|
2005-02-23 16:05:09 +00:00
|
|
|
|
// if there are handler for win32 messages
|
|
|
|
|
else if (context->msgTarget != nil)
|
|
|
|
|
{
|
|
|
|
|
when = theFuture;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2002-01-30 15:28:50 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return nil; /* Nothing waiting to be done. */
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"limit date %f",
|
2002-01-30 08:57:00 +00:00
|
|
|
|
[when timeIntervalSinceReferenceDate]);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
1999-04-22 11:24:57 +00:00
|
|
|
|
return when;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
2003-10-28 11:10:23 +00:00
|
|
|
|
* Listen for events from input sources.<br />
|
2002-02-20 06:42:05 +00:00
|
|
|
|
* If limit_date is nil or in the past, then don't wait;
|
|
|
|
|
* just poll inputs and return,
|
|
|
|
|
* otherwise block until input is available or until the
|
|
|
|
|
* earliest limit date has passed (whichever comes first).<br />
|
2002-01-16 11:38:46 +00:00
|
|
|
|
* If the supplied mode is nil, uses NSDefaultRunLoopMode.
|
|
|
|
|
*/
|
2005-02-22 11:22:44 +00:00
|
|
|
|
- (void) acceptInputForMode: (NSString*)mode
|
2001-12-17 14:31:42 +00:00
|
|
|
|
beforeDate: (NSDate*)limit_date
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
NSTimeInterval ti;
|
|
|
|
|
int timeout_ms;
|
|
|
|
|
NSString *savedMode = _currentMode;
|
1999-09-28 19:35:09 +00:00
|
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-11-19 21:26:27 +00:00
|
|
|
|
NSAssert(mode, NSInvalidArgumentException);
|
2002-01-16 11:38:46 +00:00
|
|
|
|
if (mode == nil)
|
|
|
|
|
{
|
|
|
|
|
mode = NSDefaultRunLoopMode;
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_currentMode = mode;
|
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-03-07 16:49:32 +00:00
|
|
|
|
[self _checkPerformers: context];
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
NS_DURING
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray watchers;
|
|
|
|
|
unsigned i;
|
|
|
|
|
|
2005-02-23 16:05:09 +00:00
|
|
|
|
if ((context == nil || (watchers = context->watchers) == 0
|
2002-01-30 15:28:50 +00:00
|
|
|
|
|| (i = GSIArrayCount(watchers)) == 0)
|
2005-10-11 19:09:26 +00:00
|
|
|
|
#if defined(__MINGW32__)
|
2005-02-23 16:05:09 +00:00
|
|
|
|
// there are inputs for win32 messages
|
|
|
|
|
&& context->msgTarget == 0)
|
|
|
|
|
#else
|
|
|
|
|
)
|
|
|
|
|
#endif
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
2002-02-20 06:42:05 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"no inputs in mode %@", mode);
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
GSNotifyIdle();
|
|
|
|
|
/*
|
|
|
|
|
* Pause for as long as possible (up to the limit date)
|
|
|
|
|
*/
|
2003-07-17 09:00:31 +00:00
|
|
|
|
[NSThread sleepUntilDate: limit_date];
|
|
|
|
|
ti = [limit_date timeIntervalSinceNow];
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSCheckTasks();
|
2002-02-20 06:42:05 +00:00
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
[self _checkPerformers: context];
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
_currentMode = savedMode;
|
|
|
|
|
RELEASE(arp);
|
|
|
|
|
NS_VOIDRETURN;
|
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 14:00:59 +00:00
|
|
|
|
/* Find out how much time we should wait, and set SELECT_TIMEOUT. */
|
2003-10-28 11:10:23 +00:00
|
|
|
|
if (limit_date == nil
|
|
|
|
|
|| (ti = [limit_date timeIntervalSinceNow]) <= 0.0)
|
2002-01-16 14:00:59 +00:00
|
|
|
|
{
|
|
|
|
|
/* Don't wait at all. */
|
2002-01-30 15:28:50 +00:00
|
|
|
|
timeout_ms = 0;
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
2003-10-28 11:10:23 +00:00
|
|
|
|
else
|
2002-01-16 14:00:59 +00:00
|
|
|
|
{
|
|
|
|
|
/* Wait until the LIMIT_DATE. */
|
2005-02-22 11:22:44 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"accept I/P before %f (sec from now %f)",
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[limit_date timeIntervalSinceReferenceDate], ti);
|
|
|
|
|
if (ti >= INT_MAX / 1000)
|
|
|
|
|
{
|
|
|
|
|
timeout_ms = INT_MAX; // Far future.
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
timeout_ms = ti * 1000;
|
|
|
|
|
}
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
2000-03-28 13:02:01 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if ([_contextStack indexOfObjectIdenticalTo: context] == NSNotFound)
|
2002-01-29 10:27:41 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[_contextStack addObject: context];
|
2002-01-29 10:27:41 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if ([context pollUntil: timeout_ms within: _contextStack] == NO)
|
2001-01-18 11:58:19 +00:00
|
|
|
|
{
|
2002-01-16 14:00:59 +00:00
|
|
|
|
GSNotifyIdle();
|
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[self _checkPerformers: context];
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
_currentMode = savedMode;
|
2002-01-29 10:27:41 +00:00
|
|
|
|
/*
|
2002-01-30 15:28:50 +00:00
|
|
|
|
* Once a poll has been completed on a context, we can remove that
|
|
|
|
|
* context from the stack even if it actually polling at an outer
|
|
|
|
|
* level of re-entrancy ... since the poll we have just done will
|
|
|
|
|
* have handled any events that the outer levels would have wanted
|
|
|
|
|
* to handle, and the polling for this context will be marked as ended.
|
2002-01-29 10:27:41 +00:00
|
|
|
|
*/
|
2002-01-30 15:28:50 +00:00
|
|
|
|
[context endPoll];
|
|
|
|
|
[_contextStack removeObjectIdenticalTo: context];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
}
|
|
|
|
|
NS_HANDLER
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
_currentMode = savedMode;
|
|
|
|
|
[context endPoll];
|
|
|
|
|
[_contextStack removeObjectIdenticalTo: context];
|
2002-01-16 14:00:59 +00:00
|
|
|
|
[localException raise];
|
|
|
|
|
}
|
|
|
|
|
NS_ENDHANDLER
|
1999-06-25 10:22:55 +00:00
|
|
|
|
RELEASE(arp);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-01-16 11:38:46 +00:00
|
|
|
|
/**
|
|
|
|
|
* Calls -acceptInputForMode:beforeDate: to run the loop once.<br />
|
2002-10-03 16:21:06 +00:00
|
|
|
|
* The specified date may be nil ... in which case the loop runs
|
2003-10-28 11:10:23 +00:00
|
|
|
|
* until the limit date of the first input or timeout.<br />
|
|
|
|
|
* If the specified date is in the past, runs the loop once only, to
|
|
|
|
|
* handle any events already available.<br />
|
|
|
|
|
* If there are no input sources in mode, returns NO without running the loop,
|
|
|
|
|
* otherwise returns YES.
|
2002-01-16 11:38:46 +00:00
|
|
|
|
*/
|
2001-06-21 13:36:13 +00:00
|
|
|
|
- (BOOL) runMode: (NSString*)mode beforeDate: (NSDate*)date
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2004-11-19 09:41:34 +00:00
|
|
|
|
CREATE_AUTORELEASE_POOL(arp);
|
|
|
|
|
NSDate *d;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-10-03 16:21:06 +00:00
|
|
|
|
NSAssert(mode != nil, NSInvalidArgumentException);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
/* Find out how long we can wait before first limit date. */
|
|
|
|
|
d = [self limitDateForMode: mode];
|
|
|
|
|
if (d == nil)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"run mode with nothing to do");
|
2001-06-21 04:49:20 +00:00
|
|
|
|
/*
|
|
|
|
|
* Notify if any tasks have completed.
|
|
|
|
|
*/
|
|
|
|
|
if (GSCheckTasks() == YES)
|
|
|
|
|
{
|
|
|
|
|
GSNotifyASAP();
|
|
|
|
|
}
|
2004-11-19 09:41:34 +00:00
|
|
|
|
RELEASE(arp);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return NO;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-06-25 10:22:55 +00:00
|
|
|
|
/*
|
|
|
|
|
* Use the earlier of the two dates we have.
|
|
|
|
|
* Retain the date in case the firing of a timer (or some other event)
|
|
|
|
|
* releases it.
|
|
|
|
|
*/
|
2002-10-03 16:21:06 +00:00
|
|
|
|
if (date != nil)
|
|
|
|
|
{
|
|
|
|
|
d = [d earlierDate: date];
|
|
|
|
|
}
|
1999-09-28 19:35:09 +00:00
|
|
|
|
IF_NO_GC(RETAIN(d));
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1998-10-29 08:46:30 +00:00
|
|
|
|
/* Wait, listening to our input sources. */
|
|
|
|
|
[self acceptInputForMode: mode beforeDate: d];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1999-04-22 11:24:57 +00:00
|
|
|
|
RELEASE(d);
|
2004-11-19 09:41:34 +00:00
|
|
|
|
RELEASE(arp);
|
1998-10-29 08:46:30 +00:00
|
|
|
|
return YES;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Runs the loop in <code>NSDefaultRunLoopMode</code> by repeated calls to
|
|
|
|
|
* -runMode:beforeDate: while there are still input sources. Exits when no
|
|
|
|
|
* more input sources remain.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) run
|
|
|
|
|
{
|
1999-04-19 14:29:52 +00:00
|
|
|
|
[self runUntilDate: theFuture];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Runs the loop in <code>NSDefaultRunLoopMode</code> by repeated calls to
|
|
|
|
|
* -runMode:beforeDate: while there are still input sources. Exits when no
|
|
|
|
|
* more input sources remain, or date is reached, whichever occurs first.
|
|
|
|
|
*/
|
2001-06-21 13:36:13 +00:00
|
|
|
|
- (void) runUntilDate: (NSDate*)date
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
2001-12-17 14:31:42 +00:00
|
|
|
|
double ti = [date timeIntervalSinceNow];
|
|
|
|
|
BOOL mayDoMore = YES;
|
|
|
|
|
|
|
|
|
|
/* Positive values are in the future. */
|
|
|
|
|
while (ti > 0 && mayDoMore == YES)
|
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
NSDebugMLLog(@"NSRunLoop", @"run until date %f seconds from now", ti);
|
2001-12-17 14:31:42 +00:00
|
|
|
|
mayDoMore = [self runMode: NSDefaultRunLoopMode beforeDate: date];
|
|
|
|
|
ti = [date timeIntervalSinceNow];
|
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* OpenStep-compatibility methods for [NSRunLoop]. These methods are also
|
|
|
|
|
* all in OS X.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
@implementation NSRunLoop (OPENSTEP)
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Adds port to be monitored in given mode.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) addPort: (NSPort*)port
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2003-08-20 12:13:34 +00:00
|
|
|
|
[self addEvent: (void*)port
|
|
|
|
|
type: ET_RPORT
|
|
|
|
|
watcher: (id<RunLoopEvents>)port
|
|
|
|
|
forMode: (NSString*)mode];
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-08-27 14:24:54 +00:00
|
|
|
|
/**
|
|
|
|
|
* Cancels any perform operations set up for the specified target
|
|
|
|
|
* in the receiver.
|
|
|
|
|
*/
|
|
|
|
|
- (void) cancelPerformSelectorsWithTarget: (id) target
|
|
|
|
|
{
|
|
|
|
|
NSMapEnumerator enumerator;
|
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
void *mode;
|
|
|
|
|
|
|
|
|
|
enumerator = NSEnumerateMapTable(_contextMap);
|
|
|
|
|
|
|
|
|
|
while (NSNextMapEnumeratorPair(&enumerator, &mode, (void**)&context))
|
|
|
|
|
{
|
|
|
|
|
if (context != nil)
|
|
|
|
|
{
|
|
|
|
|
GSIArray performers = context->performers;
|
|
|
|
|
unsigned count = GSIArrayCount(performers);
|
|
|
|
|
|
|
|
|
|
while (count--)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopPerformer *p;
|
|
|
|
|
|
|
|
|
|
p = GSIArrayItemAtIndex(performers, count).obj;
|
|
|
|
|
if (p->target == target)
|
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(performers, count);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Cancels any perform operations set up for the specified target
|
2002-08-27 15:04:44 +00:00
|
|
|
|
* in the receiver, but only if the value of aSelector and argument
|
2003-01-31 19:06:51 +00:00
|
|
|
|
* with which the performs were set up match those supplied.<br />
|
|
|
|
|
* Matching of the argument may be either by pointer equality or by
|
|
|
|
|
* use of the [NSObject-isEqual:] method.
|
2002-08-27 14:24:54 +00:00
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) cancelPerformSelector: (SEL)aSelector
|
1999-09-14 10:03:02 +00:00
|
|
|
|
target: (id) target
|
|
|
|
|
argument: (id) argument
|
1997-09-01 21:59:51 +00:00
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
NSMapEnumerator enumerator;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
void *mode;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
enumerator = NSEnumerateMapTable(_contextMap);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
while (NSNextMapEnumeratorPair(&enumerator, &mode, (void**)&context))
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
if (context != nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSIArray performers = context->performers;
|
|
|
|
|
unsigned count = GSIArrayCount(performers);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
while (count--)
|
1999-09-14 10:03:02 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopPerformer *p;
|
|
|
|
|
|
|
|
|
|
p = GSIArrayItemAtIndex(performers, count).obj;
|
|
|
|
|
if (p->target == target && sel_eq(p->selector, aSelector)
|
2003-01-31 19:06:51 +00:00
|
|
|
|
&& (p->argument == argument || [p->argument isEqual: argument]))
|
2002-01-30 15:28:50 +00:00
|
|
|
|
{
|
|
|
|
|
GSIArrayRemoveItemAtIndex(performers, count);
|
|
|
|
|
}
|
1999-09-14 10:03:02 +00:00
|
|
|
|
}
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2002-05-28 05:23:36 +00:00
|
|
|
|
NSEndMapTableEnumeration(&enumerator);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Configure event processing for acting as a server process for distributed
|
|
|
|
|
* objects. (In the current implementation this is a no-op.)
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) configureAsServer
|
|
|
|
|
{
|
|
|
|
|
/* Nothing to do here */
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-30 07:45:59 +00:00
|
|
|
|
/**
|
2003-02-15 14:58:25 +00:00
|
|
|
|
* Sets up sending of aSelector to target with argument.<br />
|
|
|
|
|
* The selector is sent before the next runloop iteration (unless
|
|
|
|
|
* cancelled before then).<br />
|
2002-10-30 07:45:59 +00:00
|
|
|
|
* The target and argument objects are <em>not</em> retained.<br />
|
|
|
|
|
* The order value is used to determine the order in which messages
|
2003-02-15 14:58:25 +00:00
|
|
|
|
* are sent if multiple messages have been set up. Messages with a lower
|
|
|
|
|
* order value are sent first.
|
2002-10-30 07:45:59 +00:00
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) performSelector: (SEL)aSelector
|
1999-09-14 10:03:02 +00:00
|
|
|
|
target: (id)target
|
|
|
|
|
argument: (id)argument
|
1997-09-01 21:59:51 +00:00
|
|
|
|
order: (unsigned int)order
|
|
|
|
|
modes: (NSArray*)modes
|
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
unsigned count = [modes count];
|
1998-10-29 08:46:30 +00:00
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
if (count > 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
NSString *array[count];
|
|
|
|
|
GSRunLoopPerformer *item;
|
1997-09-01 21:59:51 +00:00
|
|
|
|
|
1999-09-14 13:50:24 +00:00
|
|
|
|
item = [[GSRunLoopPerformer alloc] initWithSelector: aSelector
|
|
|
|
|
target: target
|
|
|
|
|
argument: argument
|
|
|
|
|
order: order];
|
|
|
|
|
|
|
|
|
|
[modes getObjects: array];
|
|
|
|
|
while (count-- > 0)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
1999-09-14 13:50:24 +00:00
|
|
|
|
NSString *mode = array[count];
|
|
|
|
|
unsigned end;
|
|
|
|
|
unsigned i;
|
2002-01-30 15:28:50 +00:00
|
|
|
|
GSRunLoopCtxt *context;
|
|
|
|
|
GSIArray performers;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = NSMapGet(_contextMap, mode);
|
|
|
|
|
if (context == nil)
|
1998-10-29 08:46:30 +00:00
|
|
|
|
{
|
2002-01-30 15:28:50 +00:00
|
|
|
|
context = [[GSRunLoopCtxt alloc] initWithMode: mode
|
|
|
|
|
extra: _extra];
|
|
|
|
|
NSMapInsert(_contextMap, context->mode, context);
|
|
|
|
|
RELEASE(context);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
}
|
2002-01-30 15:28:50 +00:00
|
|
|
|
performers = context->performers;
|
1999-09-14 13:50:24 +00:00
|
|
|
|
|
|
|
|
|
end = GSIArrayCount(performers);
|
|
|
|
|
for (i = 0; i < end; i++)
|
|
|
|
|
{
|
|
|
|
|
GSRunLoopPerformer *p;
|
|
|
|
|
|
|
|
|
|
p = GSIArrayItemAtIndex(performers, i).obj;
|
2003-02-15 14:58:25 +00:00
|
|
|
|
if (p->order > order)
|
1999-09-14 13:50:24 +00:00
|
|
|
|
{
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIArrayInsertItem(performers, (GSIArrayItem)((id)item), i);
|
1999-09-14 13:50:24 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i == end)
|
|
|
|
|
{
|
2005-10-07 09:57:51 +00:00
|
|
|
|
GSIArrayInsertItem(performers, (GSIArrayItem)((id)item), i);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-09-14 13:50:24 +00:00
|
|
|
|
RELEASE(item);
|
1997-09-01 21:59:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-06-22 22:40:40 +00:00
|
|
|
|
/**
|
|
|
|
|
* Removes port to be monitored from given mode.
|
|
|
|
|
* Ports are also removed if they are detected to be invalid.
|
|
|
|
|
*/
|
1997-09-01 21:59:51 +00:00
|
|
|
|
- (void) removePort: (NSPort*)port
|
|
|
|
|
forMode: (NSString*)mode
|
|
|
|
|
{
|
2003-08-20 12:13:34 +00:00
|
|
|
|
[self removeEvent: (void*)port type: ET_RPORT forMode: mode all: NO];
|
1996-04-10 18:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|