diff lib-src/emacsclient.c @ 53229:33c3c7c16e13

lib-src/emacsclient.c: Implemented --here option (open a new Emacs tty). Needs more work. (here): New variable. (decode_options): Use it. (ec_get_tty, ec_set_tty, init_tty, window_change, hang_up_signal): New functions. (window_change_signal, init_signals, reset_tty, init_pty, copy_from_to): Ditto. (pty_conversation): Ditto. (main): Use them. (master, pty_name, old_tty, tty, old_tty_valid, tty_erase_char): New variables. (flow_control, meta_key, _sobuf, in_conversation, quit_conversation): Ditto. lisp/server.el (server-process-filter): Added support for opening a new terminal frame. dispextern.h (get_frame_size): Renamed to get_tty_size, added tty_output parameter. dispnew.c (Fredraw_frame): fflush the current terminal instead of stdout. (direct_output_for_insert, direct_output_forward_char, update_frame_1): Ditto. (Fding, bitch_at_user): Ditto. (update_frame_1): Count pending output for current terminal instead of stdout. (window_change_signal): Resize all terminals. (change_frame_size): Don't resize all terminals to the same size. frame.c (Vterminal_frame): Removed. (syms_of_frame): Removed declaration of Vterminal_frame. (make_terminal_frame): Set the top frame of the terminal to the new frame. (Fmake_terminal_frame): Get a new frame size from get_tty_size, don't copy it. (do_switch_frame): Handle terminal frame visibility. (next_frame, prev_frame): Skip over frames on different terminals. frame.h (Vterminal_frame): Removed. keyboard.c (input_fd): Removed. (read_avail_input): Removed first argument from read_socket_hook. Try to read from each available tty, until one succeeds. (Fsuspend_emacs): Don't suspend if there are multiple terminals. lisp.h (get_frame_size): Removed superflous declaration. xterm.c (Xtread_socket): Removed first parameter. macterm.h (XTread_socket): Ditto. w32inevt.c (w32_console_read_socket): Ditto. w32term.c (w32_read_socket): Ditto. sysdep.c (input_fd): Removed. (change_input_fd): Removed. (discard_tty_input): Discard pending input on _all_ input descriptors. (stuff_char, tabs_safe_p): Use current terminal instead of input_fd. (init_baud_rate, request_sigio, unrequest_sigio): Ditto. (init_sys_modes, reset_sys_modes): Ditto. (narrow_foreground_group, widen_foreground_group): Use stdin. (init_sys_modes, reset_sys_modes): otty parameter renamed to tty_out. (get_frame_size): Renamed to get_tty_size, added tty_out parameter. term.c (read_socket_hook): Removed first parameter. (clear_end_of_line): Use updating_frame, if possible. (write_glyphs, insert_glyphs, ins_del_lines): Ditto. (term_init): Renamed get_frame_size to get_tty_size. termchar.h (struct tty_output): New entries: top_frame, previous_terminal_frame. termhooks.h (read_socket_hook): Removed first parameter. window.c (init_window_once): Removed reference to Vterminal_frame. xdisp.c (previous_terminal_frame): Moved to struct tty_output. (redisplay_internal): Updated to use previous_terminal_frame in tty_output. Allow for simultaneous refresh of multiple ttys. git-archimport-id: lorentey@elte.hu--2004/emacs--multi-tty--0--patch-5
author Karoly Lorentey <lorentey@elte.hu>
date Fri, 26 Dec 2003 04:24:54 +0000
parents 5c74e66d6c36
children 22aaf1e5fbe6
line wrap: on
line diff
--- a/lib-src/emacsclient.c	Thu Dec 25 07:36:05 2003 +0000
+++ b/lib-src/emacsclient.c	Fri Dec 26 04:24:54 2003 +0000
@@ -41,6 +41,54 @@
 # include <pwd.h>
 #endif /* not VMS */
 
