# HG changeset patch # User Karoly Lorentey # Date 1077931419 0 # Node ID a871be7b26a53da0eea8403adeb9cde094a4b2d5 # Parent e7e9448cb3a05f20b6ce58283fcee0625b3b3775 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 diff -r e7e9448cb3a0 -r a871be7b26a5 README.multi-tty --- a/README.multi-tty Fri Feb 27 14:10:47 2004 +0000 +++ b/README.multi-tty Sat Feb 28 01:23:39 2004 +0000 @@ -200,22 +200,50 @@ 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: + + (displayp OBJECT): Returns t if OBJECT is a display. + + (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'.) -** 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.) + (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: -** 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. + $ 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 @@ 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 -r e7e9448cb3a0 -r a871be7b26a5 lib-src/emacsclient.c --- a/lib-src/emacsclient.c Fri Feb 27 14:10:47 2004 +0000 +++ b/lib-src/emacsclient.c Sat Feb 28 01:23:39 2004 +0000 @@ -114,7 +114,7 @@ display = getenv ("DISPLAY"); if (display && strlen (display) == 0) display = NULL; - + if (display) window_system = 1; else @@ -169,7 +169,7 @@ window_system = 0; tty = 0; break; - + case 'H': print_help_and_exit (); break; @@ -212,19 +212,21 @@ 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 @@ } else { - if (*p == '&' || (*p == '-' && p == name)) + if (*p == '&' || (*p == '-' && p == str)) *q++ = '&'; *q++ = *p++; } @@ -254,6 +256,41 @@ 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 @@ } } +/* 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 @@ 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 @@ 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 @@ if (!string) return 0; - + for (i = 0; prefix[i]; i++) if (!string[i] || string[i] != prefix[i]) return 0; @@ -391,7 +490,6 @@ char **argv; { int s, i, needlf = 0; - FILE *out, *in; struct sockaddr_un server; char *cwd, *str; char string[BUFSIZ]; @@ -427,9 +525,9 @@ 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 @@ /* 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 @@ { 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 @@ " 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, "/"); - } + if (*p == 0) + { + fprintf (out, "-position "); + quote_argument (argv[i], out); + fprintf (out, " "); + continue; + } + else + relative = 1; + } + else if (*argv[i] != '/') + relative = 1; - quote_file_name (argv[i], out); - fprintf (out, " "); - } + fprintf (out, "-file "); + if (relative) + { + quote_argument (cwd, out); + fprintf (out, "/"); + } + quote_argument (argv[i], out); + fprintf (out, " "); + } } else { @@ -655,14 +766,19 @@ { 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 @@ 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 + strlen ("-print ")); - needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; + printf ("*ERROR*: %s", str); + needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; + } + else if (strprefix ("-suspend ", str)) + { + if (needlf) + printf ("\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 @@ if (needlf) printf ("\n"); fflush (stdout); + fsync (1); return 0; } diff -r e7e9448cb3a0 -r a871be7b26a5 lisp/frame.el --- a/lisp/frame.el Fri Feb 27 14:10:47 2004 +0000 +++ b/lisp/frame.el Sat Feb 28 01:23:39 2004 +0000 @@ -750,6 +750,22 @@ (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 @@ (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 -r e7e9448cb3a0 -r a871be7b26a5 lisp/server.el --- a/lisp/server.el Fri Feb 27 14:10:47 2004 +0000 +++ b/lisp/server.el Sat Feb 28 01:23:39 2004 +0000 @@ -186,7 +186,7 @@ (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 @@ (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 @@ ;; `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 @@ (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 @@ (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 @@ (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 @@ (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))))) + (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)) - ((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)))) + ((equal "-nowait" arg) (setq nowait t)) - ;; 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))) + ((and (equal "-display" arg) (string-match "\\([^ ]*\\) " request)) + (setq display (match-string 1 request) + 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))) + ;; 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)))) + 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)))) - ;; ARG is a line number option. - ((string-match "\\`\\+[0-9]+\\'" arg) - (setq lineno (string-to-int (substring arg 1)))) + ;; 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)))) - ;; 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)))) + ;; Noop; useful for debugging emacsclient. + ((and (equal "-ignore" arg) (string-match "\\([^ ]*\\) " request)) + (setq dontkill t + request (substring request (match-end 0)))) - ;; 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))))) + ;; 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))))) + (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))) - (if (not version-checked) - (error "Protocol error; make sure to use the correct version of emacsclient") + ;; 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 -r e7e9448cb3a0 -r a871be7b26a5 lisp/term/x-win.el --- a/lisp/term/x-win.el Fri Feb 27 14:10:47 2004 +0000 +++ b/lisp/term/x-win.el Sat Feb 28 01:23:39 2004 +0000 @@ -2442,11 +2442,7 @@ (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 -r e7e9448cb3a0 -r a871be7b26a5 src/cm.c --- a/src/cm.c Fri Feb 27 14:10:47 2004 +0000 +++ b/src/cm.c Sat Feb 28 01:23:39 2004 +0000 @@ -64,9 +64,9 @@ 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 @@ { 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 -r e7e9448cb3a0 -r a871be7b26a5 src/dispnew.c --- a/src/dispnew.c Fri Feb 27 14:10:47 2004 +0000 +++ b/src/dispnew.c Sat Feb 28 01:23:39 2004 +0000 @@ -3316,7 +3316,7 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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) 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 @@ (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 @@ 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 @@ else ring_bell (); if (FRAME_TERMCAP_P (XFRAME (selected_frame))) - fflush (TTY_OUTPUT (CURTTY ())); + fflush (CURTTY ()->output); } diff -r e7e9448cb3a0 -r a871be7b26a5 src/frame.c --- a/src/frame.c Fri Feb 27 14:10:47 2004 +0000 +++ b/src/frame.c Sat Feb 28 01:23:39 2004 +0000 @@ -667,7 +667,8 @@ (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 @@ { 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 -r e7e9448cb3a0 -r a871be7b26a5 src/keyboard.c --- a/src/keyboard.c Fri Feb 27 14:10:47 2004 +0000 +++ b/src/keyboard.c Sat Feb 28 01:23:39 2004 +0000 @@ -6704,10 +6704,13 @@ 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 @@ #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 @@ 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 @@ #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 @@ 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 @@ /* 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 -r e7e9448cb3a0 -r a871be7b26a5 src/sysdep.c --- a/src/sysdep.c Fri Feb 27 14:10:47 2004 +0000 +++ b/src/sysdep.c Sat Feb 28 01:23:39 2004 +0000 @@ -274,7 +274,7 @@ #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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ #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 @@ #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 @@ #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 @@ /* 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 @@ 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 @@ { /* 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 @@ #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 @@ #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 @@ #endif #ifdef BSD_PGRPS - widen_foreground_group (fileno (TTY_INPUT (tty_out))); + widen_foreground_group (fileno (tty_out->input)); #endif } @@ -2017,9 +2027,9 @@ { 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 @@ void stop_vms_input () { - return SYS$DASSGN (fileno (TTY_INPUT (CURTTY()))); + return SYS$DASSGN (fileno (CURTTY ()->input))); } short input_buffer; @@ -2046,7 +2056,7 @@ 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 @@ #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 @@ 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 -r e7e9448cb3a0 -r a871be7b26a5 src/term.c --- a/src/term.c Fri Feb 27 14:10:47 2004 +0000 +++ b/src/term.c Sat Feb 28 01:23:39 2004 +0000 @@ -106,9 +106,15 @@ 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ && 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 @@ 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 @@ 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 @@ 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 @@ defsubr (&Sframe_tty_name); defsubr (&Sframe_tty_type); defsubr (&Sdelete_tty); + defsubr (&Ssuspend_tty); + defsubr (&Sresume_tty); Fprovide (intern ("multi-tty"), Qnil); diff -r e7e9448cb3a0 -r a871be7b26a5 src/termchar.h --- a/src/termchar.h Fri Feb 27 14:10:47 2004 +0000 +++ b/src/termchar.h Sat Feb 28 01:23:39 2004 +0000 @@ -42,8 +42,10 @@ /* 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 @@ #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 -r e7e9448cb3a0 -r a871be7b26a5 src/termhooks.h --- a/src/termhooks.h Fri Feb 27 14:10:47 2004 +0000 +++ b/src/termhooks.h Sat Feb 28 01:23:39 2004 +0000 @@ -513,7 +513,6 @@ frames on the display when it calls this hook, so infinite recursion is prevented. */ void (*delete_display_hook) P_ ((struct display *)); - };