Commit 3fad2ad2 authored by Jan D's avatar Jan D

Add fd handling with callbacks to select, dbus needs it for async operation.

* src/dbusbind.c: Include process.h.
(dbus_fd_cb, xd_find_watch_fd, xd_toggle_watch)
(xd_read_message_1): New functions.
(xd_add_watch, xd_remove_watch): Call xd_find_watch_fd.  Handle
watch for both read and write.
(Fdbus_init_bus): Also register xd_toggle_watch.
(Fdbus_call_method_asynchronously, Fdbus_method_return_internal)
(Fdbus_method_error_internal, Fdbus_send_signal): Remove call
to dbus_connection_flush.
(xd_read_message):  Move most of the code to xd_read_message_1.
Call xd_read_message_1 until status is COMPLETE.

* src/keyboard.c (readable_events, gobble_input): Remove DBUS code.

* src/process.c (gpm_wait_mask, max_gpm_desc): Remove.
(write_mask): New variable.
(max_input_desc): Renamed from max_keyboard_desc.
(fd_callback_info): New variable.
(add_read_fd, delete_read_fd, add_write_fd, delete_write_fd): New
functions.
(Fmake_network_process): FD_SET write_mask.
(deactivate_process): FD_CLR write_mask.
(wait_reading_process_output): Connecting renamed to Writeok.
check_connect removed.  check_write is new.  Remove references to
gpm.  Use Writeok/check_write unconditionally (i.e. no #ifdef
NON_BLOCKING_CONNECT) instead of Connecting.
Loop over file descriptors and call callbacks in fd_callback_info
if file descriptor is ready for I/O.
(add_gpm_wait_descriptor): Just call add_keyboard_wait_descriptor.
(delete_gpm_wait_descriptor): Just call delete_keyboard_wait_descriptor.
(keyboard_bit_set): Use max_input_desc.
(add_keyboard_wait_descriptor, delete_keyboard_wait_descriptor): Remove
#ifdef subprocesses. Use max_input_desc.
(init_process): Initialize write_mask and fd_callback_info.