+
+/****************************************/
+
+#include <errno.h>
+#include <signal.h>
+
+#ifndef INCLUDED_FCNTL
+#define INCLUDED_FCNTL
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_TERMIOS
+#ifndef NO_TERMIO
+#include <termio.h>
+#endif
+#include <termios.h>
+#endif /* not HAVE_TERMIOS */
+
+#ifdef __GNU_LIBRARY__
+#include <sys/ioctl.h>
+#include <termios.h>
+#endif
+
+#if (defined (POSIX) || defined (NEED_UNISTD_H)) && defined (HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+
+
+/* Try to establish the correct character to disable terminal functions
+   in a system-independent manner.  Note that USG (at least) define
+   _POSIX_VDISABLE as 0!  */
+
+#ifdef _POSIX_VDISABLE
+#define CDISABLE _POSIX_VDISABLE
+#else /* not _POSIX_VDISABLE */
+#ifdef CDEL
+#undef CDISABLE
+#define CDISABLE CDEL
+#else /* not CDEL */
+#define CDISABLE 255
+#endif /* not CDEL */
+#endif /* not _POSIX_VDISABLE */
+
+
+
+/****************************************/
+
 char *getenv (), *getwd ();
 char *getcwd ();
 
@@ -63,6 +111,9 @@
 /* The display on which Emacs should work.  --display.  */
 char *display = NULL;
 
+/* Nonzero means open a new Emacs frame on the current terminal. */
+int here = 0;
+
 /* If non-NULL, the name of an editor to fallback to if the server
    is not running.  --alternate-editor.   */
 const char * alternate_editor = NULL;
@@ -78,6 +129,7 @@
   { "eval",	no_argument,	   NULL, 'e' },
   { "help",	no_argument,	   NULL, 'H' },
   { "version",	no_argument,	   NULL, 'V' },
+  { "here",	no_argument,       NULL, 'h' },
   { "alternate-editor", required_argument, NULL, 'a' },
   { "socket-name",	required_argument, NULL, 's' },
   { "display",	required_argument, NULL, 'd' },
@@ -95,7 +147,7 @@
   while (1)
     {
       int opt = getopt_long (argc, argv,
-			     "VHnea:s:d:", longopts, 0);
+			     "VHnea:s:d:h", longopts, 0);
 
       if (opt == EOF)
 	break;
@@ -134,6 +186,10 @@
 	  exit (0);
 	  break;
 
+        case 'h':
+          here = 1;
+          break;
+          
 	case 'H':
 	  print_help_and_exit ();
 	  break;
@@ -144,6 +200,12 @@
 	  break;
 	}
     }
+
+  if (here) {
+    nowait = 0;
+    display = 0;
+  }
+  
 }
 
 void
@@ -157,6 +219,7 @@
 The following OPTIONS are accepted:\n\
 -V, --version           Just print a version info and return\n\
 -H, --help              Print this usage information message\n\
+-h, --here              Open a new Emacs frame on the current terminal\n\
 -n, --no-wait           Don't wait for the server to return\n\
 -e, --eval              Evaluate the FILE arguments as ELisp expressions\n\
 -d, --display=DISPLAY   Visit the file in the given display\n\
@@ -247,6 +310,455 @@
     }
 }
 
