1996-03-12 15:39:13 +00:00
|
|
|
|
/* Implementation of object for waiting on several input sources
|
|
|
|
|
Copyright (C) 1996 Free Software Foundation, Inc.
|
|
|
|
|
|
1996-04-17 20:17:45 +00:00
|
|
|
|
Written by: Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
|
1996-03-12 15:39:13 +00:00
|
|
|
|
Created: March 1996
|
|
|
|
|
|
1996-05-12 00:56:10 +00:00
|
|
|
|
This file is part of the GNUstep Base Library.
|
1996-03-12 15:39:13 +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.
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
|
License along with this library; if not, write to the Free
|
|
|
|
|
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
*/
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* This is the beginning of a RunLoop implementation.
|
|
|
|
|
It is still in the early stages of development, and will most likely
|
|
|
|
|
evolve quite a bit more before the interface settles.
|
|
|
|
|
|
|
|
|
|
Handling NSTimers is implemented, but currently disabled.
|
|
|
|
|
|
|
|
|
|
Does it strike anyone else that NSNotificationCenter,
|
|
|
|
|
NSNotificationQueue, NSNotification, NSRunLoop, the "notifications"
|
|
|
|
|
a run loop sends the objects on which it is listening, NSEvent, and
|
|
|
|
|
the event queue maintained by NSApplication are all much more
|
|
|
|
|
intertwined/similar than OpenStep gives them credit for?
|
|
|
|
|
|
|
|
|
|
I wonder if these classes could be re-organized a little to make a
|
|
|
|
|
more uniform, "grand-unified" mechanism for: events,
|
|
|
|
|
event-listening, event-queuing, and event-distributing. It could
|
|
|
|
|
be quite pretty.
|
|
|
|
|
|
|
|
|
|
(GNUstep would definitely provide classes that were compatible with
|
|
|
|
|
all these OpenStep classes, but those classes could be wrappers
|
|
|
|
|
around fundamentally cleaner GNU classes. RMS has advised using an
|
|
|
|
|
underlying organization/implementation different from NeXT's
|
|
|
|
|
whenever that makes sense---it helps legally distinguish our work.)
|
|
|
|
|
|
|
|
|
|
Thoughts and insights, anyone?
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Alternate names: InputDemuxer, InputListener, EventListener.
|
|
|
|
|
Alternate names for Notification classes: Dispatcher, EventDistributor, */
|
|
|
|
|
|
1996-04-17 15:34:35 +00:00
|
|
|
|
#include <gnustep/base/preface.h>
|
1996-04-17 15:23:00 +00:00
|
|
|
|
#include <gnustep/base/RunLoop.h>
|
|
|
|
|
#include <gnustep/base/Heap.h>
|
1996-03-12 15:39:13 +00:00
|
|
|
|
#include <Foundation/NSMapTable.h>
|
|
|
|
|
#include <Foundation/NSDate.h>
|
|
|
|
|
#include <Foundation/NSValue.h>
|
|
|
|
|
#include <Foundation/NSAutoreleasePool.h>
|
|
|
|
|
#include <Foundation/NSTimer.h>
|
1996-05-27 14:26:41 +00:00
|
|
|
|
#ifndef WIN32
|
1996-03-12 15:39:13 +00:00
|
|
|
|
#include <sys/time.h>
|
1996-05-27 14:26:41 +00:00
|
|
|
|
#endif /* !WIN32 */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
#include <limits.h>
|
1996-03-18 14:00:47 +00:00
|
|
|
|
#include <string.h> /* for memset() */
|
|
|
|
|
|
|
|
|
|
/* On some systems FD_ZERO is a macro that uses bzero().
|
|
|
|
|
Just define it to use memset(). */
|
|
|
|
|
#define bzero(PTR, LEN) memset (PTR, 0, LEN)
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
1996-03-29 14:47:38 +00:00
|
|
|
|
static int debug_run_loop = 0;
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
|
|
|
|
@implementation RunLoop
|
|
|
|
|
|
|
|
|
|
static RunLoop *current_run_loop;
|
|
|
|
|
|
|
|
|
|
+ (void) initialize
|
|
|
|
|
{
|
|
|
|
|
if (self == [RunLoop class])
|
|
|
|
|
current_run_loop = [self new];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This is the designated initializer. */
|
|
|
|
|
- init
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
[super init];
|
|
|
|
|
_current_mode = RunLoopDefaultMode;
|
|
|
|
|
_mode_2_timers = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
|
|
|
|
NSObjectMapValueCallBacks, 0);
|
|
|
|
|
_mode_2_in_ports = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
|
|
|
|
NSObjectMapValueCallBacks, 0);
|
|
|
|
|
_mode_2_fd_listeners = NSCreateMapTable (NSNonRetainedObjectMapKeyCallBacks,
|
|
|
|
|
NSObjectMapValueCallBacks, 0);
|
1996-03-12 15:39:13 +00:00
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
- (id <String>) currentMode
|
1996-03-12 15:39:13 +00:00
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
return _current_mode;
|
1996-03-12 15:39:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* Adding and removing file descriptors. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
|
|
|
|
- (void) addFileDescriptor: (int)fd
|
1996-03-18 19:32:45 +00:00
|
|
|
|
object: listener
|
1996-03-12 15:39:13 +00:00
|
|
|
|
forMode: (id <String>)mode
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* xxx Perhaps this should be a Bag instead. */
|
|
|
|
|
Array *fd_listeners;
|
|
|
|
|
|
|
|
|
|
/* xxx We need to keep track of the FD too!
|
|
|
|
|
Perhaps I'll make a FileDescriptor class to hold all this;
|
|
|
|
|
it will be more analogous the Port object. */
|
|
|
|
|
[self notImplemented: _cmd];
|
|
|
|
|
|
|
|
|
|
fd_listeners = NSMapGet (_mode_2_fd_listeners, mode);
|
|
|
|
|
if (!fd_listeners)
|
|
|
|
|
{
|
|
|
|
|
fd_listeners = [Array new];
|
|
|
|
|
NSMapInsert (_mode_2_fd_listeners, mode, fd_listeners);
|
|
|
|
|
[fd_listeners release];
|
|
|
|
|
}
|
|
|
|
|
[fd_listeners addObject: listener];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void) removeFileDescriptor: (int)fd
|
|
|
|
|
forMode: (id <String>)mode
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
#if 1
|
|
|
|
|
[self notImplemented: _cmd];
|
|
|
|
|
#else
|
|
|
|
|
Array *fd_listeners;
|
|
|
|
|
|
|
|
|
|
fd_listeners = NSMapGet (_mode_2_fd_listeners, mode);
|
|
|
|
|
if (!fd_listeners)
|
|
|
|
|
/* xxx Careful, this is only suppose to "undo" one -addPort:.
|
|
|
|
|
If we change the -removeObject implementation later to remove
|
|
|
|
|
all instances of port, we'll have to change this code here. */
|
|
|
|
|
[fd_listeners removeObject: port];
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Adding and removing port objects. */
|
|
|
|
|
|
|
|
|
|
- (void) addPort: port
|
|
|
|
|
forMode: (id <String>)mode
|
|
|
|
|
{
|
1996-03-18 20:29:34 +00:00
|
|
|
|
/* xxx Perhaps this should be a Bag instead; I think this currently works
|
|
|
|
|
when a port is added more than once, but it doesn't work prettily. */
|
1996-03-18 19:32:45 +00:00
|
|
|
|
Array *in_ports;
|
|
|
|
|
|
|
|
|
|
in_ports = NSMapGet (_mode_2_in_ports, mode);
|
|
|
|
|
if (!in_ports)
|
|
|
|
|
{
|
|
|
|
|
in_ports = [Array new];
|
|
|
|
|
NSMapInsert (_mode_2_in_ports, mode, in_ports);
|
|
|
|
|
[in_ports release];
|
|
|
|
|
}
|
|
|
|
|
[in_ports addObject: port];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
- (void) removePort: port
|
|
|
|
|
forMode: (id <String>)mode
|
|
|
|
|
{
|
|
|
|
|
/* xxx Perhaps this should be a Bag instead. */
|
|
|
|
|
Array *in_ports;
|
|
|
|
|
|
|
|
|
|
in_ports = NSMapGet (_mode_2_in_ports, mode);
|
|
|
|
|
if (in_ports)
|
|
|
|
|
/* xxx Careful, this is only suppose to "undo" one -addPort:.
|
|
|
|
|
If we change the -removeObject implementation later to remove
|
|
|
|
|
all instances of port, we'll have to change this code here. */
|
|
|
|
|
[in_ports removeObject: port];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Adding timers. They are removed when they are invalid. */
|
|
|
|
|
|
1996-03-12 15:39:13 +00:00
|
|
|
|
- (void) addTimer: timer
|
|
|
|
|
forMode: (id <String>)mode
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
Heap *timers;
|
|
|
|
|
|
|
|
|
|
timers = NSMapGet (_mode_2_timers, mode);
|
|
|
|
|
if (!timers)
|
|
|
|
|
{
|
|
|
|
|
timers = [Heap new];
|
|
|
|
|
NSMapInsert (_mode_2_timers, mode, timers);
|
|
|
|
|
[timers release];
|
|
|
|
|
}
|
|
|
|
|
/* xxx Should we make sure it isn't already there? */
|
|
|
|
|
[timers addObject: timer];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* Fire appropriate timers. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
|
|
|
|
- limitDateForMode: (id <String>)mode
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* Linux doesn't always return double from methods, even though
|
|
|
|
|
I'm using -lieee. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
#if 1
|
1996-03-18 20:29:34 +00:00
|
|
|
|
assert (mode);
|
1996-03-12 15:39:13 +00:00
|
|
|
|
return nil;
|
|
|
|
|
#else
|
1996-03-18 19:32:45 +00:00
|
|
|
|
Heap *timers;
|
1996-03-12 15:39:13 +00:00
|
|
|
|
NSTimer *min_timer = nil;
|
1996-03-18 19:32:45 +00:00
|
|
|
|
id saved_mode;
|
|
|
|
|
|
|
|
|
|
saved_mode = _current_mode;
|
|
|
|
|
_current_mode = mode;
|
|
|
|
|
|
|
|
|
|
timers = NSMapGet (_mode_2_timers, mode);
|
|
|
|
|
if (!timers)
|
|
|
|
|
return nil;
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
|
|
|
|
/* Does this properly handle timers that have been sent -invalidate? */
|
1996-03-18 19:32:45 +00:00
|
|
|
|
while ((min_timer = [timers minObject])
|
1996-03-12 15:39:13 +00:00
|
|
|
|
&& ([[min_timer fireDate] timeIntervalSinceNow] > 0))
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
[timers removeFirstObject];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
/* Firing will also increment its fireDate, if it is repeating. */
|
|
|
|
|
if ([min_timer isValid])
|
|
|
|
|
{
|
|
|
|
|
[min_timer fire];
|
|
|
|
|
if ([[min_timer fireDate] timeIntervalSinceNow] < 0)
|
1996-03-18 19:32:45 +00:00
|
|
|
|
[timers addObject: min_timer];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (debug_run_loop)
|
1996-03-19 00:59:05 +00:00
|
|
|
|
printf ("\tRunLoop limit date %f\n",
|
1996-03-12 15:39:13 +00:00
|
|
|
|
[[min_timer fireDate] timeIntervalSinceReferenceDate]);
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
|
|
|
|
_current_mode = saved_mode;
|
1996-03-12 15:39:13 +00:00
|
|
|
|
return [min_timer fireDate];
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
1996-03-19 17:10:41 +00:00
|
|
|
|
/* Listen to input sources.
|
|
|
|
|
If LIMIT_DATE is nil, then don't wait; i.e. call select() with 0 timeout */
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
1996-03-13 02:35:25 +00:00
|
|
|
|
- (void) acceptInputForMode: (id <String>)mode
|
|
|
|
|
beforeDate: limit_date
|
1996-03-12 15:39:13 +00:00
|
|
|
|
{
|
|
|
|
|
NSTimeInterval ti;
|
|
|
|
|
struct timeval timeout;
|
|
|
|
|
void *select_timeout;
|
1996-03-18 19:32:45 +00:00
|
|
|
|
fd_set fds; /* The file descriptors we will listen to. */
|
|
|
|
|
fd_set read_fds; /* Copy for listening to read-ready fds. */
|
|
|
|
|
fd_set exception_fds; /* Copy for listening to exception fds. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
int select_return;
|
|
|
|
|
int fd_index;
|
1996-03-18 19:32:45 +00:00
|
|
|
|
NSMapTable *fd_2_object;
|
|
|
|
|
id saved_mode;
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
1996-03-18 20:29:34 +00:00
|
|
|
|
assert (mode);
|
1996-03-18 19:32:45 +00:00
|
|
|
|
saved_mode = _current_mode;
|
|
|
|
|
_current_mode = mode;
|
|
|
|
|
|
|
|
|
|
/* xxx No, perhaps this isn't the right thing to do. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
#if 0
|
|
|
|
|
/* If there are no input sources to listen to, just return. */
|
|
|
|
|
if (NSCountMapTable (_fd_2_object) == 0)
|
|
|
|
|
return;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Find out how much time we should wait, and set SELECT_TIMEOUT. */
|
1996-03-19 17:10:41 +00:00
|
|
|
|
if (!limit_date)
|
1996-03-12 15:39:13 +00:00
|
|
|
|
{
|
1996-03-19 17:10:41 +00:00
|
|
|
|
/* Don't wait at all. */
|
|
|
|
|
timeout.tv_sec = 0;
|
|
|
|
|
timeout.tv_usec = 0;
|
|
|
|
|
select_timeout = &timeout;
|
|
|
|
|
}
|
|
|
|
|
else if ((ti = [limit_date timeIntervalSinceNow]) < LONG_MAX
|
|
|
|
|
&& ti > 0.0)
|
|
|
|
|
{
|
|
|
|
|
/* Wait until the LIMIT_DATE. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
if (debug_run_loop)
|
1996-03-19 00:59:05 +00:00
|
|
|
|
printf ("\tRunLoop accept input before %f (seconds from now %f)\n",
|
1996-03-12 15:39:13 +00:00
|
|
|
|
[limit_date timeIntervalSinceReferenceDate], ti);
|
|
|
|
|
/* If LIMIT_DATE has already past, return immediately. */
|
|
|
|
|
if (ti < 0)
|
|
|
|
|
{
|
|
|
|
|
if (debug_run_loop)
|
1996-03-19 00:59:05 +00:00
|
|
|
|
printf ("\tRunLoop limit date past, returning\n");
|
1996-03-12 15:39:13 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
timeout.tv_sec = ti;
|
|
|
|
|
timeout.tv_usec = ti * 1000000.0;
|
|
|
|
|
select_timeout = &timeout;
|
|
|
|
|
}
|
1996-03-19 17:10:41 +00:00
|
|
|
|
else if (ti <= 0.0)
|
|
|
|
|
{
|
|
|
|
|
/* The LIMIT_DATE has already past; return immediately without
|
|
|
|
|
polling any inputs. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
1996-03-12 15:39:13 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
1996-03-19 17:10:41 +00:00
|
|
|
|
/* Wait forever. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
if (debug_run_loop)
|
1996-03-19 00:59:05 +00:00
|
|
|
|
printf ("\tRunLoop accept input waiting forever\n");
|
1996-03-12 15:39:13 +00:00
|
|
|
|
select_timeout = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* Get ready to listen to file descriptors.
|
|
|
|
|
Initialize the set of FDS we'll pass to select(), and create
|
|
|
|
|
an empty map for keeping track of which object is associated
|
|
|
|
|
with which file descriptor. */
|
|
|
|
|
FD_ZERO (&fds);
|
|
|
|
|
fd_2_object = NSCreateMapTable (NSIntMapKeyCallBacks,
|
|
|
|
|
NSObjectMapValueCallBacks, 0);
|
|
|
|
|
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* Do the pre-listening set-up for the file descriptors of this mode. */
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do the pre-listening set-up for the ports of this mode. */
|
|
|
|
|
{
|
|
|
|
|
id ports = NSMapGet (_mode_2_in_ports, mode);
|
|
|
|
|
if (ports)
|
|
|
|
|
{
|
|
|
|
|
id port;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* If a port is invalid, remove it from this mode. */
|
|
|
|
|
for (i = [ports count]-1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
port = [ports objectAtIndex: i];
|
|
|
|
|
if (![port isValid])
|
|
|
|
|
[ports removeObjectAtIndex: i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ask our ports for the list of file descriptors they
|
|
|
|
|
want us to listen to; add these to FD_LISTEN_SET. */
|
|
|
|
|
for (i = [ports count]-1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
int port_fd_count = 128;
|
|
|
|
|
int port_fd_array[port_fd_count];
|
|
|
|
|
port = [ports objectAtIndex: i];
|
|
|
|
|
if ([port respondsTo: @selector(getFds:count:)])
|
|
|
|
|
[port getFds: port_fd_array count: &port_fd_count];
|
1996-03-19 00:59:05 +00:00
|
|
|
|
if (debug_run_loop)
|
|
|
|
|
printf("\tRunLoop listening to %d sockets\n", port_fd_count);
|
1996-03-18 19:32:45 +00:00
|
|
|
|
while (port_fd_count--)
|
|
|
|
|
{
|
|
|
|
|
FD_SET (port_fd_array[port_fd_count], &fds);
|
|
|
|
|
NSMapInsert (fd_2_object,
|
|
|
|
|
(void*)port_fd_array[port_fd_count], port);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wait for incoming data, listening to the file descriptors in _FDS. */
|
|
|
|
|
read_fds = fds;
|
|
|
|
|
exception_fds = fds;
|
|
|
|
|
select_return = select (FD_SETSIZE, &read_fds, NULL, &exception_fds,
|
|
|
|
|
select_timeout);
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
|
|
|
|
if (debug_run_loop)
|
1996-03-19 00:59:05 +00:00
|
|
|
|
printf ("\tRunLoop select returned %d\n", select_return);
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
|
|
|
|
if (select_return < 0)
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* Some exceptional condition happened. */
|
|
|
|
|
/* xxx We can do something with exception_fds, instead of
|
|
|
|
|
aborting here. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
perror ("[TcpInPort receivePacketWithTimeout:] select()");
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
else if (select_return == 0)
|
|
|
|
|
return;
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
1996-03-12 15:39:13 +00:00
|
|
|
|
/* Look at all the file descriptors select() says are ready for reading;
|
1996-03-18 19:32:45 +00:00
|
|
|
|
notify the corresponding object for each of the ready fd's. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
for (fd_index = 0; fd_index < FD_SETSIZE; fd_index++)
|
1996-03-18 19:32:45 +00:00
|
|
|
|
if (FD_ISSET (fd_index, &read_fds))
|
1996-03-12 15:39:13 +00:00
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
id fd_object = (id) NSMapGet (fd_2_object, (void*)fd_index);
|
|
|
|
|
assert (fd_object);
|
|
|
|
|
[fd_object readyForReadingOnFileDescriptor: fd_index];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
}
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
|
|
|
|
/* Clean up before returning. */
|
|
|
|
|
NSFreeMapTable (fd_2_object);
|
|
|
|
|
_current_mode = saved_mode;
|
1996-03-12 15:39:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
|
|
|
|
/* Running the run loop once through for timers and input listening. */
|
|
|
|
|
|
|
|
|
|
- (BOOL) runOnceBeforeDate: date forMode: (id <String>)mode
|
1996-03-12 15:39:13 +00:00
|
|
|
|
{
|
|
|
|
|
id d;
|
|
|
|
|
|
|
|
|
|
/* If DATE is already later than now, just return. */
|
|
|
|
|
if ([date timeIntervalSinceNow] < 0)
|
|
|
|
|
{
|
|
|
|
|
if (debug_run_loop)
|
1996-03-19 00:59:05 +00:00
|
|
|
|
printf ("\tRunLoop run mode before date already past\n");
|
1996-03-12 15:39:13 +00:00
|
|
|
|
return NO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find out how long we can wait; and fire timers that are ready. */
|
1996-03-18 19:32:45 +00:00
|
|
|
|
d = [self limitDateForMode: mode];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
if (!d)
|
|
|
|
|
d = date;
|
|
|
|
|
|
|
|
|
|
/* Wait, listening to our input sources. */
|
|
|
|
|
[self acceptInputForMode: mode
|
|
|
|
|
beforeDate: d];
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
1996-03-19 17:10:41 +00:00
|
|
|
|
/* xxx Is this where we detect if the RunLoop is idle, and whether
|
|
|
|
|
we should dispatch the notifications from NotificationQueue's
|
|
|
|
|
idle queue? */
|
|
|
|
|
|
1996-03-12 15:39:13 +00:00
|
|
|
|
return YES;
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-12 19:42:45 +00:00
|
|
|
|
- (BOOL) runOnceBeforeDate: date
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
return [self runOnceBeforeDate: date forMode: _current_mode];
|
1996-03-12 19:42:45 +00:00
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
|
|
|
|
/* Running the run loop multiple times through. */
|
|
|
|
|
|
|
|
|
|
- (void) runUntilDate: date forMode: (id <String>)mode
|
1996-03-12 15:39:13 +00:00
|
|
|
|
{
|
|
|
|
|
volatile double ti;
|
|
|
|
|
|
|
|
|
|
ti = [date timeIntervalSinceNow];
|
|
|
|
|
/* Positive values are in the future. */
|
|
|
|
|
while (ti > 0)
|
|
|
|
|
{
|
|
|
|
|
id arp = [NSAutoreleasePool new];
|
|
|
|
|
if (debug_run_loop)
|
1996-03-19 00:59:05 +00:00
|
|
|
|
printf ("\tRunLoop run until date %f seconds from now\n", ti);
|
1996-03-18 19:32:45 +00:00
|
|
|
|
[self runOnceBeforeDate: date forMode: mode];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
[arp release];
|
|
|
|
|
ti = [date timeIntervalSinceNow];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
- (void) runUntilDate: date
|
|
|
|
|
{
|
|
|
|
|
[self runUntilDate: date forMode: _current_mode];
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-12 15:39:13 +00:00
|
|
|
|
- (void) run
|
|
|
|
|
{
|
|
|
|
|
[self runUntilDate: [NSDate distantFuture]];
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
|
1996-03-12 15:39:13 +00:00
|
|
|
|
/* Class methods that send messages to the current instance. */
|
|
|
|
|
|
|
|
|
|
+ (void) run
|
|
|
|
|
{
|
|
|
|
|
assert (current_run_loop);
|
|
|
|
|
[current_run_loop run];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (void) runUntilDate: date
|
|
|
|
|
{
|
|
|
|
|
assert (current_run_loop);
|
|
|
|
|
[current_run_loop runUntilDate: date];
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
+ (void) runUntilDate: date forMode: (id <String>)mode
|
|
|
|
|
{
|
|
|
|
|
assert (current_run_loop);
|
|
|
|
|
[current_run_loop runUntilDate: date forMode: mode];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (BOOL) runOnceBeforeDate: date
|
|
|
|
|
{
|
|
|
|
|
return [current_run_loop runOnceBeforeDate: date];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
+ (BOOL) runOnceBeforeDate: date forMode: (id <String>)mode
|
|
|
|
|
{
|
|
|
|
|
return [current_run_loop runOnceBeforeDate: date forMode: mode];
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-12 15:39:13 +00:00
|
|
|
|
+ currentInstance
|
|
|
|
|
{
|
|
|
|
|
assert (current_run_loop);
|
|
|
|
|
return current_run_loop;
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
+ (id <String>) currentMode
|
|
|
|
|
{
|
|
|
|
|
return [current_run_loop currentMode];
|
|
|
|
|
}
|
|
|
|
|
|
1996-03-12 15:39:13 +00:00
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
1996-03-18 19:32:45 +00:00
|
|
|
|
/* RunLoop mode strings. */
|
|
|
|
|
|
|
|
|
|
id RunLoopDefaultMode = @"RunLoopDefaultMode";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* NSObject method additions. */
|
1996-03-12 15:39:13 +00:00
|
|
|
|
|
|
|
|
|
@implementation NSObject (PerformingAfterDelay)
|
|
|
|
|
|
|
|
|
|
- (void) performSelector: (SEL)sel afterDelay: (NSTimeInterval)delay
|
|
|
|
|
{
|
1996-03-18 19:32:45 +00:00
|
|
|
|
[self notImplemented: _cmd];
|
1996-03-12 15:39:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
- getNotificationWithName: (id <String>)name
|
|
|
|
|
object: object
|
|
|
|
|
inMode: (id <String>)mode
|
|
|
|
|
beforeDate: date
|
|
|
|
|
{
|
|
|
|
|
/* See if any timers should fire, and fire them. */
|
|
|
|
|
|
|
|
|
|
/* Figure out how long we can listen to file descriptors before, either,
|
|
|
|
|
we need to fire another timer, or we need to return before DATE. */
|
|
|
|
|
|
|
|
|
|
/* Wait, listening to the file descriptors. */
|
|
|
|
|
|
|
|
|
|
/* Process active file descriptors. */
|
|
|
|
|
|
|
|
|
|
/* Is it time to return? If not, go back and check timers,
|
|
|
|
|
otherwise return. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Some alternate names */
|
|
|
|
|
- waitForNotificationWithName: (id <String>)name
|
|
|
|
|
object: object
|
|
|
|
|
inMode: (id <String>)mode
|
|
|
|
|
untilDate: date
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The old alternate names */
|
|
|
|
|
- (void) makeNotificationsForFileDescriptor: (int)fd
|
|
|
|
|
forMode: (id <String>)mode
|
|
|
|
|
name: (id <String>)name
|
|
|
|
|
object: object
|
|
|
|
|
postingTo: (id <NotificationPosting>)poster
|
|
|
|
|
postingStyle: style
|
|
|
|
|
- (void) addFileDescriptor: (int)fd
|
|
|
|
|
forMode: (id <String>)mode
|
|
|
|
|
postingWithName: (id <String>)name
|
|
|
|
|
object: object;
|
|
|
|
|
- (void) addFileDescriptor: (int)fd
|
|
|
|
|
withAttender: (id <FileDescriptorAttending>)object
|
|
|
|
|
- (void) addObserver: observer
|
|
|
|
|
selector: (SEL)
|
|
|
|
|
ofName: fileDescriptorString
|
|
|
|
|
withAttender: (id <FileDescriptorAttending>)object
|
|
|
|
|
|
|
|
|
|
#endif
|