Commit 819b8f00 authored by Karoly Lorentey's avatar Karoly Lorentey

A few more bugfixes and new features.

(Sigh.)  I obviously need to remember to separate individual changes
to multiple commits.

src/emacsclient.c: Improved error handling.
(decode_options): Changed frame option (again) from -f to -t.
(print_help_and_exit): Ditto.
(copy_from_to): Check EINTR after write, not EAGAIN.  Removed SIGIO hack.
(pty_conversation): Handle errors transmitted through the socket.
Handle pty errors by not reading from it anymore.
(main): Restore correct errno after socket_status failed.  Send -tty
on -t, not -pty.

lisp/server.el (server-process-filter): Watch -tty, not -pty.
Use make-frame-on-tty instead of make-terminal-frame.
Don't set newframe to t if make-frame-on-tty failed.
Don't delete frames here.  Print correct message when there are no
files to edit, but a new frame was requested.
(server-sentinel): Delete the frame after the process.
(server-handle-delete-frame): New function for delete-frame-functions.
(server-start): Add server-handle-delete-frame to delete-frame-functions.
(server-buffer-done): Don't delete frames here.

src/alloc.c (mark_ttys): Add prototype.
(Fgarbage_collect): Call mark_ttys.

src/emacs.c: (shut_down_emacs): Don't flush stdout before
reset_sys_modes().

src/process.c (add_keyboard_wait_descriptor_called_flag): Removed.
(add_keyboard_wait_descriptor): Removed stdin hack.

src/sysdep.c: Unconditionally include sysselect.h.
(old_fcntl_flags): Changed to an array.
(init_sigio, reset_sigio): Use it.
(narrow_foreground_group, widen_foreground_group): Use setpgid, not
setpgrp.
(old_fcntl_owner): Changed to an array.
(init_sys_modes, reset_sys_modes): Use it.  Fix fsync() and reset_sigio() calls.
src/term.c (Qframe_tty_name, Qframe_tty_type): New variables.
(syms_of_term): Initialize them.
(Fframe_tty_name, Fframe_tty_type): New functions.
(term_init): Call add_keyboard_wait_descriptor().
(Fdelete_tty): New function.
(delete_tty): Call delete_keyboard_wait_descriptor().
(get_current_tty): Removed.
(mark_ttys): New function.