+
+#ifdef HAVE_TERMIOS
+
+/* Adapted from emacs_get_tty() in sysdep.c. */
+int
+ec_get_tty (int fd, struct termios *settings)
+{
+  bzero (settings, sizeof (struct termios));
+  if (tcgetattr (fd, settings) < 0)
+    return -1;
+  return 0;
+}
+
+/* Adapted from emacs_set_tty() in sysdep.c. */
+int
+ec_set_tty (int fd, struct termios *settings, int flushp)
+{
+  /* Set the primary parameters - baud rate, character size, etcetera.  */
+
+  int i;
+  /* We have those nifty POSIX tcmumbleattr functions.
+     William J. Smith <wjs@wiis.wang.com> writes:
+     "POSIX 1003.1 defines tcsetattr to return success if it was
+     able to perform any of the requested actions, even if some
+     of the requested actions could not be performed.
+     We must read settings back to ensure tty setup properly.
+     AIX requires this to keep tty from hanging occasionally."  */
+  /* This make sure that we don't loop indefinitely in here.  */
+  for (i = 0 ; i < 10 ; i++)
+    if (tcsetattr (fd, flushp ? TCSAFLUSH : TCSADRAIN, settings) < 0)
+      {
+	if (errno == EINTR)
+	  continue;
+	else
+	  return -1;
+      }
+    else
+      {
+	struct termios new;
+        
+	bzero (&new, sizeof (new));
+	/* Get the current settings, and see if they're what we asked for.  */
+	tcgetattr (fd, &new);
+	/* We cannot use memcmp on the whole structure here because under
+	 * aix386 the termios structure has some reserved field that may
+	 * not be filled in.
+	 */
+	if (   new.c_iflag == settings->c_iflag
+	    && new.c_oflag == settings->c_oflag
+	    && new.c_cflag == settings->c_cflag
+	    && new.c_lflag == settings->c_lflag
+	    && memcmp (new.c_cc, settings->c_cc, NCCS) == 0)
+	  break;
+	else
+	  continue;
+      }
+  return 0;
+}
+
+int master;
+char *pty_name;
+
+struct termios old_tty;
+struct termios tty;
+int old_tty_valid;
+
+int tty_erase_char;
+int flow_control = 0;
+int meta_key = 0;
+char _sobuf[BUFSIZ];
+
+/* Adapted from init_sys_modes() in sysdep.c. */
+int
+init_tty ()
+{
+  if (! isatty (0))
+    {
+      fprintf (stderr, "%s: Input is not a terminal", "init_tty");
+      return 0;
+    }
+  
+  ec_get_tty (0, &old_tty);
+  old_tty_valid = 1;
+  tty = old_tty;
+  
+  tty_erase_char = old_tty.c_cc[VERASE];
+  
+  tty.c_iflag |= (IGNBRK);	/* Ignore break condition */
+  tty.c_iflag &= ~ICRNL;	/* Disable map of CR to NL on input */
+#ifdef INLCR
+  tty.c_iflag &= ~INLCR;	/* Disable map of NL to CR on input */
+#endif
+#ifdef ISTRIP
+  tty.c_iflag &= ~ISTRIP;	/* don't strip 8th bit on input */
+#endif
+  tty.c_lflag &= ~ECHO;         /* Disable echo */
+  tty.c_lflag &= ~ICANON;	/* Disable erase/kill processing */
+#ifdef IEXTEN
+  tty.c_lflag &= ~IEXTEN;	/* Disable other editing characters.  */
+#endif
+  tty.c_lflag |= ISIG;          /* Enable signals */
+  if (flow_control)
+    {
+      tty.c_iflag |= IXON;	/* Enable start/stop output control */
+#ifdef IXANY
+      tty.c_iflag &= ~IXANY;
+#endif /* IXANY */
+    }
+  else
+    tty.c_iflag &= ~IXON;	/* Disable start/stop output control */
+  tty.c_oflag &= ~ONLCR;	/* Disable map of NL to CR-NL
+                                   on output */
+  tty.c_oflag &= ~TAB3;         /* Disable tab expansion */
+#ifdef CS8
+  if (meta_key)
+    {
+      tty.c_cflag |= CS8;	/* allow 8th bit on input */
+      tty.c_cflag &= ~PARENB;   /* Don't check parity */
+    }
+#endif
+  tty.c_cc[VINTR] = CDISABLE;
+  tty.c_cc[VQUIT] = CDISABLE;
+  tty.c_cc[VMIN] = 1;      /* Input should wait for at least 1 char */
+  tty.c_cc[VTIME] = 0;          /* no matter how long that takes.  */
+#ifdef VSWTCH
+  tty.c_cc[VSWTCH] = CDISABLE;	/* Turn off shell layering use of C-z */
+#endif
+
+#ifdef VSUSP
+  tty.c_cc[VSUSP] = CDISABLE;	/* Turn off mips handling of C-z.  */
+#endif /* VSUSP */
+#ifdef V_DSUSP
+  tty.c_cc[V_DSUSP] = CDISABLE; /* Turn off mips handling of C-y.  */
+#endif /* V_DSUSP */
+#ifdef VDSUSP /* Some systems have VDSUSP, some have V_DSUSP.  */
+  tty.c_cc[VDSUSP] = CDISABLE;
+#endif /* VDSUSP */
+#ifdef VLNEXT
+  tty.c_cc[VLNEXT] = CDISABLE;
+#endif /* VLNEXT */
+#ifdef VREPRINT
+  tty.c_cc[VREPRINT] = CDISABLE;
+#endif /* VREPRINT */
+#ifdef VWERASE
+  tty.c_cc[VWERASE] = CDISABLE;
+#endif /* VWERASE */
+#ifdef VDISCARD
+  tty.c_cc[VDISCARD] = CDISABLE;
+#endif /* VDISCARD */
+
+  if (flow_control)
+    {
+#ifdef VSTART
+      tty.c_cc[VSTART] = '\021';
+#endif /* VSTART */
+#ifdef VSTOP
+      tty.c_cc[VSTOP] = '\023';
+#endif /* VSTOP */
+    }
+  else
+    {
+#ifdef VSTART
+      tty.c_cc[VSTART] = CDISABLE;
+#endif /* VSTART */
+#ifdef VSTOP
+      tty.c_cc[VSTOP] = CDISABLE;
+#endif /* VSTOP */
+    }
+  
+#ifdef SET_LINE_DISCIPLINE
+  /* Need to explicitly request TERMIODISC line discipline or
+     Ultrix's termios does not work correctly.  */
+  tty.c_line = SET_LINE_DISCIPLINE;
+#endif
+  
+#ifdef AIX
+#ifndef IBMR2AIX
+  /* AIX enhanced edit loses NULs, so disable it.  */
+  tty.c_line = 0;
+  tty.c_iflag &= ~ASCEDIT;
+#else
+  tty.c_cc[VSTRT] = 255;
+  tty.c_cc[VSTOP] = 255;
+  tty.c_cc[VSUSP] = 255;
+  tty.c_cc[VDSUSP] = 255;
+#endif /* IBMR2AIX */
+  if (flow_control)
+    {
+#ifdef VSTART
+      tty.c_cc[VSTART] = '\021';
+#endif /* VSTART */
+#ifdef VSTOP
+      tty.c_cc[VSTOP] = '\023';
+#endif /* VSTOP */
+    }
+  /* Also, PTY overloads NUL and BREAK.
+     don't ignore break, but don't signal either, so it looks like NUL.
+     This really serves a purpose only if running in an XTERM window
+     or via TELNET or the like, but does no harm elsewhere.  */
+  tty.c_iflag &= ~IGNBRK;
+  tty.c_iflag &= ~BRKINT;
+#endif /* AIX */
+  
+  ec_set_tty (0, &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 (!flow_control) ioctl (0, TCXONC, 1);
+#endif
+#ifndef APOLLO
+#ifdef TIOCSTART
+  if (!flow_control) ioctl (0, TIOCSTART, 0);
+#endif
+#endif
+
+#if defined (HAVE_TERMIOS) || defined (HPUX9)
+#ifdef TCOON
+  if (!flow_control) tcflow (0, TCOON);
+#endif
+#endif
+  
+#ifdef _IOFBF
+  /* 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 (stdout, (char *) _sobuf, _IOFBF, sizeof _sobuf);
+#else
+  setbuf (stdout, (char *) _sobuf);
+#endif
+
+  return 1;
+}
+
+void
+window_change ()
+{
+  int width, height;
+
+#ifdef TIOCGWINSZ
+  {
+    /* BSD-style.  */
+    struct winsize size;
+    
+    if (ioctl (0, TIOCGWINSZ, &size) == -1)
+      width = height = 0;
+    else
+      {
+        width = size.ws_col;
+        height = size.ws_row;
+      }
+  }
+#else
+#ifdef TIOCGSIZE
+  {
+    /* SunOS - style.  */
+    struct ttysize size;
+    
+    if (ioctl (0, TIOCGSIZE, &size) == -1)
+      width = height = 0;
+    else
+      {
+        width = size.ts_cols;
+        height = size.ts_lines;
+      }
+  }
+#endif /* not SunOS-style */
+#endif /* not BSD-style */
+
+#ifdef TIOCSWINSZ
+  {
+    /* BSD-style.  */
+    struct winsize size;
+    size.ws_row = height;
+    size.ws_col = width;
+    
+    ioctl (master, TIOCSWINSZ, &size);
+  }
+#else
+#ifdef TIOCSSIZE
+  {
+    /* SunOS - style.  */
+    struct ttysize size;
+    size.ts_lines = height;
+    size.ts_cols = width;
+    
+    ioctl (master, TIOCGSIZE, &size);
+  }
+#endif /* not SunOS-style */
+#endif /* not BSD-style */
+}
+
+int in_conversation = 0;
+int quit_conversation = 0;
+
+SIGTYPE
+hang_up_signal (int signalnum)
+{
+  int old_errno = errno;
+  
+  if (! in_conversation)
+    return;
+
+  quit_conversation = 1;
+  
+  errno = old_errno;
+}
+
+SIGTYPE
+window_change_signal (int signalnum)
+{
+  int old_errno = errno;
+
+  if (! in_conversation)
+    goto end;
+
+  window_change();
+
+ end:
+  signal (SIGWINCH, window_change_signal);
+  errno = old_errno;
+}
+
+int
+init_signals ()
+{
+  /* Set up signal handlers. */
+  signal (SIGWINCH, window_change_signal);
+  signal (SIGHUP, hang_up_signal);
+
+  return 1;
+}
+
+
+
+/* Adapted from reset_sys_modes in sysdep.c. */
+int
+reset_tty ()
+{
+  fflush (stdout);
+#ifdef BSD_SYSTEM
+#ifndef BSD4_1
+  /* Avoid possible loss of output when changing terminal modes.  */
+  fsync (fileno (stdout));
+#endif
+#endif
+
+#ifdef F_SETFL
+#ifdef O_NDELAY
+  fcntl (0, F_SETFL, fcntl (0, F_GETFL, 0) & ~O_NDELAY);
+#endif
+#endif /* F_SETFL */
+
+  if (old_tty_valid)
+    while (ec_set_tty (0, &old_tty, 0) < 0 && errno == EINTR)
+      ;
+
+  return 1;
+}
+
+
+int
+init_pty ()
+{
+  master = getpt ();
+  if (master < 0)
+    return 0;
+
+  if (grantpt (master) < 0 || unlockpt (master) < 0)
+    goto close_master;
+  pty_name = strdup (ptsname (master));
+  if (! pty_name)
+    goto close_master;
+
+  /* Propagate window size. */
+  window_change ();
+  
+  return 1;
+  
+ close_master:
+  close (master);
+  return 0;
+}
+
+int
+copy_from_to (int in, int out)
+{
+  static char buf[BUFSIZ];
+  int nread = read (in, &buf, BUFSIZ);
+  if (nread == 0)
+    return 1;                   /* EOF */
+  else if (nread < 0 && errno != EAGAIN)
+    return 0;                   /* Error */
+  else if (nread > 0)
+    {
+      int r = 0;
+      int written = 0;
+
+      do {
+        r = write (out, &buf, nread);
+      } while ((r < 0 && errno == EAGAIN)
+               || (r > 0 && (written += r) && written != nread));
+      
+      if (r < 0)
+        return 0;               /* Error */
+    }
+  return 1;
+}
+
+int
+pty_conversation ()
+{
+  fd_set set;
+
+  in_conversation = 1;
+  
+  while (! quit_conversation) {
+    int res;
+    
+    FD_ZERO (&set);
+    FD_SET (master, &set);
+    FD_SET (1, &set);
+    res = select (FD_SETSIZE, &set, NULL, NULL, NULL);
+    if (res < 0)
+      {
+        if (errno != EINTR)
+          return 0;
+      }
+    else if (res > 0)
+      {
+        if (FD_ISSET (master, &set))
+          {
+            /* Copy Emacs output to stdout. */
+            if (! copy_from_to (master, 0))
+              return 1;
+          }
+        if (FD_ISSET (1, &set))
+          {
+            /* Forward user input to Emacs. */
+            if (! copy_from_to (1, master))
+              return 1;
+          }
+      }
+  }
+  return 1;
+}
+
+#endif /* HAVE_TERMIOS */
 
 
 #if !defined (HAVE_SOCKETS) || defined (NO_SOCKETS_IN_FILE_SYSTEM)
