summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaroly Lorentey <lorentey@elte.hu>2004-02-28 01:23:39 +0000
committerKaroly Lorentey <lorentey@elte.hu>2004-02-28 01:23:39 +0000
commit0b0d3e0bcefdde298893aaad2e816e1503cef222 (patch)
tree439342044857514ad784de0470f5546eb9c3ef9a
parent2fc0cf2aefa777e5fe48596e2d43774b28051931 (diff)
downloademacs-0b0d3e0bcefdde298893aaad2e816e1503cef222.tar.gz
Implemented suspending of emacsclient frames.
lib-src/emacsclient.c (quote_file_name): Renamed to quote_argument. (unquote_argument, handle_sigcont, handle_sigtstp): New functions. (out, in): New global variables for communicating with the Emacs process. (init_signals): Set up handlers for SIGCONT, SIGTSTP and SIGTTOU. (main): Changed out and in to global variables. Prepend `-eval' or '-file' to each argument. Use fsync to force sending the strings to Emacs. Removed obsolete -bad-version code. Support the -suspend command. Cleaned up newline handling. lisp/frame.el (suspend-frame): New function. Substitute key definition of suspend-emacs with suspend-frame. lisp/server.el (server-log): Cosmetic change in log format. (server-handle-delete-tty, server-handle-delete-frame): Added logging. (server-handle-suspend-tty, server-quote-arg): New functions. (server-start): Install server-handle-suspend-tty. (server-process-filter): Reorganized source code for clarity. Implemented -resume, -suspend and -ignore commands. lisp/term/x-win.el (x-initialize-window-system): Don't change the binding of C-z. src/cm.c: Replaced TTY_INPUT, TTY_OUTPUT, TTY_TERMSCRIPT calls with their macro expansion. src/dispnew.c: Ditto. src/frame.c: Ditto. src/keyboard.c: Ditto. src/sysdep.c: Ditto. src/keyboard.c (tty_read_avail_input): Don't read if the terminal is suspended. src/sysdep.c (discard_tty_input, init_sys_modes, reset_sys_modes): Ditto. src/term.c (tty_set_terminal_modes, tty_reset_terminal_modes): Ditto. src/term.c (Vsuspend_tty_functions, Vresume_tty_functions): New hooks. (syms_of_term): Defvar them. (term_init): Don't allow opening a new frame on a suspended tty device. (Fsuspend_tty, Fresume_tty): New functions. (syms_of_term): Defsubr them. src/termchar.c (struct tty_display_info): Update documentation of input and output. (TTY_INPUT, TTY_OUTPUT, TTY_TERMSCRIPT): Removed. git-archimport-id: lorentey@elte.hu--2004/emacs--multi-tty--0--patch-105
-rw-r--r--README.multi-tty57
-rw-r--r--lib-src/emacsclient.c244
-rw-r--r--lisp/frame.el18
-rw-r--r--lisp/server.el296
-rw-r--r--lisp/term/x-win.el6
-rw-r--r--src/cm.c18
-rw-r--r--src/dispnew.c58
-rw-r--r--src/frame.c5
-rw-r--r--src/keyboard.c15
-rw-r--r--src/sysdep.c96
-rw-r--r--src/term.c234
-rw-r--r--src/termchar.h10
-rw-r--r--src/termhooks.h1
13 files changed, 741 insertions, 317 deletions
diff --git a/README.multi-tty b/README.multi-tty
index 68cae7a2060..43dfdcbc91f 100644
--- a/README.multi-tty
+++ b/README.multi-tty
@@ -200,22 +200,50 @@ THINGS TO DO
argument-handling is done in Lisp, so this should be quite easy to
implement.
-** Very strange bug: visible-bell does not work on secondary
- terminals. This might be something xterm (konsole) specific.
+** Make `struct display' accessible to Lisp programs. Accessor functions:
-** Find out the best way to support suspending Emacs with multiple
- ttys. My guess: disable it on the controlling tty, but from other
- ttys 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.)
+ (displayp OBJECT): Returns t if OBJECT is a display.
-** Clean up the frame-local variable system. I think it's ugly and
- error-prone. But maybe I just haven't yet fully understood it.
+ (selected-display): Returns the display object of the selected frame.
+
+ (frame-display FRAME): Returns the display object of FRAME.
+
+ (display-frames DISPLAY): Returns a list of frames on DISPLAY.
+
+ (display-type DISPLAY): Returns the type of DISPLAY, as a
+ symbol. (See `framep'.)
+
+ (display-device DISPLAY): Returns the name of the device that
+ DISPLAY uses, as a string. (E.g: "/dev/pts/16", or
+ ":0.0")
+
+ See next issue why this is necessary.
+
+** The following needs to be supported:
+
+ $ emacsclient -t
+ C-z
+ $ bg
+ $ emacsclient -t
+ (This fails now.)
+
+ The cleanest way to solve this is to allow multiple displays on the
+ same terminal device; each new emacsclient process should create
+ its own display. As displays are currently identified by their
+ device names, this is not possible until struct display becomes
+ accessible as a Lisp-level object.
** Add an elaborate mechanism for display-local variables. (There are
already a few of these; search for `terminal-local' in the Elisp
manual.)
+** Very strange bug: visible-bell does not work on secondary
+ terminals in xterm and konsole. The screen does flicker a bit,
+ but it's so quick it isn't noticable.
+
+** Clean up the frame-local variable system. I think it's ugly and
+ error-prone. But maybe I just haven't yet fully understood it.
+
** Move baud_rate to struct display.
** Implement support for starting an interactive Emacs session without
@@ -667,4 +695,15 @@ DIARY OF CHANGES
complaints seem to be caused by bugs in term.el; they are not
related to multi-tty.)
+-- Find out the best way to support suspending Emacs with multiple
+ ttys. My guess: disable it on the controlling tty, but from other
+ ttys 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.)
+
+ (Done. I needed to play with signal handling and the server
+ protocol a bit to make emacsclient behave as a normal UNIX program
+ wrt foreground/background process groups.)
+
+
;;; arch-tag: 8da1619e-2e79-41a8-9ac9-a0485daad17d
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index c1c8ee8f160..79642cbe47e 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -114,7 +114,7 @@ decode_options (argc, argv)
display = getenv ("DISPLAY");
if (display && strlen (display) == 0)
display = NULL;
-
+
if (display)
window_system = 1;
else
@@ -169,7 +169,7 @@ decode_options (argc, argv)
window_system = 0;
tty = 0;
break;
-
+
case 'H':
print_help_and_exit ();
break;
@@ -212,19 +212,21 @@ Report bugs to bug-gnu-emacs@gnu.org.\n", progname);
exit (0);
}
-/* In NAME, insert a & before each &, each space, each newline, and
+/* In STR, insert a & before each &, each space, each newline, and
any initial -. Change spaces to underscores, too, so that the
- return value never contains a space. */
+ return value never contains a space.
+
+ Does not change the string. Outputs the result to STREAM. */
void
-quote_file_name (name, stream)
- char *name;
+quote_argument (str, stream)
+ char *str;
FILE *stream;
{
- char *copy = (char *) malloc (strlen (name) * 2 + 1);
+ char *copy = (char *) malloc (strlen (str) * 2 + 1);
char *p, *q;
- p = name;
+ p = str;
q = copy;
while (*p)
{
@@ -242,7 +244,7 @@ quote_file_name (name, stream)
}
else
{
- if (*p == '&' || (*p == '-' && p == name))
+ if (*p == '&' || (*p == '-' && p == str))
*q++ = '&';
*q++ = *p++;
}
@@ -254,6 +256,41 @@ quote_file_name (name, stream)
free (copy);
}
+
+/* The inverse of quote_argument. Removes quoting in string STR by
+ modifying the string in place. Returns STR. */
+
+char *
+unquote_argument (str)
+ char *str;
+{
+ char *p, *q;
+
+ if (! str)
+ return str;
+
+ p = str;
+ q = str;
+ while (*p)
+ {
+ if (*p == '&')
+ {
+ p++;
+ if (*p == '&')
+ *p = '&';
+ else if (*p == '_')
+ *p = ' ';
+ else if (*p == 'n')
+ *p = '\n';
+ else if (*p == '-')
+ *p = '-';
+ }
+ *q++ = *p++;
+ }
+ *q = 0;
+ return str;
+}
+
/* Like malloc but get fatal error if memory is exhausted. */
long *
@@ -288,8 +325,12 @@ fail (void)
}
}
+/* The process id of Emacs. */
int emacs_pid;
+/* File handles for communicating with Emacs. */
+FILE *out, *in;
+
/* A signal handler that passes the signal to the Emacs process.
Useful for SIGWINCH. */
@@ -305,8 +346,62 @@ pass_signal_to_emacs (int signalnum)
errno = old_errno;
}
+/* Signal handler for SIGCONT; notify the Emacs process that it can
+ now resume our tty frame. */
+
+SIGTYPE
+handle_sigcont (int signalnum)
+{
+ int old_errno = errno;
+
+ if (tcgetpgrp (1) == getpgrp ())
+ {
+ /* We are in the foreground. */
+ fprintf (out, "-resume \n");
+ fflush (out);
+ fsync (fileno (out));
+ }
+ else
+ {
+ /* We are in the background; cancel the continue. */
+ kill (getpid (), SIGSTOP);
+ }
+ errno = old_errno;
+}
+
+/* Signal handler for SIGTSTP; notify the Emacs process that we are
+ going to sleep. Normally the suspend is initiated by Emacs via
+ server-handle-suspend-tty, but if the server gets out of sync with
+ reality, we may get a SIGTSTP on C-z. Handling this signal and
+ notifying Emacs about it should get things under control again. */
+
+SIGTYPE
+handle_sigtstp (int signalnum)
+{
+ int old_errno = errno;
+ sigset_t set;
+
+ if (out)
+ {
+ fprintf (out, "-suspend \n");
+ fflush (out);
+ fsync (fileno (out));
+ }
+
+ /* Unblock this signal and call the default handler by temprarily
+ changing the handler and resignalling. */
+ sigprocmask (SIG_BLOCK, NULL, &set);
+ sigdelset (&set, signalnum);
+ signal (signalnum, SIG_DFL);
+ kill (getpid (), signalnum);
+ sigprocmask (SIG_SETMASK, &set, NULL); /* Let's the above signal through. */
+ signal (signalnum, handle_sigtstp);
+
+ errno = old_errno;
+}
+
/* Set up signal handlers before opening a frame on the current tty. */
-
+
void
init_signals (void)
{
@@ -320,6 +415,10 @@ init_signals (void)
signal (SIGINT, pass_signal_to_emacs);
signal (SIGQUIT, pass_signal_to_emacs);
#endif
+
+ signal (SIGCONT, handle_sigcont);
+ signal (SIGTSTP, handle_sigtstp);
+ signal (SIGTTOU, handle_sigtstp);
}
@@ -378,7 +477,7 @@ strprefix (char *prefix, char *string)
if (!string)
return 0;
-
+
for (i = 0; prefix[i]; i++)
if (!string[i] || string[i] != prefix[i])
return 0;
@@ -391,7 +490,6 @@ main (argc, argv)
char **argv;
{
int s, i, needlf = 0;
- FILE *out, *in;
struct sockaddr_un server;
char *cwd, *str;
char string[BUFSIZ];
@@ -427,9 +525,9 @@ main (argc, argv)
int sock_status = 0;
int default_sock = !socket_name;
int saved_errno = 0;
-
+
char *server_name = "server";
-
+
if (socket_name && !index (socket_name, '/') && !index (socket_name, '\\'))
{ /* socket_name is a file name component. */
server_name = socket_name;
@@ -571,17 +669,14 @@ To start the server in Emacs, type \"M-x server-start\".\n",
/* First of all, send our version number for verification. */
fprintf (out, "-version %s ", VERSION);
-
+
if (nowait)
fprintf (out, "-nowait ");
- if (eval)
- fprintf (out, "-eval ");
-
if (display)
{
fprintf (out, "-display ");
- quote_file_name (display, out);
+ quote_argument (display, out);
fprintf (out, " ");
}
@@ -589,7 +684,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
{
char *tty_name = ttyname (fileno (stdin));
char *type = getenv ("TERM");
-
+
if (! tty_name)
{
fprintf (stderr, "%s: could not get terminal name\n", progname);
@@ -610,44 +705,60 @@ To start the server in Emacs, type \"M-x server-start\".\n",
" is not supported\n", progname);
fail ();
}
-
+
init_signals ();
-
+
fprintf (out, "-tty ");
- quote_file_name (tty_name, out);
+ quote_argument (tty_name, out);
fprintf (out, " ");
- quote_file_name (type, out);
+ quote_argument (type, out);
fprintf (out, " ");
}
if (window_system)
fprintf (out, "-window-system ");
-
+
if ((argc - optind > 0))
{
for (i = optind; i < argc; i++)
{
+ int relative = 0;
+
if (eval)
- ; /* Don't prepend any cwd or anything like that. */
- else if (*argv[i] == '+')
- {
+ {
+ /* Don't prepend any cwd or anything like that. */
+ fprintf (out, "-eval ");
+ quote_argument (argv[i], out);
+ fprintf (out, " ");
+ continue;
+ }
+
+ if (*argv[i] == '+')
+ {
char *p = argv[i] + 1;
while (isdigit ((unsigned char) *p) || *p == ':') p++;
- if (*p != 0)
- {
- quote_file_name (cwd, out);
- fprintf (out, "/");
- }
- }
- else if (*argv[i] != '/')
- {
- quote_file_name (cwd, out);
- fprintf (out, "/");
- }
-
- quote_file_name (argv[i], out);
- fprintf (out, " ");
- }
+ if (*p == 0)
+ {
+ fprintf (out, "-position ");
+ quote_argument (argv[i], out);
+ fprintf (out, " ");
+ continue;
+ }
+ else
+ relative = 1;
+ }
+ else if (*argv[i] != '/')
+ relative = 1;
+
+ fprintf (out, "-file ");
+ if (relative)
+ {
+ quote_argument (cwd, out);
+ fprintf (out, "/");
+ }
+ quote_argument (argv[i], out);
+ fprintf (out, " ");
+ }
}
else
{
@@ -655,14 +766,19 @@ To start the server in Emacs, type \"M-x server-start\".\n",
{
while ((str = fgets (string, BUFSIZ, stdin)))
{
- quote_file_name (str, out);
+ if (eval)
+ fprintf (out, "-eval ");
+ else
+ fprintf (out, "-file ");
+ quote_argument (str, out);
}
fprintf (out, " ");
}
}
-
+
fprintf (out, "\n");
fflush (out);
+ fsync (fileno (out));
/* Maybe wait for an answer. */
if (nowait)
@@ -676,44 +792,49 @@ To start the server in Emacs, type \"M-x server-start\".\n",
needlf = 2;
}
fflush (stdout);
+ fsync (1);
/* Now, wait for an answer and print any messages. */
while ((str = fgets (string, BUFSIZ, in)))
{
+ char *p = str + strlen (str) - 1;
+ while (p > str && *p == '\n')
+ *p-- = 0;
+
if (strprefix ("-good-version ", str))
{
/* OK, we got the green light. */
}
- else if (strprefix ("-bad-version ", str))
- {
- if (str[strlen (str) - 1] == '\n')
- str[strlen (str) - 1] = 0;
-
- fprintf (stderr, "%s: Version mismatch: Emacs is %s, but we are %s\n",
- argv[0], str + strlen ("-bad-version "), VERSION);
- fail ();
- }
else if (strprefix ("-emacs-pid ", str))
{
emacs_pid = strtol (string + strlen ("-emacs-pid"), NULL, 10);
}
else if (strprefix ("-print ", str))
{
- if (needlf == 2)
+ str = unquote_argument (str + strlen ("-print "));
+ if (needlf)
printf ("\n");
- printf ("%s", str + strlen ("-print "));
- needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
+ printf ("%s", str);
+ needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
}
else if (strprefix ("-error ", str))
{
- if (needlf == 2)
+ str = unquote_argument (str + strlen ("-error "));
+ if (needlf)
+ printf ("\n");
+ printf ("*ERROR*: %s", str);
+ needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
+ }
+ else if (strprefix ("-suspend ", str))
+ {
+ if (needlf)
printf ("\n");
- printf ("*ERROR*: %s", str + strlen ("-print "));
- needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
+ needlf = 0;
+ kill (0, SIGSTOP);
}
else
{
- if (needlf == 2)
+ if (needlf)
printf ("\n");
printf ("*ERROR*: Unknown message: %s", str);
needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n';
@@ -723,6 +844,7 @@ To start the server in Emacs, type \"M-x server-start\".\n",
if (needlf)
printf ("\n");
fflush (stdout);
+ fsync (1);
return 0;
}
diff --git a/lisp/frame.el b/lisp/frame.el
index 8d76c7b70ba..54bccd93970 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -750,6 +750,22 @@ Otherwise, that variable should be nil."
(iconify-frame)
(make-frame-visible)))
+(defun suspend-frame ()
+ "Do whatever is right to suspend the current frame.
+Calls `suspend-emacs' if invoked from the controlling terminal,
+`suspend-tty' from a secondary terminal, and
+`iconify-or-deiconify-frame' from an X frame."
+ (interactive)
+ (let ((type (framep (selected-frame))))
+ (cond
+ ((eq type 'x) (iconify-or-deiconify-frame))
+ ((eq type t)
+ (if (frame-tty-name)
+ (suspend-tty)
+ (suspend-emacs)))
+ (t (suspend-emacs)))))
+
+
(defun make-frame-names-alist ()
(let* ((current-frame (selected-frame))
(falist
@@ -1374,6 +1390,8 @@ Use Custom to set this variable to get the display updated."
(define-key ctl-x-5-map "0" 'delete-frame)
(define-key ctl-x-5-map "o" 'other-frame)
+(substitute-key-definition 'suspend-emacs 'suspend-frame global-map)
+
(provide 'frame)
;;; arch-tag: 82979c70-b8f2-4306-b2ad-ddbd6b328b56
diff --git a/lisp/server.el b/lisp/server.el
index a1619471e56..aac3da13e4f 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -186,7 +186,7 @@ are done with it in the server.")
(with-current-buffer "*server*"
(goto-char (point-max))
(insert (current-time-string)
- (if client (format " %s:" client) " ")
+ (if client (format " %s: " client) " ")
string)
(or (bolp) (newline)))))
@@ -227,6 +227,7 @@ are done with it in the server.")
(term (nth 1 entry)))
(when (equal term tty)
(let ((client (assq proc server-clients)))
+ (server-log (format "server-handle-delete-tty, tty %s" tty) (car client))
(setq server-ttys (delq entry server-ttys))
(delete-process (car client))
(when (assq proc server-clients)
@@ -234,6 +235,16 @@ are done with it in the server.")
;; `emacsclient -t -e '(delete-frame)'' correctly.
(setq server-clients (delq client server-clients))))))))
+(defun server-handle-suspend-tty (tty)
+ "Notify the emacsclient process to suspend itself when its tty device is suspended."
+ (dolist (entry server-ttys)
+ (let ((proc (nth 0 entry))
+ (term (nth 1 entry)))
+ (when (equal term tty)
+ (let ((process (car (assq proc server-clients))))
+ (server-log (format "server-handle-suspend-tty, tty %s" tty) process)
+ (process-send-string process "-suspend \n"))))))
+
(defun server-handle-delete-frame (frame)
"Delete the client connection when the emacsclient frame is deleted."
(dolist (entry server-frames)
@@ -241,6 +252,7 @@ are done with it in the server.")
(f (nth 1 entry)))
(when (equal frame f)
(let ((client (assq proc server-clients)))
+ (server-log (format "server-handle-delete-frame, frame %s" frame) (car client))
(setq server-frames (delq entry server-frames))
(delete-process (car client))
(when (assq proc server-clients)
@@ -278,6 +290,19 @@ are done with it in the server.")
(t " ")))
arg t t))
+(defun server-quote-arg (arg)
+ "In NAME, insert a & before each &, each space, each newline, and -.
+Change spaces to underscores, too, so that the return value never
+contains a space."
+ (replace-regexp-in-string
+ "[-&\n ]" (lambda (s)
+ (case (aref s 0)
+ (?& "&&")
+ (?- "&-")
+ (?\n "&n")
+ (?\s "&_")))
+ arg t t))
+
(defun server-ensure-safe-dir (dir)
"Make sure DIR is a directory with no race-condition issues.
Creates the directory if necessary and makes sure:
@@ -325,6 +350,7 @@ Prefix arg means just kill any existing server communications subprocess."
(server-log (message "Restarting server")))
(letf (((default-file-modes) ?\700))
(add-to-list 'delete-tty-after-functions 'server-handle-delete-tty)
+ (add-to-list 'suspend-tty-functions 'server-handle-suspend-tty)
(add-to-list 'delete-frame-functions 'server-handle-delete-frame)
(setq server-process
(make-network-process
@@ -358,140 +384,182 @@ PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"."
(setq string (concat prev string))
(process-put proc 'previous-string nil)))
(condition-case err
- ;; If the input is multiple lines,
- ;; process each line individually.
- (while (string-match "\n" string)
- (let ((request (substring string 0 (match-beginning 0)))
- (coding-system (and default-enable-multibyte-characters
- (or file-name-coding-system
- default-file-name-coding-system)))
- client nowait eval newframe display version-checked
- registered ; t if the client is already added to server-clients.
- (files nil)
- (lineno 1)
- (columnno 0))
- ;; Remove this line from STRING.
- (setq string (substring string (match-end 0)))
- (setq client (cons proc nil))
- (while (string-match "[^ ]* " request)
- (let ((arg (substring request (match-beginning 0) (1- (match-end 0)))))
- (setq request (substring request (match-end 0)))
- (cond
- ;; Check version numbers.
- ((and (equal "-version" arg) (string-match "\\([0-9.]+\\) " request))
- (let* ((client-version (match-string 1 request))
- (truncated-emacs-version (substring emacs-version 0 (length client-version))))
- (setq request (substring request (match-end 0)))
- (if (equal client-version truncated-emacs-version)
- (progn
- (process-send-string proc "-good-version \n")
- (setq version-checked t))
- (error (concat "Version mismatch: Emacs is " truncated-emacs-version ", emacsclient is " client-version)))))
-
- ((equal "-nowait" arg) (setq nowait t))
- ((equal "-eval" arg) (setq eval t))
-
- ((and (equal "-display" arg) (string-match "\\([^ ]*\\) " request))
- (setq display (match-string 1 request)
- request (substring request (match-end 0))))
-
- ;; Open a new X frame.
- ((equal "-window-system" arg)
- (unless version-checked
- (error "Protocol error; make sure to use the correct version of emacsclient"))
- (let ((frame (make-frame-on-display
- (or display
- (frame-parameter nil 'display)
- (getenv "DISPLAY")
- (error "Please specify display")))))
- (push (list proc frame) server-frames)
- (select-frame frame)
- ;; This makes sure that `emacsclient -w -e '(delete-frame)'' works right.
- (push client server-clients)
- (setq registered t
- newframe t)))
-
- ;; Open a new tty frame at the client. ARG is the name of the pseudo tty.
- ((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)))
+ (progn
+ ;; If the input is multiple lines,
+ ;; process each line individually.
+ (while (string-match "\n" string)
+ (let ((request (substring string 0 (match-beginning 0)))
+ (coding-system (and default-enable-multibyte-characters
+ (or file-name-coding-system
+ default-file-name-coding-system)))
+ client nowait newframe display version-checked
+ dontkill ; t if the client should not be killed.
+ registered ; t if the client is already added to server-clients.
+ (files nil)
+ (lineno 1)
+ (columnno 0))
+ ;; Remove this line from STRING.
+ (setq string (substring string (match-end 0)))
+ (setq client (cons proc nil))
+ (while (string-match "[^ ]* " request)
+ (let ((arg (substring request (match-beginning 0) (1- (match-end 0)))))
+ (setq request (substring request (match-end 0)))
+ (cond
+ ;; Check version numbers.
+ ((and (equal "-version" arg) (string-match "\\([0-9.]+\\) " request))
+ (let* ((client-version (match-string 1 request))
+ (truncated-emacs-version (substring emacs-version 0 (length client-version))))
+ (setq request (substring request (match-end 0)))
+ (if (equal client-version truncated-emacs-version)
+ (progn
+ (process-send-string proc "-good-version \n")
+ (setq version-checked t))
+ (error (concat "Version mismatch: Emacs is " truncated-emacs-version ", emacsclient is " client-version)))))
+
+ ((equal "-nowait" arg) (setq nowait t))
+
+ ((and (equal "-display" arg) (string-match "\\([^ ]*\\) " request))
+ (setq display (match-string 1 request)
+ request (substring request (match-end 0))))
+
+ ;; Open a new X frame.
+ ((equal "-window-system" arg)
(unless version-checked
(error "Protocol error; make sure to use the correct version of emacsclient"))
- (let ((frame (make-frame-on-tty tty type)))
- (push (list (car client) (frame-tty-name frame)) server-ttys)
- (process-send-string proc (concat "-emacs-pid " (number-to-string (emacs-pid)) "\n"))
+ (let ((frame (make-frame-on-display
+ (or display
+ (frame-parameter nil 'display)
+ (getenv "DISPLAY")
+ (error "Please specify display")))))
+ (push (list proc frame) server-frames)
(select-frame frame)
- ;; This makes sure that `emacsclient -t -e '(delete-frame)'' works right.
+ ;; This makes sure that `emacsclient -w -e '(delete-frame)'' works right.
(push client server-clients)
(setq registered t
- newframe t))))
-
- ;; ARG is a line number option.
- ((string-match "\\`\\+[0-9]+\\'" arg)
- (setq lineno (string-to-int (substring arg 1))))
-
- ;; ARG is line number:column option.
- ((string-match "\\`\\+\\([0-9]+\\):\\([0-9]+\\)\\'" arg)
- (setq lineno (string-to-int (match-string 1 arg))
- columnno (string-to-int (match-string 2 arg))))
-
- ;; ARG is a filename or a Lisp expression.
- (t
- ;; Undo the quoting that emacsclient does
- ;; for certain special characters.
- (setq arg (server-unquote-arg arg))
- ;; Now decode the file name if necessary.
- (if coding-system
- (setq arg (decode-coding-string arg coding-system)))
- (unless version-checked
- (error "Protocol error; make sure to use the correct version of emacsclient"))
- (if eval
- ;; ARG is a Lisp expression.
- (let ((v (eval (car (read-from-string arg)))))
+ newframe t
+ dontkill t)))
+
+ ;; Resume a suspended tty frame.
+ ((equal "-resume" arg)
+ (let ((tty (cadr (assq (car client) server-ttys))))
+ (setq dontkill t)
+ (when tty (resume-tty tty))))
+
+ ;; Suspend the client's frame. (In case we get out of
+ ;; sync, and a C-z sends a SIGTSTP to emacsclient.)
+ ((equal "-suspend" arg)
+ (let ((tty (cadr (assq (car client) server-ttys))))
+ (setq dontkill t)
+ (when tty (suspend-tty tty))))
+
+ ;; Noop; useful for debugging emacsclient.
+ ((and (equal "-ignore" arg) (string-match "\\([^ ]*\\) " request))
+ (setq dontkill t
+ request (substring request (match-end 0))))
+
+ ;; Open a new tty frame at the client. ARG is the name of the pseudo tty.
+ ((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)))
+ (unless version-checked
+ (error "Protocol error; make sure to use the correct version of emacsclient"))
+ (let ((frame (make-frame-on-tty tty type)))
+ (push (list (car client) (frame-tty-name frame)) server-ttys)
+ (process-send-string proc (concat "-emacs-pid " (number-to-string (emacs-pid)) "\n"))
+ (select-frame frame)
+ ;; This makes sure that `emacsclient -t -e '(delete-frame)'' works right.
+ (push client server-clients)
+ (setq registered t
+ dontkill t
+ newframe t))))
+
+ ;; ARG is a line number option.
+ ((and (equal "-position" arg) (string-match "\\(\\+[0-9]+\\) " request))
+ (setq request (substring request (match-end 0))
+ lineno (string-to-int (substring (match-string 1 request) 1))))
+
+ ;; ARG is line number:column option.
+ ((and (equal "-position" arg) (string-match "\\+\\([0-9]+\\):\\([0-9]+\\) " request))
+ (setq request (substring request (match-end 0))
+ lineno (string-to-int (match-string 1 request))
+ columnno (string-to-int (match-string 2 request))))
+
+ ;; ARG is a file to load.
+ ((and (equal "-file" arg) (string-match "\\([^ ]+\\) " request))
+ (let ((file (server-unquote-arg (match-string 1 request))))
+ (setq request (substring request (match-end 0)))
+ (if coding-system
+ (setq file (decode-coding-string file coding-system)))
+ (setq file (command-line-normalize-file-name file))
+ (push (list file lineno columnno) files))
+ (setq lineno 1
+ columnno 0))
+
+ ;; ARG is a Lisp expression.
+ ((and (equal "-eval" arg) (string-match "\\([^ ]+\\) " request))
+ (let ((expr (server-unquote-arg (match-string 1 request))))
+ (setq request (substring request (match-end 0)))
+ (if coding-system
+ (setq expr (decode-coding-string expr coding-system)))
+ (let ((v (eval (car (read-from-string expr)))))
(when (and (not newframe) v)
(with-temp-buffer
(let ((standard-output (current-buffer)))
(pp v)
(process-send-string proc "-print ")
- (process-send-region proc (point-min) (point-max))))))
- ;; ARG is a file name.
- ;; Collapse multiple slashes to single slashes.
- (setq arg (command-line-normalize-file-name arg))
- (push (list arg lineno columnno) files))
- (setq lineno 1)
- (setq columnno 0)))))
-
- (if (not version-checked)
- (error "Protocol error; make sure to use the correct version of emacsclient")
+ (process-send-string
+ proc (server-quote-arg
+ (buffer-substring-no-properties (point-min)
+ (point-max))))
+ (process-send-string proc "\n")))))
+ (setq lineno 1
+ columnno 0)))
+
+ ;; Unknown command.
+ (t (error "Unknown command: %s" arg)))))
+
(when files
(run-hooks 'pre-command-hook)
(server-visit-files files client nowait)
(run-hooks 'post-command-hook))
+
;; CLIENT is now a list (CLIENTNUM BUFFERS...)
- (if (and (not newframe) (null (cdr client)))
- ;; This client is empty; get rid of it immediately.
- (progn
- (delete-process proc)
- (server-log "Close empty client" proc))
- ;; We visited some buffer for this client.
- (or nowait registered (push client server-clients))
- (unless (or isearch-mode (minibufferp))
- (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]"))))))))
+
+ ;; Delete the client if necessary.
+ (cond
+ ;; Client requested nowait; return immediately.
+ (nowait
+ (delete-process proc)
+ (server-log "Close nowait client" proc))
+ ;; This client is empty; get rid of it immediately.
+ ((and (not dontkill) (null (cdr client)))
+ (delete-process proc)
+ (server-log "Close empty client" proc))
+ ((not registered)
+ (push client server-clients)))
+
+ ;; We visited some buffer for this client.
+ (cond
+ ((or isearch-mode (minibufferp))
+ nil)
+ ((and newframe (null (cdr client)))
+ (message (substitute-command-keys
+ "When done with this frame, type \\[delete-frame]")))
+ ((not (null (cdr client)))
+ (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)))
;; condition-case
(error (ignore-errors
(process-send-string
- proc (concat "-error " (error-message-string err)))
+ proc (concat "-error " (server-quote-arg (error-message-string err))))
(setq string "")
(server-log (error-message-string err) proc)
(delete-process proc)))))
diff --git a/lisp/term/x-win.el b/lisp/term/x-win.el
index e09285e86c1..da5ac04a6c9 100644
--- a/lisp/term/x-win.el
+++ b/lisp/term/x-win.el
@@ -2442,11 +2442,7 @@ order until succeed.")
(if res-selection-timeout
(setq x-selection-timeout (string-to-number res-selection-timeout))))
- ;; XXX This is wrong in general with multi-tty support.
- (substitute-key-definition 'suspend-emacs 'iconify-or-deiconify-frame
- global-map)
-
- ;; XXX This is wrong in general with multi-tty support.
+ ;; Don't let Emacs suspend under X.
(add-hook 'suspend-hook 'x-win-suspend-error)
;; Arrange for the kill and yank functions to set and check the clipboard.
diff --git a/src/cm.c b/src/cm.c
index 5ce03483b06..9f9cc0e0a34 100644
--- a/src/cm.c
+++ b/src/cm.c
@@ -64,9 +64,9 @@ int
cmputc (c)
char c;
{
- if (TTY_TERMSCRIPT (current_tty))
- putc (c & 0177, TTY_TERMSCRIPT (current_tty));
- putc (c & 0177, TTY_OUTPUT (current_tty));
+ if (current_tty->termscript)
+ putc (c & 0177, current_tty->termscript);
+ putc (c & 0177, current_tty->output);
return c;
}
@@ -136,12 +136,12 @@ cmcheckmagic (struct tty_display_info *tty)
{
if (!MagicWrap (tty) || curY (tty) >= FrameRows (tty) - 1)
abort ();
- if (TTY_TERMSCRIPT (tty))
- putc ('\r', TTY_TERMSCRIPT (tty));
- putc ('\r', TTY_OUTPUT (tty));
- if (TTY_TERMSCRIPT (tty))
- putc ('\n', TTY_TERMSCRIPT (tty));
- putc ('\n', TTY_OUTPUT (tty));
+ if (tty->termscript)
+ putc ('\r', tty->termscript);
+ putc ('\r', tty->output);
+ if (tty->termscript)
+ putc ('\n', tty->termscript);
+ putc ('\n', tty->output);
curX (tty) = 0;
curY (tty)++;
}
diff --git a/src/dispnew.c b/src/dispnew.c
index aaf3c440f34..8a3d7013c3e 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -3316,7 +3316,7 @@ DEFUN ("redraw-frame", Fredraw_frame, Sredraw_frame, 1, 1, 0,
clear_current_matrices (f);
update_end (f);
if (FRAME_TERMCAP_P (f))
- fflush (TTY_OUTPUT (FRAME_TTY (f)));
+ fflush (FRAME_TTY (f)->output);
windows_or_buffers_changed++;
/* Mark all windows as inaccurate, so that every window will have
its redisplay done. */
@@ -3659,7 +3659,7 @@ direct_output_for_insert (g)
update_end (f);
updated_row = NULL;
if (FRAME_TERMCAP_P (f))
- fflush (TTY_OUTPUT (FRAME_TTY (f)));
+ fflush (FRAME_TTY (f)->output);
TRACE ((stderr, "direct output for insert\n"));
mark_window_display_accurate (it.window, 1);
@@ -3751,7 +3751,7 @@ direct_output_forward_char (n)
}
if (FRAME_TERMCAP_P (f))
- fflush (TTY_OUTPUT (FRAME_TTY (f)));
+ fflush (FRAME_TTY (f)->output);
redisplay_performed_directly_p = 1;
return 1;
}
@@ -3849,9 +3849,9 @@ update_frame (f, force_p, inhibit_hairy_id_p)
if (FRAME_TERMCAP_P (f))
{
- if (TTY_TERMSCRIPT (FRAME_TTY (f)))
- fflush (TTY_TERMSCRIPT (FRAME_TTY (f)));
- fflush (TTY_OUTPUT (FRAME_TTY (f)));
+ if (FRAME_TTY (f)->termscript)
+ fflush (FRAME_TTY (f)->termscript);
+ fflush (FRAME_TTY (f)->output);
}
/* Check window matrices for lost pointers. */
@@ -5133,18 +5133,18 @@ update_frame_1 (f, force_p, inhibit_id_p)
Also flush out if likely to have more than 1k buffered
otherwise. I'm told that some telnet connections get
really screwed by more than 1k output at once. */
- int outq = PENDING_OUTPUT_COUNT (TTY_OUTPUT (FRAME_TTY (f)));
+ int outq = PENDING_OUTPUT_COUNT (FRAME_TTY (f)->output);
if (outq > 900
|| (outq > 20 && ((i - 1) % preempt_count == 0)))
{
- fflush (TTY_OUTPUT (FRAME_TTY (f)));
+ fflush (FRAME_TTY (f)->output);
if (preempt_count == 1)
{
#ifdef EMACS_OUTQSIZE
if (EMACS_OUTQSIZE (0, &outq) < 0)
/* Probably not a tty. Ignore the error and reset
the outq count. */
- outq = PENDING_OUTPUT_COUNT (TTY_OUTPUT (FRAME_TTY (f)));
+ outq = PENDING_OUTPUT_COUNT (FRAME_TTY (f->output));
#endif
outq *= 10;
if (baud_rate <= outq && baud_rate > 0)
@@ -5999,7 +5999,7 @@ window_change_signal (signalnum) /* If we don't have an argument, */
if (! tty->term_initted)
continue;
- get_tty_size (fileno (TTY_INPUT (tty)), &width, &height);
+ get_tty_size (fileno (tty->input), &width, &height);
{
Lisp_Object tail, frame;
@@ -6211,15 +6211,22 @@ FILE = nil means just close any termscript file currently open. */)
(file)
Lisp_Object file;
{
- if (TTY_TERMSCRIPT (CURTTY ()) != 0)
- fclose (TTY_TERMSCRIPT (CURTTY ()));
- TTY_TERMSCRIPT (CURTTY ()) = 0;
+ struct tty_display_info *tty;
+
+ if (! FRAME_TERMCAP_P (SELECTED_FRAME ()))
+ error ("Current frame is not on a tty device");
+
+ tty = CURTTY ();
+
+ if (tty->termscript != 0)
+ fclose (tty->termscript);
+ tty->termscript = 0;
if (! NILP (file))
{
file = Fexpand_file_name (file, Qnil);
- TTY_TERMSCRIPT (CURTTY ()) = fopen (SDATA (file), "w");
- if (TTY_TERMSCRIPT (CURTTY ()) == 0)
+ tty->termscript = fopen (SDATA (file), "w");
+ if (tty->termscript == 0)
report_file_error ("Opening termscript", Fcons (file, Qnil));
}
return Qnil;
@@ -6233,20 +6240,23 @@ Control characters in STRING will have terminal-dependent effects. */)
(string)
Lisp_Object string;
{
+ struct tty_display_info *tty;
+
/* ??? Perhaps we should do something special for multibyte strings here. */
CHECK_STRING (string);
+
if (! FRAME_TERMCAP_P (SELECTED_FRAME ()))
error ("Current frame is not on a tty device");
+
+ tty = CURTTY ();
- if (TTY_TERMSCRIPT (CURTTY ()))
+ if (tty->termscript)
{
- fwrite (SDATA (string), 1, SBYTES (string),
- TTY_TERMSCRIPT (CURTTY ()));
- fflush (TTY_TERMSCRIPT (CURTTY ()));
+ fwrite (SDATA (string), 1, SBYTES (string), tty->termscript);
+ fflush (tty->termscript);
}
- fwrite (SDATA (string), 1, SBYTES (string),
- TTY_OUTPUT (CURTTY ()));
- fflush (TTY_OUTPUT (CURTTY ()));
+ fwrite (SDATA (string), 1, SBYTES (string), tty->output);
+ fflush (tty->output);
return Qnil;
}
@@ -6265,7 +6275,7 @@ terminate any keyboard macro currently executing. */)
else
ring_bell ();
if (FRAME_TERMCAP_P (XFRAME (selected_frame)))
- fflush (TTY_OUTPUT (CURTTY ()));
+ fflush (CURTTY ()->output);
}
else
bitch_at_user ();
@@ -6283,7 +6293,7 @@ bitch_at_user ()
else
ring_bell ();
if (FRAME_TERMCAP_P (XFRAME (selected_frame)))
- fflush (TTY_OUTPUT (CURTTY ()));
+ fflush (CURTTY ()->output);
}
diff --git a/src/frame.c b/src/frame.c
index 8ffabfa8b89..c7b5491500f 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -667,7 +667,8 @@ and the `tty-type' parameter specifies the terminal type. Example:
(make-terminal-frame '((tty . "/dev/pts/5") (tty-type . "xterm")))
-Note that changing the size of one terminal frame automatically affects all. */)
+Note that changing the size of one terminal frame automatically
+affects all frames on the same terminal device. */)
(parms)
Lisp_Object parms;
{
@@ -742,7 +743,7 @@ Note that changing the size of one terminal frame automatically affects all. */
{
int width, height;
- get_tty_size (fileno (TTY_INPUT (FRAME_TTY (f))), &width, &height);
+ get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height);
change_frame_size (f, height, width, 0, 0, 0);
}
diff --git a/src/keyboard.c b/src/keyboard.c
index 2c6edc68f99..5bcd53a260f 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -6704,10 +6704,13 @@ tty_read_avail_input (struct display *display,
if (! tty->term_initted) /* In case we get called during bootstrap. */
return 0;
+ if (! tty->input)
+ return 0; /* The terminal is suspended. */
+
/* Determine how many characters we should *try* to read. */
#ifdef FIONREAD
/* Find out how much input is available. */
- if (ioctl (fileno (TTY_INPUT (tty)), FIONREAD, &n_to_read) < 0)
+ if (ioctl (fileno (tty->input), FIONREAD, &n_to_read) < 0)
{
if (! noninteractive)
return -2; /* Close this display. */
@@ -6722,7 +6725,7 @@ tty_read_avail_input (struct display *display,
#if defined (USG) || defined (DGUX) || defined(CYGWIN)
/* Read some input if available, but don't wait. */
n_to_read = sizeof cbuf;
- fcntl (fileno (TTY_INPUT (tty)), F_SETFL, O_NDELAY);
+ fcntl (fileno (tty->input), F_SETFL, O_NDELAY);
#else
you lose;
#endif
@@ -6732,7 +6735,7 @@ tty_read_avail_input (struct display *display,
NREAD is set to the number of chars read. */
do
{
- nread = emacs_read (fileno (TTY_INPUT (tty)), cbuf, n_to_read);
+ nread = emacs_read (fileno (tty->input), cbuf, n_to_read);
/* POSIX infers that processes which are not in the session leader's
process group won't get SIGHUP's at logout time. BSDI adheres to
this part standard and returns -1 from read (0) with errno==EIO
@@ -6770,7 +6773,7 @@ tty_read_avail_input (struct display *display,
#ifndef FIONREAD
#if defined (USG) || defined (DGUX) || defined (CYGWIN)
- fcntl (fileno (TTY_INPUT (tty)), F_SETFL, 0);
+ fcntl (fileno (tty->input), F_SETFL, 0);
#endif /* USG or DGUX or CYGWIN */
#endif /* no FIONREAD */
@@ -10168,7 +10171,7 @@ On such systems, Emacs starts a subshell instead of suspending. */)
call1 (Vrun_hooks, intern ("suspend-hook"));
GCPRO1 (stuffstring);
- get_tty_size (fileno (TTY_INPUT (CURTTY ())), &old_width, &old_height);
+ get_tty_size (fileno (CURTTY ()->input), &old_width, &old_height);
reset_all_sys_modes ();
/* sys_suspend can get an error if it tries to fork a subshell
and the system resources aren't available for that. */
@@ -10184,7 +10187,7 @@ On such systems, Emacs starts a subshell instead of suspending. */)
/* Check if terminal/window size has changed.
Note that this is not useful when we are running directly
with a window system; but suspend should be disabled in that case. */
- get_tty_size (fileno (TTY_INPUT (CURTTY ())), &width, &height);
+ get_tty_size (fileno (CURTTY ()->input), &width, &height);
if (width != old_width || height != old_height)
change_frame_size (SELECTED_FRAME (), height, width, 0, 0, 0);
diff --git a/src/sysdep.c b/src/sysdep.c
index febf59253e1..d4693f99a94 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -274,7 +274,7 @@ discard_tty_input ()
#ifdef VMS
end_kbd_input ();
- SYS$QIOW (0, fileno (TTY_INPUT (CURTTY())), IO$_READVBLK|IO$M_PURGE, input_iosb, 0, 0,
+ SYS$QIOW (0, fileno (CURTTY()->input), IO$_READVBLK|IO$M_PURGE, input_iosb, 0, 0,
&buf.main, 0, 0, terminator_mask, 0, 0);
queue_kbd_input ();
#else /* not VMS */
@@ -284,7 +284,8 @@ discard_tty_input ()
for (tty = tty_list; tty; tty = tty->next)
{
int zero = 0;
- ioctl (fileno (TTY_INPUT (tty)), TIOCFLUSH, &zero);
+ if (tty->input)
+ ioctl (fileno (tty->input), TIOCFLUSH, &zero);
}
}
#else /* not Apollo */
@@ -296,8 +297,11 @@ discard_tty_input ()
struct tty_display_info *tty;
for (tty = tty_list; tty; tty = tty->next)
{
- EMACS_GET_TTY (fileno (TTY_INPUT (tty)), &buf);
- EMACS_SET_TTY (fileno (TTY_INPUT (tty)), &buf, 0);
+ if (tty->input) /* Is the device suspended? */
+ {
+ EMACS_GET_TTY (fileno (tty->input), &buf);
+ EMACS_SET_TTY (fileno (tty->input), &buf, 0);
+ }
}
}
#endif /* not MSDOS */
@@ -322,7 +326,7 @@ stuff_char (char c)
/* Should perhaps error if in batch mode */
#ifdef TIOCSTI
- ioctl (fileno (TTY_INPUT (CURTTY())), TIOCSTI, &c);
+ ioctl (fileno (CURTTY()->input), TIOCSTI, &c);
#else /* no TIOCSTI */
error ("Cannot stuff terminal input characters in this version of Unix");
#endif /* no TIOCSTI */
@@ -1005,7 +1009,7 @@ request_sigio ()
return;
/* XXX CURTTY() is bogus here. */
- ioctl (fileno (TTY_INPUT (CURTTY ())), FIOASYNC, &on);
+ ioctl (fileno (CURTTY ()->input), FIOASYNC, &on);
interrupts_deferred = 0;
}
@@ -1018,7 +1022,7 @@ unrequest_sigio ()
return;
/* XXX CURTTY() is bogus here. */
- ioctl (fileno (TTY_INPUT (CURTTY ())), FIOASYNC, &off);
+ ioctl (fileno (CURTTY ()->input), FIOASYNC, &off);
interrupts_deferred = 1;
}
@@ -1366,6 +1370,9 @@ nil means don't delete them until `list-processes' is run. */);
if (noninteractive)
return;
+ if (!tty_out->output)
+ return; /* The tty is suspended. */
+
#ifdef VMS
if (!input_ef)
input_ef = get_kbd_event_flag ();
@@ -1404,13 +1411,13 @@ nil means don't delete them until `list-processes' is run. */);
unconditionally will not cause any problems. */
if (! read_socket_hook && EQ (Vinitial_window_system, Qnil))
#endif
- narrow_foreground_group (fileno (TTY_INPUT (tty_out)));
+ narrow_foreground_group (fileno (tty_out->input));
#endif
if (! tty_out->old_tty)
tty_out->old_tty = (struct emacs_tty *) xmalloc (sizeof (struct emacs_tty));
- EMACS_GET_TTY (fileno (TTY_INPUT (tty_out)), tty_out->old_tty);
+ EMACS_GET_TTY (fileno (tty_out->input), tty_out->old_tty);
tty = *tty_out->old_tty;
@@ -1626,23 +1633,23 @@ nil means don't delete them until `list-processes' is run. */);
dos_ttraw ();
#endif
- EMACS_SET_TTY (fileno (TTY_INPUT (tty_out)), &tty, 0);
+ EMACS_SET_TTY (fileno (tty_out->input), &tty, 0);
/* This code added to insure that, if flow-control is not to be used,
we have an unlocked terminal at the start. */
#ifdef TCXONC
- if (!tty_out->flow_control) ioctl (fileno (TTY_INPUT (tty_out)), TCXONC, 1);
+ if (!tty_out->flow_control) ioctl (fileno (tty_out->input), TCXONC, 1);
#endif
#ifndef APOLLO
#ifdef TIOCSTART
- if (!tty_out->flow_control) ioctl (fileno (TTY_INPUT (tty_out)), TIOCSTART, 0);
+ if (!tty_out->flow_control) ioctl (fileno (tty_out->input), TIOCSTART, 0);
#endif
#endif
#if defined (HAVE_TERMIOS) || defined (HPUX9)
#ifdef TCOON
- if (!tty_out->flow_control) tcflow (fileno (TTY_INPUT (tty_out)), TCOON);
+ if (!tty_out->flow_control) tcflow (fileno (tty_out->input), TCOON);
#endif
#endif
@@ -1662,7 +1669,7 @@ nil means don't delete them until `list-processes' is run. */);
#ifdef VMS
/* Appears to do nothing when in PASTHRU mode.
- SYS$QIOW (0, fileno (TTY_INPUT (tty_out)), IO$_SETMODE|IO$M_OUTBAND, 0, 0, 0,
+ SYS$QIOW (0, fileno (tty_out->input), IO$_SETMODE|IO$M_OUTBAND, 0, 0, 0,
interrupt_signal, oob_chars, 0, 0, 0, 0);
*/
queue_kbd_input (0);
@@ -1673,10 +1680,10 @@ nil means don't delete them until `list-processes' is run. */);
#ifdef F_GETOWN /* F_SETFL does not imply existence of F_GETOWN */
if (interrupt_input)
{
- old_fcntl_owner[fileno (TTY_INPUT (tty_out))] =
- fcntl (fileno (TTY_INPUT (tty_out)), F_GETOWN, 0);
- fcntl (fileno (TTY_INPUT (tty_out)), F_SETOWN, getpid ());
- init_sigio (fileno (TTY_INPUT (tty_out)));
+ old_fcntl_owner[fileno (tty_out->input)] =
+ fcntl (fileno (tty_out->input), F_GETOWN, 0);
+ fcntl (fileno (tty_out->input), F_SETOWN, getpid ());
+ init_sigio (fileno (tty_out->input));
}
#endif /* F_GETOWN */
#endif /* F_SETOWN_BUG */
@@ -1684,7 +1691,7 @@ nil means don't delete them until `list-processes' is run. */);
#ifdef BSD4_1
if (interrupt_input)
- init_sigio (fileno (TTY_INPUT (tty_out)));
+ init_sigio (fileno (tty_out->input));
#endif
#ifdef VMS /* VMS sometimes has this symbol but lacks setvbuf. */
@@ -1694,9 +1701,9 @@ nil means don't delete them until `list-processes' is run. */);
/* This symbol is defined on recent USG systems.
Someone says without this call USG won't really buffer the file
even with a call to setbuf. */
- setvbuf (TTY_OUTPUT (tty_out), (char *) _sobuf, _IOFBF, sizeof _sobuf);
+ setvbuf (tty_out->output, (char *) _sobuf, _IOFBF, sizeof _sobuf);
#else
- setbuf (TTY_OUTPUT (tty_out), (char *) _sobuf);
+ setbuf (tty_out->output, (char *) _sobuf);
#endif
tty_set_terminal_modes (tty_out->display);
@@ -1867,10 +1874,13 @@ reset_sys_modes (tty_out)
if (!tty_out->term_initted)
return;
+ if (!tty_out->output)
+ return; /* The tty is suspended. */
+
/* Go to and clear the last line of the terminal. */
cmgoto (tty_out, FrameRows (tty_out) - 1, 0);
-
+
/* Code adapted from tty_clear_end_of_line. */
if (tty_out->TS_clr_line)
{
@@ -1880,13 +1890,13 @@ reset_sys_modes (tty_out)
{ /* have to do it the hard way */
int i;
turn_off_insert (tty_out);
-
+
for (i = curX (tty_out); i < FrameCols (tty_out) - 1; i++)
- {
- fputc (' ', TTY_OUTPUT (tty_out));
- }
+ {
+ fputc (' ', tty_out->output);
+ }
}
-
+
cmgoto (tty_out, FrameRows (tty_out) - 1, 0);
fflush (tty_out->output);
@@ -1902,11 +1912,11 @@ reset_sys_modes (tty_out)
#endif
tty_reset_terminal_modes (tty_out->display);
- fflush (TTY_OUTPUT (tty_out));
+ fflush (tty_out->output);
#ifdef BSD_SYSTEM
#ifndef BSD4_1
/* Avoid possible loss of output when changing terminal modes. */
- fsync (fileno (TTY_OUTPUT (tty_out)));
+ fsync (fileno (tty_out->output));
#endif
#endif
@@ -1915,24 +1925,24 @@ reset_sys_modes (tty_out)
#ifdef F_SETOWN /* F_SETFL does not imply existence of F_SETOWN */
if (interrupt_input)
{
- reset_sigio (fileno (TTY_INPUT (tty_out)));
- fcntl (fileno (TTY_INPUT (tty_out)), F_SETOWN,
- old_fcntl_owner[fileno (TTY_INPUT (tty_out))]);
+ reset_sigio (fileno (tty_out->input));
+ fcntl (fileno (tty_out->input), F_SETOWN,
+ old_fcntl_owner[fileno (tty_out->input)]);
}
#endif /* F_SETOWN */
#endif /* F_SETOWN_BUG */
#ifdef O_NDELAY
- fcntl (fileno (TTY_INPUT (tty_out)), F_SETFL,
- fcntl (fileno (TTY_INPUT (tty_out)), F_GETFL, 0) & ~O_NDELAY);
+ fcntl (fileno (tty_out->input), F_SETFL,
+ fcntl (fileno (tty_out->input), F_GETFL, 0) & ~O_NDELAY);
#endif
#endif /* F_SETFL */
#ifdef BSD4_1
if (interrupt_input)
- reset_sigio (fileno (TTY_INPUT (tty_out)));
+ reset_sigio (fileno (tty_out->input));
#endif /* BSD4_1 */
if (tty_out->old_tty)
- while (EMACS_SET_TTY (fileno (TTY_INPUT (tty_out)),
+ while (EMACS_SET_TTY (fileno (tty_out->input),
tty_out->old_tty, 0) < 0 && errno == EINTR)
;
@@ -1952,7 +1962,7 @@ reset_sys_modes (tty_out)
#endif
#ifdef BSD_PGRPS
- widen_foreground_group (fileno (TTY_INPUT (tty_out)));
+ widen_foreground_group (fileno (tty_out->input));
#endif
}
@@ -2017,9 +2027,9 @@ init_vms_input ()
{
int status;
- if (fileno (TTY_INPUT (CURTTY())) == 0)
+ if (fileno (CURTTY ()->input)) == 0)
{
- status = SYS$ASSIGN (&input_dsc, &fileno (TTY_INPUT (CURTTY())), 0, 0);
+ status = SYS$ASSIGN (&input_dsc, &fileno (CURTTY ()->input)), 0, 0);
if (! (status & 1))
LIB$STOP (status);
}
@@ -2030,7 +2040,7 @@ init_vms_input ()
void
stop_vms_input ()
{
- return SYS$DASSGN (fileno (TTY_INPUT (CURTTY())));
+ return SYS$DASSGN (fileno (CURTTY ()->input)));
}
short input_buffer;
@@ -2046,7 +2056,7 @@ queue_kbd_input ()
waiting_for_ast = 0;
stop_input = 0;
- status = SYS$QIO (0, fileno (TTY_INPUT (CURTTY())), IO$_READVBLK,
+ status = SYS$QIO (0, fileno (CURTTY()->input), IO$_READVBLK,
&input_iosb, kbd_input_ast, 1,
&input_buffer, 1, 0, terminator_mask, 0, 0);
}
@@ -2163,7 +2173,7 @@ end_kbd_input ()
#endif
if (LIB$AST_IN_PROG ()) /* Don't wait if suspending from kbd_buffer_store_event! */
{
- SYS$CANCEL (fileno (TTY_INPUT (CURTTY())));
+ SYS$CANCEL (fileno (CURTTY()->input));
return;
}
@@ -2172,7 +2182,7 @@ end_kbd_input ()
SYS$CLREF (input_ef);
waiting_for_ast = 1;
stop_input = 1;
- SYS$CANCEL (fileno (TTY_INPUT (CURTTY())));
+ SYS$CANCEL (fileno (CURTTY()->input));
SYS$SETAST (1);
SYS$WAITFR (input_ef);
waiting_for_ast = 0;
diff --git a/src/term.c b/src/term.c
index e3b176c51ea..785f2a3bb33 100644
--- a/src/term.c
+++ b/src/term.c
@@ -106,9 +106,15 @@ void delete_tty_output P_ ((struct frame *));
Lisp_Object Vring_bell_function;
-/* Functions to call after a tty was deleted. */
+/* Functions to call after deleting a tty. */
Lisp_Object Vdelete_tty_after_functions;
+/* Functions to call after suspending a tty. */
+Lisp_Object Vsuspend_tty_functions;
+
+/* Functions to call after resuming a tty. */
+Lisp_Object Vresume_tty_functions;
+
/* Chain of all displays currently in use. */
struct display *display_list;
@@ -231,10 +237,13 @@ tty_set_terminal_modes (struct display *display)
{
struct tty_display_info *tty = display->display_info.tty;
- OUTPUT_IF (tty, tty->TS_termcap_modes);
- OUTPUT_IF (tty, tty->TS_cursor_visible);
- OUTPUT_IF (tty, tty->TS_keypad_mode);
- losecursor (tty);
+ if (tty->output)
+ {
+ OUTPUT_IF (tty, tty->TS_termcap_modes);
+ OUTPUT_IF (tty, tty->TS_cursor_visible);
+ OUTPUT_IF (tty, tty->TS_keypad_mode);
+ losecursor (tty);
+ }
}
/* Reset termcap modes before exiting Emacs. */
@@ -243,16 +252,19 @@ void
tty_reset_terminal_modes (struct display *display)
{
struct tty_display_info *tty = display->display_info.tty;
-
- turn_off_highlight (tty);
- turn_off_insert (tty);
- OUTPUT_IF (tty, tty->TS_end_keypad_mode);
- OUTPUT_IF (tty, tty->TS_cursor_normal);
- OUTPUT_IF (tty, tty->TS_end_termcap_modes);
- OUTPUT_IF (tty, tty->TS_orig_pair);
- /* Output raw CR so kernel can track the cursor hpos. */
- current_tty = tty;
- cmputc ('\r');
+
+ if (tty->output)
+ {
+ turn_off_highlight (tty);
+ turn_off_insert (tty);
+ OUTPUT_IF (tty, tty->TS_end_keypad_mode);
+ OUTPUT_IF (tty, tty->TS_cursor_normal);
+ OUTPUT_IF (tty, tty->TS_end_termcap_modes);
+ OUTPUT_IF (tty, tty->TS_orig_pair);
+ /* Output raw CR so kernel can track the cursor hpos. */
+ current_tty = tty;
+ cmputc ('\r');
+ }
}
void
@@ -619,9 +631,9 @@ tty_clear_end_of_line (int first_unused_hpos)
for (i = curX (tty); i < first_unused_hpos; i++)
{
- if (TTY_TERMSCRIPT (tty))
- fputc (' ', TTY_TERMSCRIPT (tty));
- fputc (' ', TTY_OUTPUT (tty));
+ if (tty->termscript)
+ fputc (' ', tty->termscript);
+ fputc (' ', tty->output);
}
cmplus (tty, first_unused_hpos - curX (tty));
}
@@ -807,12 +819,12 @@ tty_write_glyphs (struct glyph *string, int len)
if (produced > 0)
{
fwrite (conversion_buffer, 1, produced,
- TTY_OUTPUT (tty));
- if (ferror (TTY_OUTPUT (tty)))
- clearerr (TTY_OUTPUT (tty));
- if (TTY_TERMSCRIPT (tty))
+ tty->output);
+ if (ferror (tty->output))
+ clearerr (tty->output);
+ if (tty->termscript)
fwrite (conversion_buffer, 1, produced,
- TTY_TERMSCRIPT (tty));
+ tty->termscript);
}
len -= consumed;
n -= consumed;
@@ -833,12 +845,12 @@ tty_write_glyphs (struct glyph *string, int len)
if (terminal_coding.produced > 0)
{
fwrite (conversion_buffer, 1, terminal_coding.produced,
- TTY_OUTPUT (tty));
- if (ferror (TTY_OUTPUT (tty)))
- clearerr (TTY_OUTPUT (tty));
- if (TTY_TERMSCRIPT (tty))
+ tty->output);
+ if (ferror (tty->output))
+ clearerr (tty->output);
+ if (tty->termscript)
fwrite (conversion_buffer, 1, terminal_coding.produced,
- TTY_TERMSCRIPT (tty));
+ tty->termscript);
}
}
@@ -927,12 +939,12 @@ tty_insert_glyphs (struct glyph *start, int len)
if (produced > 0)
{
fwrite (conversion_buffer, 1, produced,
- TTY_OUTPUT (tty));
- if (ferror (TTY_OUTPUT (tty)))
- clearerr (TTY_OUTPUT (tty));
- if (TTY_TERMSCRIPT (tty))
+ tty->output);
+ if (ferror (tty->output))
+ clearerr (tty->output);
+ if (tty->termscript)
fwrite (conversion_buffer, 1, produced,
- TTY_TERMSCRIPT (tty));
+ tty->termscript);
}
OUTPUT1_IF (tty, tty->TS_pad_inserted_char);
@@ -2240,7 +2252,11 @@ term_init (char *name, char *terminal_type, int must_succeed)
display = get_named_tty_display (name);
if (display)
- return display; /* We have already opened a display there. */
+ {
+ if (! display->display_info.tty->input)
+ error ("%s already has a suspended frame on it, can't open it twice", name);
+ return display;
+ }
display = create_display ();
tty = (struct tty_display_info *) xmalloc (sizeof (struct tty_display_info));
@@ -2550,7 +2566,7 @@ to do `unset TERMCAP' (C-shell: `unsetenv TERMCAP') as well.",
/* Get frame size from system, or else from termcap. */
{
int height, width;
- get_tty_size (fileno (TTY_INPUT (tty)), &width, &height);
+ get_tty_size (fileno (tty->input), &width, &height);
FrameCols (tty) = width;
FrameRows (tty) = height;
}
@@ -2735,7 +2751,7 @@ to do `unset TERMCAP' (C-shell: `unsetenv TERMCAP') as well.",
&& tty->TS_end_standout_mode
&& !strcmp (tty->TS_standout_mode, tty->TS_end_standout_mode));
- UseTabs (tty) = tabs_safe_p (fileno (TTY_INPUT (tty))) && TabWidth (tty) == 8;
+ UseTabs (tty) = tabs_safe_p (fileno (tty->input)) && TabWidth (tty) == 8;
display->scroll_region_ok
= (tty->Wcm->cm_abs
@@ -2754,7 +2770,7 @@ to do `unset TERMCAP' (C-shell: `unsetenv TERMCAP') as well.",
display->fast_clear_end_of_line = tty->TS_clr_line != 0;
- init_baud_rate (fileno (TTY_INPUT (tty)));
+ init_baud_rate (fileno (tty->input));
#ifdef AIXHFT
/* The HFT system on AIX doesn't optimize for scrolling, so it's
@@ -3067,6 +3083,134 @@ delete_display (struct display *dev)
xfree (dev);
}
+
+
+DEFUN ("suspend-tty", Fsuspend_tty, Ssuspend_tty, 0, 1, 0,
+ doc: /* Suspend the terminal device TTY.
+The terminal is restored to its default state, and Emacs closes all
+access to the terminal device. Frames that use the device are not
+deleted, but input is not read from them and if they change, their
+display is not updated.
+
+TTY may a string (a device name), a frame, or nil for the display
+device of the currently selected frame.
+
+This function runs `suspend-tty-functions' after suspending the
+device. The functions are run with one arg, the name of the terminal
+device.
+
+`suspend-tty' does nothing if it is called on an already suspended
+device.
+
+A suspended terminal device may be resumed by calling `resume-tty' on
+it. */)
+ (tty)
+ Lisp_Object tty;
+{
+ struct display *d = get_tty_display (tty);
+ FILE *f;
+
+ if (!d)
+ error ("Unknown tty device");
+
+ f = d->display_info.tty->input;
+
+ if (f)
+ {
+ reset_sys_modes (d->display_info.tty);
+
+ delete_keyboard_wait_descriptor (fileno (f));
+
+ fclose (f);
+ if (f != d->display_info.tty->output)
+ fclose (d->display_info.tty->output);
+
+ d->display_info.tty->input = 0;
+ d->display_info.tty->output = 0;
+
+ if (FRAMEP (d->display_info.tty->top_frame))
+ FRAME_SET_VISIBLE (XFRAME (d->display_info.tty->top_frame), 0);
+
+ /* Run `suspend-tty-functions'. */
+ if (!NILP (Vrun_hooks))
+ {
+ Lisp_Object args[2];
+ args[0] = intern ("suspend-tty-functions");
+ if (d->display_info.tty->name)
+ {
+ args[1] = build_string (d->display_info.tty->name);
+ }
+ else
+ args[1] = Qnil;
+ Frun_hook_with_args (2, args);
+ }
+ }
+
+ return Qnil;
+}
+
+
+DEFUN ("resume-tty", Fresume_tty, Sresume_tty, 0, 1, 0,
+ doc: /* Resume the previously suspended terminal device TTY.
+The terminal is opened and reinitialized. Frames that used the
+suspended device are revived.
+
+This function runs `resume-tty-functions' after resuming the device.
+The functions are run with one arg, the name of the terminal device.
+
+`resume-tty' does nothing if it is called on a device that is not
+suspended.
+
+TTY may a string (a device name), a frame, or nil for the display
+device of the currently selected frame. */)
+ (tty)
+ Lisp_Object tty;
+{
+ struct display *d = get_tty_display (tty);
+ int fd;
+
+ if (!d)
+ error ("Unknown tty device");
+
+ if (!d->display_info.tty->input)
+ {
+ fd = emacs_open (d->display_info.tty->name, O_RDWR | O_NOCTTY, 0);
+
+#ifdef TIOCNOTTY
+ /* Drop our controlling tty if it is the same device. */
+ if (ioctl (fd, TIOCNOTTY, 0) != -1)
+ {
+ no_controlling_tty = 1;
+ }
+#endif
+
+ d->display_info.tty->output = fdopen (fd, "w+");
+ d->display_info.tty->input = d->display_info.tty->output;
+
+ add_keyboard_wait_descriptor (fd);
+
+ if (FRAMEP (d->display_info.tty->top_frame))
+ FRAME_SET_VISIBLE (XFRAME (d->display_info.tty->top_frame), 1);
+
+ init_sys_modes (d->display_info.tty);
+
+ /* Run `suspend-tty-functions'. */
+ if (!NILP (Vrun_hooks))
+ {
+ Lisp_Object args[2];
+ args[0] = intern ("resume-tty-functions");
+ if (d->display_info.tty->name)
+ {
+ args[1] = build_string (d->display_info.tty->name);
+ }
+ else
+ args[1] = Qnil;
+ Frun_hook_with_args (2, args);
+ }
+ }
+
+ return Qnil;
+}
void
@@ -3092,6 +3236,20 @@ The functions are run with one argument, the name of the tty to be deleted.
See `delete-tty'. */);
Vdelete_tty_after_functions = Qnil;
+
+ DEFVAR_LISP ("suspend-tty-functions", &Vsuspend_tty_functions,
+ doc: /* Functions to be run after suspending a tty.
+The functions are run with one argument, the name of the tty to be suspended.
+See `suspend-tty'. */);
+ Vsuspend_tty_functions = Qnil;
+
+
+ DEFVAR_LISP ("resume-tty-functions", &Vresume_tty_functions,
+ doc: /* Functions to be run after resuming a tty.
+The functions are run with one argument, the name of the tty that was revived.
+See `resume-tty'. */);
+ Vresume_tty_functions = Qnil;
+
Qframe_tty_name = intern ("frame-tty-name");
staticpro (&Qframe_tty_name);
@@ -3103,6 +3261,8 @@ See `delete-tty'. */);
defsubr (&Sframe_tty_name);
defsubr (&Sframe_tty_type);
defsubr (&Sdelete_tty);
+ defsubr (&Ssuspend_tty);
+ defsubr (&Sresume_tty);
Fprovide (intern ("multi-tty"), Qnil);
diff --git a/src/termchar.h b/src/termchar.h
index fbf91f2458f..3053061c1b7 100644
--- a/src/termchar.h
+++ b/src/termchar.h
@@ -42,8 +42,10 @@ struct tty_display_info
/* Input/output */
- FILE *input; /* The stream to be used for terminal input. */
- FILE *output; /* The stream to be used for terminal output. */
+ FILE *input; /* The stream to be used for terminal input.
+ NULL if the terminal is suspended. */
+ FILE *output; /* The stream to be used for terminal output.
+ NULL if the terminal is suspended. */
FILE *termscript; /* If nonzero, send all terminal output
characters to this stream also. */
@@ -200,9 +202,5 @@ extern struct tty_display_info *tty_list;
#define CURTTY() FRAME_TTY (SELECTED_FRAME())
-#define TTY_INPUT(t) ((t)->input)
-#define TTY_OUTPUT(t) ((t)->output)
-#define TTY_TERMSCRIPT(t) ((t)->termscript)
-
/* arch-tag: bf9f0d49-842b-42fb-9348-ec8759b27193
(do not change this comment) */
diff --git a/src/termhooks.h b/src/termhooks.h
index c79e77379b1..6b2b0d07867 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -513,7 +513,6 @@ struct display
frames on the display when it calls this hook, so infinite
recursion is prevented. */
void (*delete_display_hook) P_ ((struct display *));
-
};