git-archimport-id: lorentey@elte.hu--2004/emacs--multi-tty--0--patch-28
parent 16c290d8
......@@ -45,15 +45,13 @@ You'll hopefully have two fully working, independent frames on
separate terminals. (This seems to be very useful, emacsclient starts
up even faster than vi!) :-) You can close the newly opened frame and
return to the shell without exiting Emacs by pressing C-x 5 0, i.e.,
delete-frame. (Note that this does not seem to work yet on the
original terminal.) Creating new frames on the same tty with C-x 5 2
delete-frame. Creating new frames on the same tty with C-x 5 2
works exactly as before. Suspending Emacs is disabled at the moment.
If you exit emacs, all terminals should be restored to their previous
states.
X, Mac, Windows and DOS support is broken, probably doesn't even
compile -- this will be solved later. Please see the attached README
for other issues, implementation notes and sub-TODO items.
compile -- this will be solved later.
Only tested on my GNU/Linux box.
......@@ -75,6 +73,10 @@ For the NEWS file:
** A make-frame-on-tty function has been added to make it easier to
create frames on new terminals.
** New functions: frame-tty-name, frame-tty-type for accessing
terminal parameters, and delete-tty for closing the terminal
device.
** Emacsclient has been extended to support opening a new terminal
frame.
......@@ -108,6 +110,8 @@ DIARY OF CHANGES
succeeds. MULTIKBOARD is not used. Secondary terminals don't send
SIGIO!)
(Update: They do, now.)
-- other-frame should cycle through the frames on the `current'
terminal only.
......@@ -159,6 +163,10 @@ DIARY OF CHANGES
(Update: This is bullshit. There is a read_input_waiting function,
extend that somehow.)
(Update of update: The first update was not right either, extending
read_input_waiting was not necessary. Secondary ttys do seem to
send signals on input.)
-- Make make-terminal-frame look up the `tty' and `tty-type' frame
parameters from the currently selected terminal before the global
default.
......@@ -231,56 +239,67 @@ DIARY OF CHANGES
free()ing memory. Utterly trivial matter. I love the C's memory
management, it puts hair on your chest.)
THINGS TO DO
------------
-- Support raw secondary terminals. (Note that SIGIO works only on
the controlling terminal.) Hint: extend read_input_waiting() for
multiple ttys and hopefully this will be fixed.
** Understand Emacs's low-level input system (it seems complicated) :-)
and maybe rewrite multi-tty input in terms of MULTIKBOARD.
(Done, it seems to have been working already for some time. It
seems F_SETOWN does work, after all. Not sure what made it fail
earlier, but it seems to be fixed (there were several changes
around request_sigio, maybe one of them did it).
read_input_waiting() is only used in sys_select(), don't change
it.)
** Find out why does Emacs abort when it wants to close its
-- Find out why does Emacs abort when it wants to close its
controlling tty. Hint: chan_process[] array. Hey, maybe
noninterrupt-IO would work, too? Update: no, there is no process
for stdin/out.
** Support raw secondary terminals. (Note that SIGIO works only on
the controlling terminal.) Hint: extend read_input_waiting() for
multiple ttys and hopefully this will be fixed.
** What does interrupt_input do? I tried to disable it for raw
secondary tty support, but it does not seem to do anything useful.
(Done. Added add/delete_keyboard_wait_descriptor to
term_init/delete_tty. The hint was right, in a way.)
** Issue with SIGIO: it needs to be disabled during redisplay. See if
-- Issue with SIGIO: it needs to be disabled during redisplay. See if
fcntl() kernel behaviour could be emulated by emacsclient.
** Get rid of the accessor macros in termchar.h, or define macros for
all members.
** Make parts of struct tty_output accessible from Lisp. The device
name and the type is sufficient.
(Done. Simply disabled the SIGIO emulation hack in emacsclient.)
** server.el: There are issues with saving files in buffers of closed
-- server.el: There are issues with saving files in buffers of closed
clients. Try editing a file with emacsclient -f, and (without
saving it) do a delete-frame. The frame is closed without
question, and a surprising confirmation prompt appears in another
frame.
** emacsclient.el, server.el: Handle eval or file open errors when
(Done. delete-frame now asks for confirmation if it still has
pending buffers, and modified buffers don't seem to be deleted.)
-- emacsclient.el, server.el: Handle eval or file open errors when
doing -f.
** Export delete_tty to the Lisp environment, for emacsclient.
(Done.)
** Make sure C-g goes to the right frame. This is hard, as SIGINT
doesn't have a tty parameter. :-(
-- Make parts of struct tty_output accessible from Lisp. The device
name and the type is sufficient.
** Implement support for starting an interactive Emacs session without
an initial frame. (The user would connect to it and open frames
later, with emacsclient.) Not necessarily a good idea.
(Done, see frame-tty-name and frame-tty-type.)
-- Export delete_tty to the Lisp environment, for emacsclient.
** Move optimalization parameters (costs) from union output_data to
a backend-neutral per-device structure.
(Done, see delete-tty.)
THINGS TO DO
------------
** Find out the best way to support suspending Emacs with multiple
ttys.
ttys. My guess: disable it on the controlling tty, but other ttys
should pass it on to emacsclient somehow. (It is (I hope) trivial
to extend emacsclient to handle suspend/resume. A `kill -STOP'
almost works right now.)
** Move baud_rate to tty_output.
** Move device-specific parameters (like costs) commonly used by
device backends to a common, device-dependent structure.
** Do tty output through term_hooks, like graphical display backends.
......@@ -289,6 +308,10 @@ THINGS TO DO
** Allow simultaneous X and tty frames. (Handling input could be
tricky.)
** Implement support for starting an interactive Emacs session without
an initial frame. (The user would connect to it and open frames
later, with emacsclient.) Not necessarily a good idea.
** Fix Mac support (I can't do this myself).
** Fix W32 support (I can't do this myself).
......@@ -297,4 +320,35 @@ THINGS TO DO
** Do a grep on XXX and ?? for more issues.
** Get rid of the accessor macros in termchar.h, or define macros for
all members.
** Understand Emacs's low-level input system (it seems complicated) :-)
and maybe rewrite multi-tty input in terms of MULTIKBOARD.
** What does interrupt_input do? I tried to disable it for raw
secondary tty support, but it does not seem to do anything useful.
** Make sure C-g goes to the right frame. This is hard, as SIGINT
doesn't have a tty parameter. :-(
** I have seen a case when Emacs with multiple ttys went ate 100% of
CPU time. Strace showed this loop:
getpid() = 30284
kill(30284, SIGIO) = 0
--- SIGIO (I/O possible) @ 0 (0) ---
ioctl(6, FIONREAD, [0]) = -1 EIO (Input/output error)
ioctl(5, FIONREAD, [0]) = -1 EIO (Input/output error)
ioctl(0, FIONREAD, [0]) = 0
sigreturn() = ? (mask now [])
gettimeofday({1072842297, 747760}, NULL) = 0
gettimeofday({1072842297, 747806}, NULL) = 0
select(9, [0 3 5 6], NULL, NULL, {0, 0}) = 2 (in [5 6], left {0, 0})
select(9, [0 3 5 6], NULL, NULL, {0, 0}) = 2 (in [5 6], left {0, 0})
gettimeofday({1072842297, 748245}, NULL) = 0
I have not been able to reproduce this.
;;; arch-tag: 8da1619e-2e79-41a8-9ac9-a0485daad17d
......@@ -153,7 +153,7 @@ decode_options (argc, argv)
while (1)
{
int opt = getopt_long (argc, argv,
"VHnea:s:d:f", longopts, 0);
"VHnea:s:d:t", longopts, 0);
if (opt == EOF)
break;
......@@ -192,7 +192,7 @@ decode_options (argc, argv)
exit (0);
break;
case 'f':
case 't':
frame = 1;
break;
......@@ -225,7 +225,7 @@ Every FILE can be either just a FILENAME or [+LINE[:COLUMN]] FILENAME.\n\
The following OPTIONS are accepted:\n\
-V, --version Just print a version info and return\n\
-H, --help Print this usage information message\n\
-f, --frame Open a new Emacs frame on the current terminal\n\
-t, --tty Open a new Emacs frame on the current terminal\n\
-n, --no-wait Don't wait for the server to return\n\
-e, --eval Evaluate the FILE arguments as ELisp expressions\n\
-d, --display=DISPLAY Visit the file in the given display\n\
......@@ -300,7 +300,7 @@ xmalloc (size)
defined-- exit with an errorcode.
*/
void
fail ()
fail (void)
{
if (alternate_editor)
{
......@@ -720,14 +720,14 @@ init_pty ()
}
int
copy_from_to (int in, int out, int sigio)
copy_from_to (int in, int out)
{
static char buf[BUFSIZ];
int nread = read (in, &buf, BUFSIZ);
if (nread == 0)
return 1; /* EOF */
else if (nread < 0 && errno != EAGAIN)
return 0; /* Error */
return 0;
else if (nread > 0)
{
int r = 0;
......@@ -735,16 +735,11 @@ copy_from_to (int in, int out, int sigio)
do {
r = write (out, &buf, nread);
} while ((r < 0 && errno == EAGAIN)
} while ((r < 0 && errno == EINTR)
|| (r > 0 && (written += r) && written != nread));
if (r < 0)
return 0; /* Error */
if (emacs_pid && sigio)
{
kill (emacs_pid, SIGIO);
}
return 0;
}
return 1;
}
......@@ -754,53 +749,85 @@ pty_conversation (FILE *in)
{
char *str;
char string[BUFSIZ];
fd_set set;
fd_set set, rset;
int res;
FD_ZERO (&set);
FD_SET (master, &set);
FD_SET (1, &set);
FD_SET (fileno (in), &set);
in_conversation = 1;
while (! quit_conversation) {
int res;
FD_ZERO (&set);
FD_SET (master, &set);
FD_SET (1, &set);
FD_SET (fileno (in), &set);
res = select (FD_SETSIZE, &set, NULL, NULL, NULL);
if (res < 0)
rset = set;
res = select (FD_SETSIZE, &rset, NULL, NULL, NULL);
if (res < 0 && errno != EINTR)
{
if (errno != EINTR)
return 0;
reset_tty ();
fprintf (stderr, "%s: ", progname);
perror ("select");
return 0; /* Error */
}
else if (res > 0)
{
if (FD_ISSET (master, &set))
if (FD_ISSET (master, &rset))
{
/* Copy Emacs output to stdout. */
if (! copy_from_to (master, 0, 0))
return 1;
if (! copy_from_to (master, 0))
{
FD_CLR (master, &set);
}
}
if (FD_ISSET (1, &set))
if (FD_ISSET (1, &rset))
{
/* Forward user input to Emacs. */
if (! copy_from_to (1, master, 1))
return 1;
if (! copy_from_to (1, master))
{
FD_CLR (master, &set);
}
}
if (FD_ISSET (fileno (in), &set))
if (FD_ISSET (fileno (in), &rset))
{
do {
res = read (fileno (in), string, BUFSIZ-1);
} while (res == EINTR);
if (res < 0)
{
reset_tty ();
fprintf (stderr, "%s: ", progname);
perror ("read");
return 0;
}
if (!res)
{
return 1;
}
string[res] = 0;
if (string[res-1] == '\n')
string[res-1] = 0;
if (! emacs_pid)
{
/* Get the pid of the Emacs process.
XXX Is there is some nifty libc/kernel feature for doing this?
XXX Is there some nifty libc/kernel feature for doing this?
*/
str = fgets (string, BUFSIZ, in);
if (! str)
if (! string[0])
{
reset_tty ();
fprintf (stderr, "%s: %s\n", progname, str);
fail ();
fprintf (stderr, "%s: could not get Emacs process id\n"
"Maybe this Emacs does not support multiple terminals.\n", progname);
return 0;
}
emacs_pid = atoi (str);
emacs_pid = strtol (string, NULL, 10);
}
if (! emacs_pid) /* emacs_pid should be set above */
{
reset_tty ();
fprintf (stderr, "%s: %s\n", progname, string);
return 0;
}
}
}
......@@ -822,7 +849,7 @@ main (argc, argv)
argv[0]);
fprintf (stderr, "on systems with Berkeley sockets.\n");
fail (argc, argv);
fail ();
}
#else /* HAVE_SOCKETS */
......@@ -891,7 +918,7 @@ main (argc, argv)
{
fprintf (stderr, "%s: ", argv[0]);
perror ("socket");
fail (argc, argv);
fail ();
}
server.sun_family = AF_UNIX;
......@@ -922,7 +949,8 @@ main (argc, argv)
{
int sock_status = 0;
int oerrno = 0;
if (! socket_name)
{
socket_name = alloca (system_name_length + 100);
......@@ -933,11 +961,15 @@ main (argc, argv)
if (strlen (socket_name) < sizeof (server.sun_path))
strcpy (server.sun_path, socket_name);
else
fprintf (stderr, "%s: socket-name %s too long",
argv[0], socket_name);
{
fprintf (stderr, "%s: socket-name %s too long",
argv[0], socket_name);
fail ();
}
/* See if the socket exists, and if it's owned by us. */
sock_status = socket_status (server.sun_path);
oerrno = errno;
if (sock_status)
{
/* Failing that, see if LOGNAME or USER exist and differ from
......@@ -958,6 +990,7 @@ main (argc, argv)
sprintf (server.sun_path, "/tmp/esrv%d-%s",
(int) pw->pw_uid, system_name);
sock_status = socket_status (server.sun_path);
oerrno = errno;
}
}
}
......@@ -970,7 +1003,7 @@ main (argc, argv)
if (0 != geteuid ())
{
fprintf (stderr, "%s: Invalid socket owner\n", argv[0]);
fail (argc, argv);
fail ();
}
break;
......@@ -978,13 +1011,13 @@ main (argc, argv)
/* `stat' failed */
if (errno == ENOENT)
fprintf (stderr,
"%s: can't find socket; have you started the server?\n\
"%s: Can't find socket; have you started the server?\n\
To start the server in Emacs, type \"M-x server-start\".\n",
argv[0]);
else
fprintf (stderr, "%s: can't stat %s: %s\n",
argv[0], server.sun_path, strerror (errno));
fail (argc, argv);
fprintf (stderr, "%s: Can't stat %s: %s\n",
argv[0], server.sun_path, strerror (oerrno));
fail ();
break;
}
}
......@@ -994,7 +1027,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
{
fprintf (stderr, "%s: ", argv[0]);
perror ("connect");
fail (argc, argv);
fail ();
}
/* We use the stream OUT to send our command to the server. */
......@@ -1002,7 +1035,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
{
fprintf (stderr, "%s: ", argv[0]);
perror ("fdopen");
fail (argc, argv);
fail ();
}
/* We use the stream IN to read the response.
......@@ -1014,7 +1047,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
{
fprintf (stderr, "%s: ", argv[0]);
perror ("fdopen");
fail (argc, argv);
fail ();
}
#ifdef HAVE_GETCWD
......@@ -1032,7 +1065,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
#else
fprintf (stderr, "%s: %s (%s)\n", argv[0], string, strerror (errno));
#endif
fail (argc, argv);
fail ();
}
if (nowait)
......@@ -1054,7 +1087,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
{
fprintf (stderr, "%s: ", argv[0]);
perror ("fdopen");
fail (argc, argv);
fail ();
}
if (! init_tty ())
......@@ -1062,7 +1095,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
reset_tty ();
fprintf (stderr, "%s: ", argv[0]);
perror ("fdopen");
fail (argc, argv);
fail ();
}
if (! init_pty ())
......@@ -1070,10 +1103,10 @@ To start the server in Emacs, type \"M-x server-start\".\n",
reset_tty ();
fprintf (stderr, "%s: ", argv[0]);
perror ("fdopen");
fail (argc, argv);
fail ();
}
fprintf (out, "-pty ");
fprintf (out, "-tty ");
quote_file_name (pty_name, out);
fprintf (out, " ");
quote_file_name (getenv("TERM"), out);
......@@ -1133,11 +1166,8 @@ To start the server in Emacs, type \"M-x server-start\".\n",
if (! pty_conversation (out))
{
reset_tty ();
fprintf (stderr, "%s: ", argv[0]);
perror ("fdopen");
fail (argc, argv);
fail ();
}
close (master);
reset_tty ();
return 0;
}
......
......@@ -185,9 +185,6 @@ are done with it in the server.")
;; Remove PROC from the list of clients.
(when client
(setq server-clients (delq client server-clients))
(let ((frame (assq (car client) server-frames)))
(setq server-frames (delq frame server-frames))
(when (frame-live-p (cadr frame)) (delete-frame (cadr frame) 'force)))
(dolist (buf (cdr client))
(with-current-buffer buf
;; Remove PROC from the clients of each buffer.
......@@ -197,9 +194,24 @@ are done with it in the server.")
(or (and server-kill-new-buffers
(not server-existing-buffer))
(server-temp-file-p)))
(kill-buffer (current-buffer)))))))
(kill-buffer (current-buffer)))))
(let ((frame (assq (car client) server-frames)))
(when frame
(setq server-frames (delq frame server-frames))
(when (frame-live-p (cadr frame)) (delete-frame (cadr frame) 'force))))))
(server-log (format "Status changed to %s" (process-status proc)) proc))
(defun server-handle-delete-frame (frame)
(dolist (entry server-frames)
(let ((proc (nth 0 entry))
(f (nth 1 entry)))
(when (eq f frame)
(let ((client (assq proc server-clients)))
(if (and (cdr client) (not (yes-or-no-p "Frame has pending buffers; close anyway? ")))
(error "Frame deletion cancelled")
(setq server-frames (delq entry server-frames))
(delete-process (car client))))))))
(defun server-select-display (display)
;; If the current frame is on `display' we're all set.
(unless (equal (frame-parameter (selected-frame) 'display) display)
......@@ -276,6 +288,7 @@ Prefix arg means just kill any existing server communications subprocess."
(if server-process
(server-log (message "Restarting server")))
(letf (((default-file-modes) ?\700))
(add-to-list 'delete-frame-functions 'server-handle-delete-frame)
(setq server-process
(make-network-process
:name "server" :family 'local :server t :noquery t
......@@ -335,18 +348,18 @@ PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"."
(error (process-send-string proc (nth 1 err))
(setq request "")))))
;; Open a new frame at the client. ARG is the name of the pseudo tty.
((and (equal "-pty" arg) (string-match "\\([^ ]*\\) \\([^ ]*\\) " request))
(setq newframe t)
(let ((pty (server-unquote-arg (match-string 1 request)))
((and (equal "-tty" arg) (string-match "\\([^ ]*\\) \\([^ ]*\\) " request))
(let ((tty (server-unquote-arg (match-string 1 request)))
(type (server-unquote-arg (match-string 2 request))))
(setq request (substring request (match-end 0)))
(condition-case err
(let ((frame (make-terminal-frame `((tty . ,pty) (tty-type . ,type)))))
(let ((frame (make-frame-on-tty tty type)))
(setq server-frames (cons (list (car client) frame) server-frames))
(sit-for 0)
(process-send-string proc (concat (number-to-string (emacs-pid)) "\n"))
(select-frame frame))
(error (process-send-string proc (nth 1 err))
(select-frame frame)
(setq newframe t))
(error (ignore-errors (process-send-string proc (concat (nth 1 err) "\n")))
(setq request "")))))
;; ARG is a line number option.
((string-match "\\`\\+[0-9]+\\'" arg)
......@@ -386,19 +399,19 @@ PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"."
(if (and (not newframe) (null (cdr client)))
;; This client is empty; get rid of it immediately.
(progn
(let ((frame (assq (car client) server-frames)))
(setq server-frames (delq frame server-frames))
(when (frame-live-p (cadr frame)) (delete-frame (cadr frame) 'force)))
(delete-process proc)
(server-log "Close empty client" proc))
;; We visited some buffer for this client.
(or nowait (push client server-clients))
(unless (or isearch-mode (minibufferp))
(server-switch-buffer (nth 1 client))
(run-hooks 'server-switch-hook)
(unless nowait
(message (substitute-command-keys
"When done with a buffer, type \\[server-edit]")))))))
(if (and newframe (null (cdr client)))
(message (substitute-command-keys
"When done with this frame, type \\[delete-frame]"))
(server-switch-buffer (nth 1 client))
(run-hooks 'server-switch-hook)
(unless nowait
(message (substitute-command-keys
"When done with a buffer, type \\[server-edit]"))))))))
;; Save for later any partial line that remains.
(when (> (length string) 0)
(process-put proc 'previous-string string)))
......@@ -475,9 +488,6 @@ FOR-KILLING if non-nil indicates that we are called from `kill-buffer'."
;; If client now has no pending buffers,
;; tell it that it is done, and forget it entirely.
(unless (cdr client)
(let ((frame (assq (car client) server-frames)))
(setq server-frames (delq frame server-frames))
(when (frame-live-p (cadr frame)) (delete-frame (cadr frame) 'force)))
(delete-process (car client))
(server-log "Close" (car client))
(setq server-clients (delq client server-clients))))
......
......@@ -256,6 +256,7 @@ EMACS_INT gcs_done; /* accumulated GCs */
static void mark_buffer P_ ((Lisp_Object));
extern void mark_kboards P_ ((void));
extern void mark_ttys P_ ((void));
static void gc_sweep P_ ((void));
static void mark_glyph_matrix P_ ((struct glyph_matrix *));
static void mark_face_cache P_ ((struct face_cache *));
......@@ -4441,6 +4442,7 @@ returns nil, because real GC can't be done. */)
mark_object (backlist->args[i]);
}
mark_kboards ();
mark_ttys ();
/* Look thru every buffer's undo list
for elements that update markers that were not marked,
......
......@@ -1990,7 +1990,6 @@ shut_down_emacs (sig, no_x, stuff)
if (EMACS_GET_TTY_PGRP (0, &tpgrp) != -1
&& tpgrp == pgrp)
{
fflush (stdout);
reset_all_sys_modes ();
if (sig && sig != SIGTERM)
fprintf (stderr, "Fatal error (%d)", sig);
......
......@@ -275,11 +275,11 @@ extern int keyboard_bit_set P_ ((SELECT_TYPE *));
static SELECT_TYPE input_wait_mask;
/* Mask that excludes keyboard input descriptor (s). */
/* Mask that excludes keyboard input descriptor(s). */
static SELECT_TYPE non_keyboard_wait_mask;
/* Mask that excludes process input descriptor (s). */