From 805fe507087b9675a010a30a8a8840587ffdf5be Mon Sep 17 00:00:00 2001 From: Mark Laws Date: Fri, 27 Feb 2015 12:43:30 +0200 Subject: [PATCH] Support daemon mode on MS-Windows (bug#19688) src/emacs.c [WINDOWSNT]: New global var. (main) [WINDOWSNT]: Initialize it to NULL. Create the event to signal clients we are ready for connections. (Fdaemon_initialized): Use DAEMON_RUNNING. [WINDOWSNT]: MS-Windows specific code to signal clients we are ready for connections. src/lisp.h (DAEMON_RUNNING): New macro, encapsulates Posix and MS-Windows conditions for running in daemon mode. src/minibuf.c (read_minibuf): Use DAEMON_RUNNING. src/keyboard.c (kbd_buffer_get_event): Use DAEMON_RUNNING. src/dispnew.c (init_display) [WINDOWSNT]: Initialize frames/terminal even in daemon mode. nt/inc/ms-w32.h (W32_DAEMON_EVENT): New macro. lib-src/emacsclient.c (decode_options) [WINDOWSNT]: Don't reject empty arguments for --alternate-editor. (print_help_and_exit) [WINDOWSNT]: Don't refrain from advertising empty arguments for --alternate-editor. (start_daemon_and_retry_set_socket) [WINDOWSNT]: MS-Windows specific code to start Emacs in daemon mode and wait for it to be ready for client connections. lisp/server.el (server-process-filter): Force GUI frames on MS-Windows in daemon mode, even if a TTY frame was requested. lisp/frameset.el (frameset-keep-original-display-p): Don't assume windows-nt cannot be in daemon mode. lisp/frame.el (window-system-for-display): Don't assume windows-nt cannot be in daemon mode. --- lib-src/ChangeLog | 11 ++++++ lib-src/emacsclient.c | 81 +++++++++++++++++++++++++++++++++++++------ lisp/ChangeLog | 12 +++++++ lisp/frame.el | 3 +- lisp/frameset.el | 4 +-- lisp/server.el | 12 +++++-- nt/ChangeLog | 5 +++ nt/inc/ms-w32.h | 2 ++ src/ChangeLog | 20 +++++++++++ src/dispnew.c | 5 ++- src/emacs.c | 37 ++++++++++++++++---- src/keyboard.c | 2 +- src/lisp.h | 9 ++++- src/minibuf.c | 2 +- 14 files changed, 179 insertions(+), 26 deletions(-) diff --git a/lib-src/ChangeLog b/lib-src/ChangeLog index 5c55bcea506..83855afa675 100644 --- a/lib-src/ChangeLog +++ b/lib-src/ChangeLog @@ -1,3 +1,14 @@ +2015-02-27 Mark Laws + + Support daemon mode on MS-Windows (bug#19688) + * emacsclient.c (decode_options) [WINDOWSNT]: Don't reject empty + arguments for --alternate-editor. + (print_help_and_exit) [WINDOWSNT]: Don't refrain from advertising + empty arguments for --alternate-editor. + (start_daemon_and_retry_set_socket) [WINDOWSNT]: MS-Windows + specific code to start Emacs in daemon mode and wait for it to be + ready for client connections. + 2015-02-23 Pete Williamson (tiny change) Use ${EXEEXT} more uniformly in makefiles diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c index a04dda6408f..806275f5b1d 100644 --- a/lib-src/emacsclient.c +++ b/lib-src/emacsclient.c @@ -595,13 +595,6 @@ decode_options (int argc, char **argv) display = NULL; tty = 1; } - - if (alternate_editor && alternate_editor[0] == '\0') - { - message (true, "--alternate-editor argument or ALTERNATE_EDITOR variable cannot be\n\ -an empty string"); - exit (EXIT_FAILURE); - } #endif /* WINDOWSNT */ } @@ -642,10 +635,8 @@ The following OPTIONS are accepted:\n\ Set filename of the TCP authentication file\n\ -a EDITOR, --alternate-editor=EDITOR\n\ Editor to fallback to if the server is not running\n" -#ifndef WINDOWSNT " If EDITOR is the empty string, start Emacs in daemon\n\ mode and try connecting again\n" -#endif /* not WINDOWSNT */ "\n\ Report bugs with M-x report-emacs-bug.\n"); exit (EXIT_SUCCESS); @@ -1511,7 +1502,77 @@ start_daemon_and_retry_set_socket (void) execvp ("emacs", d_argv); message (true, "%s: error starting emacs daemon\n", progname); } -#endif /* WINDOWSNT */ +#else /* WINDOWSNT */ + DWORD wait_result; + HANDLE w32_daemon_event; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory (&si, sizeof si); + si.cb = sizeof si; + ZeroMemory (&pi, sizeof pi); + + /* We start Emacs in daemon mode, and then wait for it to signal us + it is ready to accept client connections, by asserting an event + whose name is known to the daemon (defined by nt/inc/ms-w32.h). */ + + if (!CreateProcess (NULL, "emacs --daemon", NULL, NULL, FALSE, + CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) + { + char* msg = NULL; + + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_ARGUMENT_ARRAY, + NULL, GetLastError (), 0, (LPTSTR)&msg, 0, NULL); + message (true, "%s: error starting emacs daemon (%s)\n", progname, msg); + exit (EXIT_FAILURE); + } + + w32_daemon_event = CreateEvent (NULL, TRUE, FALSE, W32_DAEMON_EVENT); + if (w32_daemon_event == NULL) + { + message (true, "Couldn't create Windows daemon event"); + exit (EXIT_FAILURE); + } + if ((wait_result = WaitForSingleObject (w32_daemon_event, INFINITE)) + != WAIT_OBJECT_0) + { + char *msg = NULL; + + switch (wait_result) + { + case WAIT_ABANDONED: + msg = "The daemon exited unexpectedly"; + break; + case WAIT_TIMEOUT: + /* Can't happen due to INFINITE. */ + default: + case WAIT_FAILED: + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER + | FORMAT_MESSAGE_ARGUMENT_ARRAY, + NULL, GetLastError (), 0, (LPTSTR)&msg, 0, NULL); + break; + } + message (true, "Error: Could not start the Emacs daemon: %s\n", msg); + exit (EXIT_FAILURE); + } + CloseHandle (w32_daemon_event); + + /* Try connecting, the daemon should have started by now. */ + /* It's just a progress message, so don't pop a dialog if this is + emacsclientw. */ + if (!w32_window_app ()) + message (true, + "Emacs daemon should have started, trying to connect again\n"); + if ((emacs_socket = set_socket (1)) == INVALID_SOCKET) + { + message (true, + "Error: Cannot connect even after starting the Emacs daemon\n"); + exit (EXIT_FAILURE); + } +#endif /* WINDOWSNT */ } int diff --git a/lisp/ChangeLog b/lisp/ChangeLog index e9f62365f03..b9681d35cf0 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,15 @@ +2015-02-27 Mark Laws + + Support daemon mode on MS-Windows (bug#19688) + * server.el (server-process-filter): Force GUI frames on + MS-Windows in daemon mode, even if a TTY frame was requested. + + * frameset.el (frameset-keep-original-display-p): Don't assume + windows-nt cannot be in daemon mode. + + * frame.el (window-system-for-display): Don't assume windows-nt + cannot be in daemon mode. + 2015-02-26 Ivan Shmakov * faces.el (face-list-p): Split from face-at-point. diff --git a/lisp/frame.el b/lisp/frame.el index 0096ef9696a..c81ee9bfa61 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -546,7 +546,8 @@ is not considered (see `next-frame')." Return nil if we don't know how to interpret DISPLAY." ;; MS-Windows doesn't know how to create a GUI frame in a -nw session. (if (and (eq system-type 'windows-nt) - (null (window-system))) + (null (window-system)) + (not (daemonp))) nil (cl-loop for descriptor in display-format-alist for pattern = (car descriptor) diff --git a/lisp/frameset.el b/lisp/frameset.el index 4a0637439db..17fe39be844 100644 --- a/lisp/frameset.el +++ b/lisp/frameset.el @@ -1022,8 +1022,8 @@ Internal use only." (defun frameset-keep-original-display-p (force-display) "True if saved frames' displays should be honored. For the meaning of FORCE-DISPLAY, see `frameset-restore'." - (cond ((daemonp) t) - ((eq system-type 'windows-nt) nil) ;; Does ns support more than one display? + (cond ((eq system-type 'windows-nt) nil) ;; Does ns support more than one display? + ((daemonp) t) (t (not force-display)))) (defun frameset-minibufferless-first-p (frame1 _frame2) diff --git a/lisp/server.el b/lisp/server.el index 166cd44bb2e..9585b1755c6 100644 --- a/lisp/server.el +++ b/lisp/server.el @@ -1139,9 +1139,12 @@ The following commands are accepted by the client: ;; frame. If running a GUI server, force the frame ;; type to GUI. (Cygwin is perfectly happy with ;; multi-tty support, so don't override the user's - ;; choice there.) + ;; choice there.) In daemon mode on Windows, we can't + ;; make tty frames, so force the frame type to GUI + ;; there too. (when (and (eq system-type 'windows-nt) - (eq window-system 'w32)) + (or (daemonp) + (eq window-system 'w32))) (push "-window-system" args-left))) ;; -position LINE[:COLUMN]: Set point to the given @@ -1215,7 +1218,10 @@ The following commands are accepted by the client: terminal-frame))))) (setq tty-name nil tty-type nil) (if display (server-select-display display))) - ((eq tty-name 'window-system) + ((or (and (eq system-type 'windows-nt) + (daemonp) + (setq display "w32")) + (eq tty-name 'window-system)) (server-create-window-system-frame display nowait proc parent-id frame-parameters)) diff --git a/nt/ChangeLog b/nt/ChangeLog index b9966fb27d8..240f58c850b 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog @@ -1,3 +1,8 @@ +2015-02-27 Mark Laws + + Support daemon mode on MS-Windows (bug#19688) + * inc/ms-w32.h (W32_DAEMON_EVENT): New macro. + 2015-01-16 Eli Zaretskii * Makefile.in (AM_V_CC, am__v_CC_, am__v_CC_0, am__v_CC_1) diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h index adac2e3b4a1..c06ed588818 100644 --- a/nt/inc/ms-w32.h +++ b/nt/inc/ms-w32.h @@ -597,5 +597,7 @@ extern void _DebPrint (const char *fmt, ...); #endif #endif +/* Event name for when emacsclient starts the Emacs daemon on Windows. */ +#define W32_DAEMON_EVENT "EmacsServerEvent" /* ============================================================ */ diff --git a/src/ChangeLog b/src/ChangeLog index bf4043666c6..61bb321649f 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,23 @@ +2015-02-27 Mark Laws + + Support daemon mode on MS-Windows (bug#19688) + * emacs.c [WINDOWSNT]: New global var. + (main) [WINDOWSNT]: Initialize it to NULL. Create the event to + signal clients we are ready for connections. + (Fdaemon_initialized): Use DAEMON_RUNNING. + [WINDOWSNT]: MS-Windows specific code to signal clients we are + ready for connections. + + * lisp.h (DAEMON_RUNNING): New macro, encapsulates Posix and + MS-Windows conditions for running in daemon mode. + + * minibuf.c (read_minibuf): Use DAEMON_RUNNING. + + * keyboard.c (kbd_buffer_get_event): Use DAEMON_RUNNING. + + * dispnew.c (init_display) [WINDOWSNT]: Initialize frames/terminal + even in daemon mode. + 2015-02-26 Jan Djärv * xmenu.c (create_and_show_popup_menu): Call XTranslateCoordinates, diff --git a/src/dispnew.c b/src/dispnew.c index a1782913154..6bc24697cb7 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -5949,9 +5949,12 @@ init_display (void) } #endif /* SIGWINCH */ - /* If running as a daemon, no need to initialize any frames/terminal. */ + /* If running as a daemon, no need to initialize any frames/terminal, + except on Windows, where we at least want to initialize it. */ +#ifndef WINDOWSNT if (IS_DAEMON) return; +#endif /* If the user wants to use a window system, we shouldn't bother initializing the terminal. This is especially important when the diff --git a/src/emacs.c b/src/emacs.c index cb0c8417794..ca5633da2dd 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -195,9 +195,13 @@ bool no_site_lisp; /* Name for the server started by the daemon.*/ static char *daemon_name; +#ifndef WINDOWSNT /* Pipe used to send exit notification to the daemon parent at startup. */ int daemon_pipe[2]; +#else +HANDLE w32_daemon_event; +#endif /* Save argv and argc. */ char **initial_argv; @@ -982,8 +986,12 @@ main (int argc, char **argv) exit (0); } +#ifndef WINDOWSNT /* Make sure IS_DAEMON starts up as false. */ daemon_pipe[1] = 0; +#else + w32_daemon_event = NULL; +#endif if (argmatch (argv, argc, "-daemon", "--daemon", 5, NULL, &skip_args) || argmatch (argv, argc, "-daemon", "--daemon", 5, &dname_arg, &skip_args)) @@ -1107,16 +1115,25 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem } #endif /* DAEMON_MUST_EXEC */ - if (dname_arg) - daemon_name = xstrdup (dname_arg); /* Close unused reading end of the pipe. */ emacs_close (daemon_pipe[0]); setsid (); -#else /* DOS_NT */ +#elif defined(WINDOWSNT) + /* Indicate that we want daemon mode. */ + w32_daemon_event = CreateEvent (NULL, TRUE, FALSE, W32_DAEMON_EVENT); + if (w32_daemon_event == NULL) + { + fprintf (stderr, "Couldn't create MS-Windows event for daemon: %s\n", + w32_strerror (0)); + exit (1); + } +#else /* MSDOS */ fprintf (stderr, "This platform does not support the -daemon flag.\n"); exit (1); -#endif /* DOS_NT */ +#endif /* MSDOS */ + if (dname_arg) + daemon_name = xstrdup (dname_arg); } #if defined HAVE_PTHREAD && !defined SYSTEM_MALLOC \ @@ -2313,17 +2330,18 @@ This finishes the daemonization process by doing the other half of detaching from the parent process and its tty file descriptors. */) (void) { - int nfd; bool err = 0; if (!IS_DAEMON) error ("This function can only be called if emacs is run as a daemon"); - if (daemon_pipe[1] < 0) + if (!DAEMON_RUNNING) error ("The daemon has already been initialized"); if (NILP (Vafter_init_time)) error ("This function can only be called after loading the init files"); +#ifndef WINDOWSNT + int nfd; /* Get rid of stdin, stdout and stderr. */ nfd = emacs_open ("/dev/null", O_RDWR, 0); @@ -2344,6 +2362,13 @@ from the parent process and its tty file descriptors. */) err |= emacs_close (daemon_pipe[1]) != 0; /* Set it to an invalid value so we know we've already run this function. */ daemon_pipe[1] = -1; +#else /* WINDOWSNT */ + /* Signal the waiting emacsclient process. */ + err |= SetEvent (w32_daemon_event) == 0; + err |= CloseHandle (w32_daemon_event) == 0; + /* Set it to an invalid value so we know we've already run this function. */ + w32_daemon_event = INVALID_HANDLE_VALUE; +#endif if (err) error ("I/O error during daemon initialization"); diff --git a/src/keyboard.c b/src/keyboard.c index c2174539ea7..e1c5691324d 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -3853,7 +3853,7 @@ kbd_buffer_get_event (KBOARD **kbp, if (noninteractive /* In case we are running as a daemon, only do this before detaching from the terminal. */ - || (IS_DAEMON && daemon_pipe[1] >= 0)) + || (IS_DAEMON && DAEMON_RUNNING)) { int c = getchar (); XSETINT (obj, c); diff --git a/src/lisp.h b/src/lisp.h index 9764b096ef0..fb436776121 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4222,9 +4222,16 @@ extern bool noninteractive; extern bool no_site_lisp; /* Pipe used to send exit notification to the daemon parent at - startup. */ + startup. On Windows, we use a kernel event instead. */ +#ifndef WINDOWSNT extern int daemon_pipe[2]; #define IS_DAEMON (daemon_pipe[1] != 0) +#define DAEMON_RUNNING (daemon_pipe[1] >= 0) +#else /* WINDOWSNT */ +extern void *w32_daemon_event; +#define IS_DAEMON (w32_daemon_event != NULL) +#define DAEMON_RUNNING (w32_daemon_event != INVALID_HANDLE_VALUE) +#endif /* True if handling a fatal error already. */ extern bool fatal_error_in_progress; diff --git a/src/minibuf.c b/src/minibuf.c index 2dc5c544457..e7c288b251b 100644 --- a/src/minibuf.c +++ b/src/minibuf.c @@ -459,7 +459,7 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, if ((noninteractive /* In case we are running as a daemon, only do this before detaching from the terminal. */ - || (IS_DAEMON && (daemon_pipe[1] >= 0))) + || (IS_DAEMON && DAEMON_RUNNING)) && NILP (Vexecuting_kbd_macro)) { val = read_minibuf_noninteractive (map, initial, prompt, -- 2.39.2