Mercurial > emacs
changeset 83043:78a785f205ea
Added -w option to emacsclient for opening a new X frame.
lib-src/emacsclient.c (window_system): New variable.
(frame): Renamed to tty for consistency with the option name.
(longopts, print_help_and_exit): Added -w option. (Suggested by Ami
Fischman <ami at fischman dot org>.
(decode_options): Initialize display to $DISPLAY. Handle -w option.
(main): Implement the -w option. Changed to a more elaborate protocol
between Emacs and emacsclient, in preparation to suspend support.
lisp/server.el (server-frames): New variable.
(server-handle-delete-frame): New function.
(server-start): Add it to delete-frame-functions.
(server-select-display): Don't make the new frame invisible.
(server-with-errors-reported): New macro for brevity.
(server-process-filter): Implement the "-window-system" command.
Use server-with-errors-reported. Fixed regexp for +line:column syntax.
Use the new protocol.
git-archimport-id: lorentey@elte.hu--2004/emacs--multi-tty--0--patch-83
author | Karoly Lorentey <lorentey@elte.hu> |
---|---|
date | Thu, 19 Feb 2004 23:55:51 +0000 |
parents | 968e8c7ff1f4 |
children | 52039abab942 |
files | lib-src/emacsclient.c lisp/server.el |
diffstat | 2 files changed, 138 insertions(+), 71 deletions(-) [+] |
line wrap: on
line diff
--- a/lib-src/emacsclient.c Thu Feb 19 13:25:32 2004 +0000 +++ b/lib-src/emacsclient.c Thu Feb 19 23:55:51 2004 +0000 @@ -70,11 +70,14 @@ /* Nonzero means args are expressions to be evaluated. --eval. */ int eval = 0; +/* Nonzero means open a new graphical frame. */ +int window_system = 0; + /* The display on which Emacs should work. --display. */ char *display = NULL; /* Nonzero means open a new Emacs frame on the current terminal. */ -int frame = 0; +int tty = 0; /* If non-NULL, the name of an editor to fallback to if the server is not running. --alternate-editor. */ @@ -92,6 +95,7 @@ { "help", no_argument, NULL, 'H' }, { "version", no_argument, NULL, 'V' }, { "tty", no_argument, NULL, 't' }, + { "window-system", no_argument, NULL, 'w' }, { "alternate-editor", required_argument, NULL, 'a' }, { "socket-name", required_argument, NULL, 's' }, { "display", required_argument, NULL, 'd' }, @@ -107,11 +111,12 @@ char **argv; { alternate_editor = getenv ("ALTERNATE_EDITOR"); + display = getenv ("DISPLAY"); while (1) { int opt = getopt_long (argc, argv, - "VHnea:s:d:t", longopts, 0); + "VHnea:s:d:tw", longopts, 0); if (opt == EOF) break; @@ -149,7 +154,13 @@ break; case 't': - frame = 1; + tty = 1; + window_system = 0; + break; + + case 'w': + window_system = 1; + tty = 0; break; case 'H': @@ -163,11 +174,10 @@ } } - if (frame) { + if (tty) { nowait = 0; display = 0; } - } void @@ -182,6 +192,7 @@ -V, --version Just print a version info and return\n\ -H, --help Print this usage information message\n\ -t, --tty Open a new Emacs frame on the current terminal\n\ +-w, --window-system Open a new graphical Emacs frame\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\ @@ -272,16 +283,6 @@ int emacs_pid; -#ifdef nec_ews_svr4 -extern char *_sobuf ; -#else -#if defined (USG) || defined (DGUX) -unsigned char _sobuf[BUFSIZ+8]; -#else -char _sobuf[BUFSIZ]; -#endif -#endif - /* A signal handler that passes the signal to the Emacs process. Useful for SIGWINCH. */ @@ -395,7 +396,7 @@ /* Process options. */ decode_options (argc, argv); - if ((argc - optind < 1) && !eval && !frame) + if ((argc - optind < 1) && !eval && !tty && !window_system) { fprintf (stderr, "%s: file name or argument required\n", progname); fprintf (stderr, "Try `%s --help' for more information\n", progname); @@ -574,7 +575,7 @@ fprintf (out, " "); } - if (frame) + if (tty) { char *tty_name = ttyname (fileno (stdin)); if (! tty_name) @@ -588,6 +589,9 @@ quote_file_name (getenv("TERM"), out); fprintf (out, " "); } + + if (window_system) + fprintf (out, "-window-system "); if ((argc - optind > 0)) { @@ -617,7 +621,7 @@ } else { - if (!frame) + if (!tty && !window_system) { while ((str = fgets (string, BUFSIZ, stdin))) { @@ -636,7 +640,7 @@ return 0; } - if (!eval && !frame) + if (!eval && !tty) { printf ("Waiting for Emacs..."); needlf = 2; @@ -646,18 +650,29 @@ /* Now, wait for an answer and print any messages. */ while ((str = fgets (string, BUFSIZ, in))) { - if (frame) + if (strprefix ("-emacs-pid ", str)) + { + emacs_pid = strtol (string + strlen ("-emacs-pid"), NULL, 10); + } + else if (strprefix ("-print ", str)) { - if (strprefix ("emacs-pid ", str)) - { - emacs_pid = strtol (string + strlen ("emacs-pid"), NULL, 10); - } + if (needlf == 2) + printf ("\n"); + printf ("%s", str + strlen ("-print ")); + needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; + } + else if (strprefix ("-error ", str)) + { + if (needlf == 2) + printf ("\n"); + printf ("*ERROR*: %s", str + strlen ("-print ")); + needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; } else { if (needlf == 2) printf ("\n"); - printf ("%s", str); + printf ("*ERROR*: Unknown message: %s", str); needlf = str[0] == '\0' ? needlf : str[strlen (str) - 1] != '\n'; } }
--- a/lisp/server.el Thu Feb 19 13:25:32 2004 +0000 +++ b/lisp/server.el Thu Feb 19 23:55:51 2004 +0000 @@ -111,8 +111,18 @@ Each element is (CLIENTID TTY) where CLIENTID is a string that can be given to the server process to identify a client. TTY is the name of the tty device. -When all the buffers of the client are marked as \"done\", -the frame is deleted.") + +When all frames on the device are deleted, the server quits the +connection to the client, and vice versa.") + +(defvar server-frames nil + "List of current window-system frames used by the server. +Each element is (CLIENTID FRAME) where CLIENTID is a string +that can be given to the server process to identify a client. +FRAME is the frame that was opened by the client. + +When the frame is deleted, the server closes the connection to +the client, and vice versa.") (defvar server-buffer-clients nil "List of client ids for clients requesting editing of current buffer.") @@ -211,7 +221,7 @@ (server-log (format "Status changed to %s" (process-status proc)) proc)) (defun server-handle-delete-tty (tty) - "Delete the client connection when the emacsclient frame is deleted." + "Delete the client connection when the emacsclient terminal device is closed." (dolist (entry server-ttys) (let ((proc (nth 0 entry)) (term (nth 1 entry))) @@ -224,6 +234,20 @@ ;; `emacsclient -t -e '(delete-frame)'' correctly. (setq server-clients (delq client server-clients)))))))) +(defun server-handle-delete-frame (frame) + "Delete the client connection when the emacsclient frame is deleted." + (dolist (entry server-frames) + (let ((proc (nth 0 entry)) + (f (nth 1 entry))) + (when (equal frame f) + (let ((client (assq proc server-clients))) + (setq server-frames (delq entry server-frames)) + (delete-process (car client)) + (when (assq proc server-clients) + ;; This seems to be necessary to handle + ;; `emacsclient -t -e '(delete-frame)'' correctly. + (setq server-clients (delq client server-clients)))))))) + (defun server-select-display (display) ;; If the current frame is on `display' we're all set. (unless (equal (frame-parameter (selected-frame) 'display) display) @@ -235,14 +259,14 @@ ;; and select it. (unless (equal (frame-parameter (selected-frame) 'display) display) (select-frame - (make-frame-on-display - display + (make-frame-on-display display))))) ;; This frame is only there in place of an actual "current display" ;; setting, so we want it to be as unobtrusive as possible. That's ;; what the invisibility is for. The minibuffer setting is so that ;; we don't end up displaying a buffer in it (which noone would ;; notice). - '((visibility . nil) (minibuffer . only))))))) + ;; XXX I have found this behaviour to be surprising and annoying. -- Lorentey + ;; '((visibility . nil) (minibuffer . only))))))) (defun server-unquote-arg (arg) (replace-regexp-in-string @@ -301,6 +325,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 'delete-frame-functions 'server-handle-delete-frame) (setq server-process (make-network-process :name "server" :family 'local :server t :noquery t @@ -324,6 +349,17 @@ ;; nothing if there is one (for multiple Emacs sessions)? (server-start (not server-mode))) +(defmacro server-with-errors-reported (&rest forms) + "Evaluate FORMS; if an error occurs, report it to the client +and return nil. Otherwise, return the result of the last form. +For use in server-process-filter only." + `(condition-case err + (progn ,@forms) + (error (ignore-errors + (process-send-string + proc (concat "-error " (error-message-string err))) + (setq request ""))))) + (defun server-process-filter (proc string) "Process a request from the server to edit some files. PROC is the server process. Format of STRING is \"PATH PATH PATH... \\n\"." @@ -339,7 +375,7 @@ (coding-system (and default-enable-multibyte-characters (or file-name-coding-system default-file-name-coding-system))) - client nowait eval newframe + client nowait eval newframe display registered ; t if the client is already added to server-clients. (files nil) (lineno 1) @@ -353,37 +389,53 @@ (cond ((equal "-nowait" arg) (setq nowait t)) ((equal "-eval" arg) (setq eval t)) + ((and (equal "-display" arg) (string-match "\\([^ ]*\\) " request)) - (let ((display (server-unquote-arg (match-string 1 request)))) - (setq request (substring request (match-end 0))) - (condition-case err - (server-select-display display) - (error (process-send-string proc (nth 1 err)) - (setq request ""))))) - ;; Open a new frame at the client. ARG is the name of the pseudo tty. + (setq display (match-string 1 request) + request (substring request (match-end 0)))) + + ;; Open a new X frame. + ((equal "-window-system" arg) + (server-with-errors-reported + (let ((frame (make-frame-on-display + (or display + (frame-parameter nil 'display) + (getenv "DISPLAY") + (error "Please specify display"))))) + (push (list proc frame) server-frames) + (select-frame frame) + ;; This makes sure that `emacsclient -w -e '(delete-frame)'' works right. + (push client server-clients) + (setq registered t + newframe t)))) + + ;; Open a new tty frame at the client. ARG is the name of the pseudo tty. ((and (equal "-tty" arg) (string-match "\\([^ ]*\\) \\([^ ]*\\) " request)) (let ((tty (server-unquote-arg (match-string 1 request))) (type (server-unquote-arg (match-string 2 request)))) (setq request (substring request (match-end 0))) - (condition-case err - (let ((frame (make-frame-on-tty tty type))) - (setq server-ttys (cons (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 - newframe t)) - (error (process-send-string proc (concat (nth 1 err) "\n")) - (setq request ""))))) + (server-with-errors-reported + (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 + newframe t))))) + ;; ARG is a line number option. ((string-match "\\`\\+[0-9]+\\'" arg) (setq lineno (string-to-int (substring arg 1)))) + ;; ARG is line number:column option. - ((string-match "\\`+\\([0-9]+\\):\\([0-9]+\\)\\'" arg) + ((string-match "\\`\\+\\([0-9]+\\):\\([0-9]+\\)\\'" arg) (setq lineno (string-to-int (match-string 1 arg)) columnno (string-to-int (match-string 2 arg)))) + + ;; ARG is a filename or a Lisp expression. (t + ;; Undo the quoting that emacsclient does ;; for certain special characters. (setq arg (server-unquote-arg arg)) @@ -391,17 +443,14 @@ (if coding-system (setq arg (decode-coding-string arg coding-system))) (if eval - (condition-case err - (let ((v (eval (car (read-from-string arg))))) - (when (and (not newframe) v) - (with-temp-buffer - (let ((standard-output (current-buffer))) - (pp v) - (process-send-region proc (point-min) (point-max)))))) - (error - (ignore-errors - (process-send-string - proc (concat "*Error* " (error-message-string err)))))) + (server-with-errors-reported + (let ((v (eval (car (read-from-string arg))))) + (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. @@ -409,6 +458,7 @@ (push (list arg lineno columnno) files)) (setq lineno 1) (setq columnno 0))))) + (when files (run-hooks 'pre-command-hook) (server-visit-files files client nowait) @@ -506,15 +556,17 @@ ;; If client now has no pending buffers, ;; tell it that it is done, and forget it entirely. (unless (cdr client) - (let ((tty (assq (car client) server-ttys))) - (if tty - ;; Be careful, if we delete the process before the - ;; tty, then the terminal modes will not be restored - ;; correctly. - (delete-tty (cadr tty)) - (delete-process (car client)) - (server-log "Close" (car client)) - (setq server-clients (delq client server-clients)))))) + (let ((tty (cadr (assq (car client) server-ttys))) + (frame (cadr (assq (car client) server-frames)))) + (cond + ;; Be careful, if we delete the process before the + ;; tty, then the terminal modes will not be restored + ;; correctly. + (tty (delete-tty tty)) + (frame (delete-frame frame)) + (t (delete-process (car client)) + (server-log "Close" (car client)) + (setq server-clients (delq client server-clients))))))) (setq old-clients (cdr old-clients))) (if (and (bufferp buffer) (buffer-name buffer)) ;; We may or may not kill this buffer;