@@ -312,7 +824,7 @@
   /* Process options.  */
   decode_options (argc, argv);
 
-  if ((argc - optind < 1) && !eval)
+  if ((argc - optind < 1) && !eval && !here)
     {
       fprintf (stderr, "%s: file name or argument required\n", progname);
       fprintf (stderr, "Try `%s --help' for more information\n", progname);
@@ -484,6 +996,38 @@
       fprintf (out, " ");
     }
 
+  if (here)
+    {
+      if (! init_signals ())
+        {
+          fprintf (stderr, "%s: ", argv[0]);
+          perror ("fdopen");
+          fail (argc, argv);
+        }
+        
+      if (! init_tty ())
+        {
+          reset_tty ();
+          fprintf (stderr, "%s: ", argv[0]);
+          perror ("fdopen");
+          fail (argc, argv);
+        }
+      
+      if (! init_pty ())
+        {
+          reset_tty ();
+          fprintf (stderr, "%s: ", argv[0]);
+          perror ("fdopen");
+          fail (argc, argv);
+        }
+      
+      fprintf (out, "-pty ");
+      quote_file_name (pty_name, out);
+      fprintf (out, " ");
+      quote_file_name (getenv("TERM"), out);
+      fprintf (out, " ");
+    }
+  
   if ((argc - optind > 0))
     {
       for (i = optind; i < argc; i++)
@@ -512,11 +1056,14 @@
     }
   else
     {
-      while ((str = fgets (string, BUFSIZ, stdin)))
-	{
-	  quote_file_name (str, out);
-	}
-      fprintf (out, " ");
+      if (!here)
+        {
+          while ((str = fgets (string, BUFSIZ, stdin)))
+            {
+              quote_file_name (str, out);
+            }
+          fprintf (out, " ");
+        }
     }
   
   fprintf (out, "\n");
@@ -524,8 +1071,25 @@
 
   /* Maybe wait for an answer.   */
   if (nowait)
-    return 0;
+    {
+      reset_tty ();
+      return 0;
+    }
 
+  if (here)
+    {
+      if (! pty_conversation ())
+        {
+          reset_tty ();
+          fprintf (stderr, "%s: ", argv[0]);
+          perror ("fdopen");
+          fail (argc, argv);
+        }
+      close (master);
+      reset_tty ();
+      return 0;
+    }
+  
   if (!eval)
     {
       printf ("Waiting for Emacs...");
@@ -546,6 +1110,7 @@
     printf ("\n");
   fflush (stdout);
 
+  reset_tty ();
   return 0;
 }