changeset 83065:a871be7b26a5

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
author Karoly Lorentey <lorentey@elte.hu>
date Sat, 28 Feb 2004 01:23:39 +0000
parents e7e9448cb3a0
children 887bb2eb4a89
files README.multi-tty lib-src/emacsclient.c lisp/frame.el lisp/server.el lisp/term/x-win.el src/cm.c src/dispnew.c src/frame.c src/keyboard.c src/sysdep.c src/term.c src/termchar.h src/termhooks.h
diffstat 13 files changed, 733 insertions(+), 309 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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;
 }
--- 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
--- 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)))))
--- 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.
--- 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)++;
     }
--- 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);
 }
 
 
--- 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);
   }
   
--- 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);
 
--- 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;
--- 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);
 
--- 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) */
--- 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 *));
-
 };