* src/process.h (add_read_fd, delete_read_fd, add_write_fd)
(delete_write_fd): Declare.
parent 6303aba1
2010-09-26 Jan Djärv <jan.h.d@swipnet.se>
* process.h (add_read_fd, delete_read_fd, add_write_fd)
(delete_write_fd): Declare.
* process.c (gpm_wait_mask, max_gpm_desc): Remove.
(write_mask): New variable.
(max_input_desc): Renamed from max_keyboard_desc.
(fd_callback_info): New variable.
(add_read_fd, delete_read_fd, add_write_fd, delete_write_fd): New
functions.
(Fmake_network_process): FD_SET write_mask.
(deactivate_process): FD_CLR write_mask.
(wait_reading_process_output): Connecting renamed to Writeok.
check_connect removed. check_write is new. Remove references to
gpm. Use Writeok/check_write unconditionally (i.e. no #ifdef
NON_BLOCKING_CONNECT) instead of Connecting.
Loop over file descriptors and call callbacks in fd_callback_info
if file descriptor is ready for I/O.
(add_gpm_wait_descriptor): Just call add_keyboard_wait_descriptor.
(delete_gpm_wait_descriptor): Just call delete_keyboard_wait_descriptor.
(keyboard_bit_set): Use max_input_desc.
(add_keyboard_wait_descriptor, delete_keyboard_wait_descriptor): Remove
#ifdef subprocesses. Use max_input_desc.
(init_process): Initialize write_mask and fd_callback_info.
* keyboard.c (readable_events, gobble_input): Remove DBUS code.
* dbusbind.c: Include process.h.
(dbus_fd_cb, xd_find_watch_fd, xd_toggle_watch)
(xd_read_message_1): New functions.
(xd_add_watch, xd_remove_watch): Call xd_find_watch_fd. Handle
watch for both read and write.
(Fdbus_init_bus): Also register xd_toggle_watch.
(Fdbus_call_method_asynchronously, Fdbus_method_return_internal)
(Fdbus_method_error_internal, Fdbus_send_signal): Remove call
to dbus_connection_flush.
(xd_read_message): Move most of the code to xd_read_message_1.
Call xd_read_message_1 until status is COMPLETE.
2010-09-26 Dan Nicolaescu <dann@ics.uci.edu>
* term.c: Do not include sys/ioctl.h, not needed.
......
......@@ -27,6 +27,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "frame.h"
#include "termhooks.h"
#include "keyboard.h"
#include "process.h"
/* Subroutines. */
......@@ -799,71 +800,93 @@ xd_initialize (Lisp_Object bus, int raise_error)
return connection;
}
/* Callback called when something is read to read ow write. */
/* Add connection file descriptor to input_wait_mask, in order to
let select() detect, whether a new message has been arrived. */
dbus_bool_t
xd_add_watch (DBusWatch *watch, void *data)
static void
dbus_fd_cb (int fd, void *data, int for_read)
{
xd_read_queued_messages ();
}
/* Return the file descriptor for WATCH, -1 if not found. */
static int
xd_find_watch_fd (DBusWatch *watch)
{
/* We check only for incoming data. */
if (dbus_watch_get_flags (watch) & DBUS_WATCH_READABLE)
{
#if HAVE_DBUS_WATCH_GET_UNIX_FD
/* TODO: Reverse these on Win32, which prefers the opposite. */
int fd = dbus_watch_get_unix_fd(watch);
if (fd == -1)
fd = dbus_watch_get_socket(watch);
/* TODO: Reverse these on Win32, which prefers the opposite. */
int fd = dbus_watch_get_unix_fd (watch);
if (fd == -1)
fd = dbus_watch_get_socket (watch);
#else
int fd = dbus_watch_get_fd(watch);
int fd = dbus_watch_get_fd (watch);
#endif
XD_DEBUG_MESSAGE ("fd %d", fd);
return fd;
}
if (fd == -1)
return FALSE;
/* Add the file descriptor to input_wait_mask. */
add_keyboard_wait_descriptor (fd);
}
/* Start monitoring WATCH for possible I/O. */
/* Return. */
static dbus_bool_t
xd_add_watch (DBusWatch *watch, void *data)
{
unsigned int flags = dbus_watch_get_flags (watch);
int fd = xd_find_watch_fd (watch);
XD_DEBUG_MESSAGE ("fd %d, write %d, enabled %d",
fd, flags & DBUS_WATCH_WRITABLE,
dbus_watch_get_enabled (watch));
if (fd == -1)
return FALSE;
if (dbus_watch_get_enabled (watch))
{
if (flags & DBUS_WATCH_WRITABLE)
add_write_fd (fd, dbus_fd_cb, NULL);
if (flags & DBUS_WATCH_READABLE)
add_read_fd (fd, dbus_fd_cb, NULL);
}
return TRUE;
}
/* Remove connection file descriptor from input_wait_mask. DATA is
the used bus, either a string or QCdbus_system_bus or
/* Stop monitoring WATCH for possible I/O.
DATA is the used bus, either a string or QCdbus_system_bus or
QCdbus_session_bus. */
void
static void
xd_remove_watch (DBusWatch *watch, void *data)
{
/* We check only for incoming data. */
if (dbus_watch_get_flags (watch) & DBUS_WATCH_READABLE)
{
#if HAVE_DBUS_WATCH_GET_UNIX_FD
/* TODO: Reverse these on Win32, which prefers the opposite. */
int fd = dbus_watch_get_unix_fd(watch);
if (fd == -1)
fd = dbus_watch_get_socket(watch);
#else
int fd = dbus_watch_get_fd(watch);
#endif
XD_DEBUG_MESSAGE ("fd %d", fd);
unsigned int flags = dbus_watch_get_flags (watch);
int fd = xd_find_watch_fd (watch);
if (fd == -1)
return;
XD_DEBUG_MESSAGE ("fd %d", fd);
if (fd == -1) return;
/* Unset session environment. */
if ((data != NULL) && (data == (void*) XHASH (QCdbus_session_bus)))
{
XD_DEBUG_MESSAGE ("unsetenv DBUS_SESSION_BUS_ADDRESS");
unsetenv ("DBUS_SESSION_BUS_ADDRESS");
}
/* Remove the file descriptor from input_wait_mask. */
delete_keyboard_wait_descriptor (fd);
/* Unset session environment. */
if (data != NULL && data == (void*) XHASH (QCdbus_session_bus))
{
XD_DEBUG_MESSAGE ("unsetenv DBUS_SESSION_BUS_ADDRESS");
unsetenv ("DBUS_SESSION_BUS_ADDRESS");
}
/* Return. */
return;
if (flags & DBUS_WATCH_WRITABLE)
delete_write_fd (fd);
if (flags & DBUS_WATCH_READABLE)
delete_read_fd (fd);
}
/* Toggle monitoring WATCH for possible I/O. */
static void
xd_toggle_watch (DBusWatch *watch, void *data)
{
if (dbus_watch_get_enabled (watch))
xd_add_watch (watch, data);
else
xd_remove_watch (watch, data);
}
DEFUN ("dbus-init-bus", Fdbus_init_bus, Sdbus_init_bus, 1, 1, 0,
......@@ -880,7 +903,8 @@ DEFUN ("dbus-init-bus", Fdbus_init_bus, Sdbus_init_bus, 1, 1, 0,
if (!dbus_connection_set_watch_functions (connection,
xd_add_watch,
xd_remove_watch,
NULL, (void*) XHASH (bus), NULL))
xd_toggle_watch,
(void*) XHASH (bus), NULL))
XD_SIGNAL1 (build_string ("Cannot add watch functions"));
/* Add bus to list of registered buses. */
......@@ -1288,9 +1312,6 @@ usage: (dbus-call-method-asynchronously BUS SERVICE PATH INTERFACE METHOD HANDLE
result = Qnil;
}
/* Flush connection to ensure the message is handled. */
dbus_connection_flush (connection);
XD_DEBUG_MESSAGE ("Message sent");
/* Cleanup. */
......@@ -1379,9 +1400,6 @@ usage: (dbus-method-return-internal BUS SERIAL SERVICE &rest ARGS) */)
if (!dbus_connection_send (connection, dmessage, NULL))
XD_SIGNAL1 (build_string ("Cannot send message"));
/* Flush connection to ensure the message is handled. */
dbus_connection_flush (connection);
XD_DEBUG_MESSAGE ("Message sent");
/* Cleanup. */
......@@ -1471,9 +1489,6 @@ usage: (dbus-method-error-internal BUS SERIAL SERVICE &rest ARGS) */)
if (!dbus_connection_send (connection, dmessage, NULL))
XD_SIGNAL1 (build_string ("Cannot send message"));
/* Flush connection to ensure the message is handled. */
dbus_connection_flush (connection);
XD_DEBUG_MESSAGE ("Message sent");
/* Cleanup. */
......@@ -1589,9 +1604,6 @@ usage: (dbus-send-signal BUS SERVICE PATH INTERFACE SIGNAL &rest ARGS) */)
if (!dbus_connection_send (connection, dmessage, NULL))
XD_SIGNAL1 (build_string ("Cannot send message"));
/* Flush connection to ensure the message is handled. */
dbus_connection_flush (connection);
XD_DEBUG_MESSAGE ("Signal sent");
/* Cleanup. */
......@@ -1645,32 +1657,27 @@ xd_pending_messages (void)
return FALSE;
}
/* Read queued incoming message of the D-Bus BUS. BUS is either a
Lisp symbol, :system or :session, or a string denoting the bus
address. */
static Lisp_Object
xd_read_message (Lisp_Object bus)
/* Read one queued incoming message of the D-Bus BUS.
BUS is either a Lisp symbol, :system or :session, or a string denoting
the bus address. */
static void
xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
{
Lisp_Object args, key, value;
struct gcpro gcpro1;
struct input_event event;
DBusConnection *connection;
DBusMessage *dmessage;
DBusMessageIter iter;
unsigned int dtype;
int mtype, serial;
const char *uname, *path, *interface, *member;
/* Open a connection to the bus. */
connection = xd_initialize (bus, TRUE);
/* Non blocking read of the next available message. */
dbus_connection_read_write (connection, 0);
dmessage = dbus_connection_pop_message (connection);
/* Return if there is no queued message. */
if (dmessage == NULL)
return Qnil;
return;
/* Collect the parameters. */
args = Qnil;
......@@ -1801,7 +1808,26 @@ xd_read_message (Lisp_Object bus)
cleanup:
dbus_message_unref (dmessage);
RETURN_UNGCPRO (Qnil);
UNGCPRO;
}
/* Read queued incoming messages of the D-Bus BUS.
BUS is either a Lisp symbol, :system or :session, or a string denoting
the bus address. */
static Lisp_Object
xd_read_message (Lisp_Object bus)
{
/* Open a connection to the bus. */
DBusConnection *connection = xd_initialize (bus, TRUE);
/* Non blocking read of the next available message. */
dbus_connection_read_write (connection, 0);
while (dbus_connection_get_dispatch_status (connection)
!= DBUS_DISPATCH_COMPLETE)
xd_read_message_1 (connection, bus);
return Qnil;
}
/* Read queued incoming messages from all buses. */
......
......@@ -3522,12 +3522,6 @@ restore_getcjmp (jmp_buf temp)
static int
readable_events (int flags)
{
#ifdef HAVE_DBUS
/* Check whether a D-Bus message has arrived. */
if (xd_pending_messages () > 0)
return 1;
#endif /* HAVE_DBUS */
if (flags & READABLE_EVENTS_DO_TIMERS_NOW)
timer_check (1);
......@@ -6877,11 +6871,6 @@ get_input_pending (int *addr, int flags)
void
gobble_input (int expected)
{
#ifdef HAVE_DBUS
/* Read D-Bus messages. */
xd_read_queued_messages ();
#endif /* HAVE_DBUS */
#ifdef SIGIO
if (interrupt_input)
{
......
......@@ -294,9 +294,9 @@ static SELECT_TYPE non_keyboard_wait_mask;
static SELECT_TYPE non_process_wait_mask;
/* Mask for the gpm mouse input descriptor. */
/* Mask for selecting for write. */
static SELECT_TYPE gpm_wait_mask;
static SELECT_TYPE write_mask;
#ifdef NON_BLOCKING_CONNECT
/* Mask of bits indicating the descriptors that we wait for connect to
......@@ -316,11 +316,8 @@ static int num_pending_connects;
/* The largest descriptor currently in use for a process object. */
static int max_process_desc;
/* The largest descriptor currently in use for keyboard input. */
static int max_keyboard_desc;
/* The largest descriptor currently in use for gpm mouse input. */
static int max_gpm_desc;
/* The largest descriptor currently in use for input. */
static int max_input_desc;
/* Indexed by descriptor, gives the process (if any) for that descriptor */
Lisp_Object chan_process[MAXDESC];
......@@ -365,6 +362,90 @@ static int pty_max_bytes;
static char pty_name[24];
#endif
struct fd_callback_data
{
fd_callback func;
void *data;
#define FOR_READ 1
#define FOR_WRITE 2
int condition; /* mask of the defines above. */
} fd_callback_info[MAXDESC];
/* Add a file descriptor FD to be monitored for when read is possible.
When read is possible, call FUNC with argument DATA. */
void
add_read_fd (int fd, fd_callback func, void *data)
{
xassert (fd < MAXDESC);
add_keyboard_wait_descriptor (fd);
fd_callback_info[fd].func = func;
fd_callback_info[fd].data = data;
fd_callback_info[fd].condition |= FOR_READ;
}
/* Stop monitoring file descriptor FD for when read is possible. */
void
delete_read_fd (int fd)
{
xassert (fd < MAXDESC);
delete_keyboard_wait_descriptor (fd);
fd_callback_info[fd].condition &= ~FOR_READ;
if (fd_callback_info[fd].condition == 0)
{
fd_callback_info[fd].func = 0;
fd_callback_info[fd].data = 0;
}
}
/* Add a file descriptor FD to be monitored for when write is possible.
When write is possible, call FUNC with argument DATA. */
void
add_write_fd (int fd, fd_callback func, void *data)
{
xassert (fd < MAXDESC);
FD_SET (fd, &write_mask);
if (fd > max_input_desc)
max_input_desc = fd;
fd_callback_info[fd].func = func;
fd_callback_info[fd].data = data;
fd_callback_info[fd].condition |= FOR_WRITE;
}
/* Stop monitoring file descriptor FD for when write is possible. */
void
delete_write_fd (int fd)
{
int lim = max_input_desc;
xassert (fd < MAXDESC);
FD_CLR (fd, &write_mask);
fd_callback_info[fd].condition &= ~FOR_WRITE;
if (fd_callback_info[fd].condition == 0)
{
fd_callback_info[fd].func = 0;
fd_callback_info[fd].data = 0;
if (fd == max_input_desc)
for (fd = lim; fd >= 0; fd--)
if (FD_ISSET (fd, &input_wait_mask) || FD_ISSET (fd, &write_mask))
{
max_input_desc = fd;
break;
}
}
}
/* Compute the Lisp form of the process status, p->status, from
the numeric status that was returned by `wait'. */
......@@ -3620,6 +3701,7 @@ usage: (make-network-process &rest ARGS) */)
if (!FD_ISSET (inch, &connect_wait_mask))
{
FD_SET (inch, &connect_wait_mask);
FD_SET (inch, &write_mask);
num_pending_connects++;
}
}
......@@ -4023,6 +4105,7 @@ deactivate_process (Lisp_Object proc)
if (FD_ISSET (inchannel, &connect_wait_mask))
{
FD_CLR (inchannel, &connect_wait_mask);
FD_CLR (inchannel, &write_mask);
if (--num_pending_connects < 0)
abort ();
}
......@@ -4401,10 +4484,8 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
{
register int channel, nfds;
SELECT_TYPE Available;
#ifdef NON_BLOCKING_CONNECT
SELECT_TYPE Connecting;
int check_connect;
#endif
SELECT_TYPE Writeok;
int check_write;
int check_delay, no_avail;
int xerrno;
Lisp_Object proc;
......@@ -4414,9 +4495,7 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
int count = SPECPDL_INDEX ();
FD_ZERO (&Available);
#ifdef NON_BLOCKING_CONNECT
FD_ZERO (&Connecting);
#endif
FD_ZERO (&Writeok);
if (time_limit == 0 && microsecs == 0 && wait_proc && !NILP (Vinhibit_quit)
&& !(CONSP (wait_proc->status) && EQ (XCAR (wait_proc->status), Qexit)))
......@@ -4552,19 +4631,16 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
if (update_tick != process_tick)
{
SELECT_TYPE Atemp;
#ifdef NON_BLOCKING_CONNECT
SELECT_TYPE Ctemp;
#endif
if (kbd_on_hold_p ())
FD_ZERO (&Atemp);
else
Atemp = input_wait_mask;
IF_NON_BLOCKING_CONNECT (Ctemp = connect_wait_mask);
Ctemp = write_mask;
EMACS_SET_SECS_USECS (timeout, 0, 0);
if ((select (max (max (max_process_desc, max_keyboard_desc),
max_gpm_desc) + 1,
if ((select (max (max_process_desc, max_input_desc) + 1,
&Atemp,
#ifdef NON_BLOCKING_CONNECT
(num_pending_connects > 0 ? &Ctemp : (SELECT_TYPE *)0),
......@@ -4635,13 +4711,13 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
break;
FD_SET (wait_proc->infd, &Available);
check_delay = 0;
IF_NON_BLOCKING_CONNECT (check_connect = 0);
check_write = 0;
}
else if (!NILP (wait_for_cell))
{
Available = non_process_wait_mask;
check_delay = 0;
IF_NON_BLOCKING_CONNECT (check_connect = 0);
check_write = 0;
}
else
{
......@@ -4649,7 +4725,8 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
Available = non_keyboard_wait_mask;
else
Available = input_wait_mask;
IF_NON_BLOCKING_CONNECT (check_connect = (num_pending_connects > 0));
Writeok = write_mask;
check_write = 1;
check_delay = wait_channel >= 0 ? 0 : process_output_delay_count;
}
......@@ -4674,10 +4751,6 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
}
else
{
#ifdef NON_BLOCKING_CONNECT
if (check_connect)
Connecting = connect_wait_mask;
#endif
#ifdef ADAPTIVE_READ_BUFFERING
/* Set the timeout for adaptive read buffering if any
......@@ -4719,15 +4792,10 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
#else
nfds = select
#endif
(max (max (max_process_desc, max_keyboard_desc),
max_gpm_desc) + 1,
&Available,
#ifdef NON_BLOCKING_CONNECT
(check_connect ? &Connecting : (SELECT_TYPE *)0),
#else
(SELECT_TYPE *)0,
#endif
(SELECT_TYPE *)0, &timeout);
(max (max_process_desc, max_input_desc) + 1,
&Available,
(check_write ? &Writeok : (SELECT_TYPE *)0),
(SELECT_TYPE *)0, &timeout);
}
xerrno = errno;
......@@ -4767,7 +4835,7 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
if (no_avail)
{
FD_ZERO (&Available);
IF_NON_BLOCKING_CONNECT (check_connect = 0);
check_write = 0;
}
#if 0 /* When polling is used, interrupt_input is 0,
......@@ -4863,12 +4931,26 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
if (no_avail || nfds == 0)
continue;
for (channel = 0; channel <= max_input_desc; ++channel)
{
struct fd_callback_data *d = &fd_callback_info[channel];
if (FD_ISSET (channel, &Available)
&& d->func != 0
&& (d->condition & FOR_READ) != 0)
d->func (channel, d->data, 1);
if (FD_ISSET (channel, &write_mask)
&& d->func != 0
&& (d->condition & FOR_WRITE) != 0)
d->func (channel, d->data, 0);
}
/* Really FIRST_PROC_DESC should be 0 on Unix,
but this is safer in the short run. */
for (channel = 0; channel <= max_process_desc; channel++)
{
if (FD_ISSET (channel, &Available)
&& FD_ISSET (channel, &non_keyboard_wait_mask))
&& FD_ISSET (channel, &non_keyboard_wait_mask)
&& !FD_ISSET (channel, &non_process_wait_mask))
{
int nread;
......@@ -4973,7 +5055,7 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
}
}
#ifdef NON_BLOCKING_CONNECT
if (check_connect && FD_ISSET (channel, &Connecting)
if (FD_ISSET (channel, &Writeok)
&& FD_ISSET (channel, &connect_wait_mask))
{
struct Lisp_Process *p;
......@@ -6745,35 +6827,16 @@ DEFUN ("process-filter-multibyte-p", Fprocess_filter_multibyte_p,
static int add_gpm_wait_descriptor_called_flag;
void
add_gpm_wait_descriptor (int desc)
{
if (! add_gpm_wait_descriptor_called_flag)
FD_CLR (0, &input_wait_mask);
add_gpm_wait_descriptor_called_flag = 1;
FD_SET (desc, &input_wait_mask);
FD_SET (desc, &gpm_wait_mask);
if (desc > max_gpm_desc)
max_gpm_desc = desc;
add_keyboard_wait_descriptor (desc);
}
void
delete_gpm_wait_descriptor (int desc)
{
int fd;
int lim = max_gpm_desc;
FD_CLR (desc, &input_wait_mask);
FD_CLR (desc, &non_process_wait_mask);
if (desc == max_gpm_desc)
for (fd = 0; fd < lim; fd++)
if (FD_ISSET (fd, &input_wait_mask)
&& !FD_ISSET (fd, &non_keyboard_wait_mask)
&& !FD_ISSET (fd, &non_process_wait_mask))
max_gpm_desc = fd;
delete_keyboard_wait_descriptor (desc);
}
/* Return nonzero if *MASK has a bit set
......@@ -6784,7 +6847,7 @@ keyboard_bit_set (fd_set *mask)
{
int fd;
for (fd = 0; fd <= max_keyboard_desc; fd++)
for (fd = 0; fd <= max_input_desc; fd++)
if (FD_ISSET (fd, mask) && FD_ISSET (fd, &input_wait_mask)
&& !FD_ISSET (fd, &non_keyboard_wait_mask))
return 1;
......@@ -7023,12 +7086,10 @@ wait_reading_process_output (int time_limit, int microsecs, int read_kbd,
void
add_keyboard_wait_descriptor (int desc)
{
#ifdef subprocesses
FD_SET (desc, &input_wait_mask);