From: Mark Laws <mdl@60hz.org>
To: Daniel Colascione <dancol@dancol.org>
Cc: 19688@debbugs.gnu.org
Subject: bug#19688: [patch] add support for emacs daemon on Windows
Date: Tue, 27 Jan 2015 17:40:27 +0900 [thread overview]
Message-ID: <CADemMPN8Thav55Zb7McmxhA-_h4ZNS0qpQOLgiEqXr-QkDm-5Q@mail.gmail.com> (raw)
In-Reply-To: <54C62B6C.3050608@dancol.org>
[-- Attachment #1: Type: text/plain, Size: 1315 bytes --]
On Mon, Jan 26, 2015 at 8:56 PM, Daniel Colascione <dancol@dancol.org> wrote:
> Inheriting an anonymous event feels a bit cleaner to me; you can provide
> the HANDLE value in an environment variable or a command line parameter.
> Failing that, the event name should at least contain "emacs" somewhere
> so as to not confuse people browsing named object directories.
It may seem cleaner in that the event doesn't leak into a shared
namespace, but it requires significantly more code. Other handles may
also get unnecessarily inherited as a result, and it would require yet
more code to ensure they don't--it's difficult to ensure that it's
doing exactly what you want. The named event method is much, much
simpler. See for yourself; I've attached the patch written three ways:
a named event (simple), an inherited event passed through the
environment (a bit complicated), and an inherited event passed via the
command line (pretty heinous).
Also, I added multiple daemon support--after another look at the code,
I saw no reason to not include it, though there's still one difference
between the Windows and UNIX implementations: emacsclient can't
provide a name to be used for the daemon when it's starting a new
Emacs (but a user could provide one if they used emacs --daemon=foo).
--
|v\ /\ |\ |< |_ /\ \^| //
[-- Attachment #2: emacs-windows-daemon-inheritev.patch --]
[-- Type: application/octet-stream, Size: 12586 bytes --]
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index a04dda6..a1d1c44 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);
@@ -1459,9 +1450,66 @@ w32_give_focus (void)
/* Start the emacs daemon and try to connect to it. */
static void
+connect_to_emacs_socket (void)
+{
+#ifdef WINDOWSNT
+ /* It's just a progress message, so don't pop a dialog if this is
+ emacsclientw. */
+ if (!w32_window_app ())
+#endif
+ 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);
+ }
+}
+
+static void
start_daemon_and_retry_set_socket (void)
{
-#ifndef WINDOWSNT
+#ifdef WINDOWSNT
+ DWORD wait_result;
+ HANDLE w32_daemon_event;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES sa;
+ char evbuf[32];
+
+ ZeroMemory (&si, sizeof si);
+ si.cb = sizeof si;
+ ZeroMemory (&pi, sizeof pi);
+
+ sa.nLength = sizeof sa;
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ w32_daemon_event = CreateEvent (&sa, TRUE, FALSE, NULL);
+ if (w32_daemon_event == NULL)
+ {
+ message (true, "Couldn't create Windows daemon event");
+ exit (EXIT_FAILURE);
+ }
+
+ snprintf (evbuf, sizeof evbuf, "%p", w32_daemon_event);
+ SetEnvironmentVariable ("EMACS_W32_DAEMON_EVENT", evbuf);
+ if (!CreateProcess (NULL, "emacs --daemon", NULL, NULL, TRUE,
+ CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
+ {
+ message (true, "%s: error starting emacs daemon\n", progname);
+ exit (EXIT_FAILURE);
+ }
+
+ if (WaitForSingleObject (w32_daemon_event, INFINITE) != WAIT_OBJECT_0)
+ {
+ message (true, "Error while waiting for Windows daemon event");
+ exit (EXIT_FAILURE);
+ }
+ CloseHandle (w32_daemon_event);
+
+ /* Try connecting, the daemon should have started by now. */
+ connect_to_emacs_socket ();
+#elif !defined(WINDOWSNT)
pid_t dpid;
int status;
@@ -1479,12 +1527,7 @@ start_daemon_and_retry_set_socket (void)
}
/* Try connecting, the daemon should have started by now. */
- 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);
- }
+ connect_to_emacs_socket ();
}
else if (dpid < 0)
{
@@ -1511,7 +1554,7 @@ start_daemon_and_retry_set_socket (void)
execvp ("emacs", d_argv);
message (true, "%s: error starting emacs daemon\n", progname);
}
-#endif /* WINDOWSNT */
+#endif /* !WINDOWSNT */
}
int
diff --git a/lisp/frame.el b/lisp/frame.el
index 1d5bbf2..23bbc0d 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -536,7 +536,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 4a06374..17fe39b 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 166cd44..9585b17 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/src/dispnew.c b/src/dispnew.c
index 3c8117e..e86fbb8 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -5974,9 +5974,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 345fe3e..725605f 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;
@@ -980,8 +984,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))
@@ -1111,10 +1119,44 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
emacs_close (daemon_pipe[0]);
setsid ();
-#else /* DOS_NT */
+#elif defined(WINDOWSNT)
+ DWORD ret;
+ char evbuf[32];
+
+ ret = GetEnvironmentVariable ("EMACS_W32_DAEMON_EVENT", evbuf,
+ sizeof evbuf);
+ if (ret == 0)
+ {
+ if (GetLastError () == ERROR_ENVVAR_NOT_FOUND)
+ {
+ /* We weren't called by emacsclient; the user started the
+ daemon. */
+ w32_daemon_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ }
+ else
+ {
+ fprintf (stderr, "Couldn't copy handle address from environment\n");
+ exit (1);
+ }
+ }
+ else if (sscanf (evbuf, "%p", &w32_daemon_event) < 1)
+ {
+ fprintf (stderr, "Couldn't parse handle address from environment\n");
+ exit (1);
+ }
+
+ if (w32_daemon_event == NULL)
+ {
+ fprintf (stderr, "Couldn't create or get event handle for daemon\n");
+ exit (1);
+ }
+
+ if (dname_arg)
+ daemon_name = xstrdup (dname_arg);
+#else /* MSDOS */
fprintf (stderr, "This platform does not support the -daemon flag.\n");
exit (1);
-#endif /* DOS_NT */
+#endif /* MSDOS */
}
#if defined HAVE_PTHREAD && !defined SYSTEM_MALLOC \
@@ -2310,23 +2352,40 @@ If the daemon was given a name argument, return that name. */)
return Qnil;
}
-DEFUN ("daemon-initialized", Fdaemon_initialized, Sdaemon_initialized, 0, 0, 0,
- doc: /* Mark the Emacs daemon as being initialized.
-This finishes the daemonization process by doing the other half of detaching
-from the parent process and its tty file descriptors. */)
- (void)
+static void
+daemon_check_preconditions (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)
+#ifdef WINDOWSNT
+ /* IS_DAEMON above already checks that w32_daemon_event != NULL, so only
+ check that we successfully started the daemon here. */
+ if (w32_daemon_event == INVALID_HANDLE_VALUE)
+#else
+ if (daemon_pipe[1] < 0 )
+#endif
error ("The daemon has already been initialized");
if (NILP (Vafter_init_time))
error ("This function can only be called after loading the init files");
+}
+
+DEFUN ("daemon-initialized", Fdaemon_initialized, Sdaemon_initialized, 0, 0, 0,
+ doc: /* Mark the Emacs daemon as being initialized.
+This finishes the daemonization process by doing the other half of detaching
+from the parent process and its tty file descriptors. */)
+ (void)
+{
+ daemon_check_preconditions ();
+#ifdef WINDOWSNT
+ /* Signal the waiting emacsclient process. */
+ SetEvent (w32_daemon_event);
+ CloseHandle (w32_daemon_event);
+ w32_daemon_event = INVALID_HANDLE_VALUE;
+#else
+ int nfd;
+ bool err = 0;
/* Get rid of stdin, stdout and stderr. */
nfd = emacs_open ("/dev/null", O_RDWR, 0);
@@ -2350,6 +2409,7 @@ from the parent process and its tty file descriptors. */)
if (err)
error ("I/O error during daemon initialization");
+#endif
return Qt;
}
diff --git a/src/keyboard.c b/src/keyboard.c
index 383c109..8f0e18f 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3829,7 +3829,11 @@ kbd_buffer_get_event (KBOARD **kbp,
if (noninteractive
/* In case we are running as a daemon, only do this before
detaching from the terminal. */
+#ifdef WINDOWSNT
+ || (IS_DAEMON && w32_daemon_event != INVALID_HANDLE_VALUE))
+#else
|| (IS_DAEMON && daemon_pipe[1] >= 0))
+#endif
{
int c = getchar ();
XSETINT (obj, c);
diff --git a/src/lisp.h b/src/lisp.h
index f5242ab..0b85817 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4215,9 +4215,14 @@ 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)
+#elif defined(WINDOWSNT)
+extern void *w32_daemon_event;
+#define IS_DAEMON (w32_daemon_event != NULL)
+#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 3408bb9..dee7ce8 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -459,7 +459,11 @@ 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. */
+#ifdef WINDOWSNT
+ || (IS_DAEMON && w32_daemon_event != INVALID_HANDLE_VALUE))
+#else
|| (IS_DAEMON && (daemon_pipe[1] >= 0)))
+#endif
&& NILP (Vexecuting_kbd_macro))
{
val = read_minibuf_noninteractive (map, initial, prompt,
[-- Attachment #3: emacs-windows-daemon-inheritev-cmdline.patch --]
[-- Type: application/octet-stream, Size: 14251 bytes --]
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index a04dda6..a9bce5a 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);
@@ -1459,9 +1450,65 @@ w32_give_focus (void)
/* Start the emacs daemon and try to connect to it. */
static void
+connect_to_emacs_socket (void)
+{
+#ifdef WINDOWSNT
+ /* It's just a progress message, so don't pop a dialog if this is
+ emacsclientw. */
+ if (!w32_window_app ())
+#endif
+ 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);
+ }
+}
+
+static void
start_daemon_and_retry_set_socket (void)
{
-#ifndef WINDOWSNT
+#ifdef WINDOWSNT
+ DWORD wait_result;
+ HANDLE w32_daemon_event;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES sa;
+ char execstr[64];
+
+ ZeroMemory (&si, sizeof si);
+ si.cb = sizeof si;
+ ZeroMemory (&pi, sizeof pi);
+
+ sa.nLength = sizeof sa;
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ w32_daemon_event = CreateEvent (&sa, TRUE, FALSE, NULL);
+ if (w32_daemon_event == NULL)
+ {
+ message (true, "Couldn't create Windows daemon event");
+ exit (EXIT_FAILURE);
+ }
+
+ snprintf (execstr, sizeof execstr, "emacs --daemon=*%p", w32_daemon_event);
+ if (!CreateProcess (NULL, execstr, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL,
+ NULL, &si, &pi))
+ {
+ message (true, "%s: error starting emacs daemon\n", progname);
+ exit (EXIT_FAILURE);
+ }
+
+ if (WaitForSingleObject (w32_daemon_event, INFINITE) != WAIT_OBJECT_0)
+ {
+ message (true, "Error while waiting for Windows daemon event");
+ exit (EXIT_FAILURE);
+ }
+ CloseHandle (w32_daemon_event);
+
+ /* Try connecting, the daemon should have started by now. */
+ connect_to_emacs_socket ();
+#elif !defined(WINDOWSNT)
pid_t dpid;
int status;
@@ -1479,12 +1526,7 @@ start_daemon_and_retry_set_socket (void)
}
/* Try connecting, the daemon should have started by now. */
- 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);
- }
+ connect_to_emacs_socket ();
}
else if (dpid < 0)
{
@@ -1511,7 +1553,7 @@ start_daemon_and_retry_set_socket (void)
execvp ("emacs", d_argv);
message (true, "%s: error starting emacs daemon\n", progname);
}
-#endif /* WINDOWSNT */
+#endif /* !WINDOWSNT */
}
int
diff --git a/lisp/frame.el b/lisp/frame.el
index 1d5bbf2..23bbc0d 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -536,7 +536,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 4a06374..17fe39b 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 166cd44..9585b17 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/src/dispnew.c b/src/dispnew.c
index 3c8117e..e86fbb8 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -5974,9 +5974,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 345fe3e..d54643e 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;
@@ -687,6 +691,43 @@ close_output_streams (void)
_exit (EXIT_FAILURE);
}
+static void
+handle_dname_arg (char **dname_arg, char *dname_arg2, int sep)
+{
+ int has_dname = *dname_arg != NULL;
+ /* Were we called by emacsclient or did the user start the daemon? */
+#ifdef WINDOWSNT
+ int user_started_daemon = has_dname && !strchr (*dname_arg, sep);
+ if (!has_dname || user_started_daemon)
+ {
+ w32_daemon_event = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (user_started_daemon)
+ /* We already have a name in dname_arg or none was given. */
+ return;
+ else
+ goto process_dname;
+ }
+#endif
+ if (!has_dname || !strchr (*dname_arg, sep)
+ || strlen (*dname_arg) < 1 || strlen (*dname_arg) > 70)
+ {
+ fprintf (stderr, "emacs daemon: daemon name absent or too long\n");
+ exit (EXIT_CANNOT_INVOKE);
+ }
+
+process_dname:
+ dname_arg2[0] = '\0';
+#ifdef DAEMON_MUST_EXEC
+ sscanf (*dname_arg, "\n%d,%d\n%s", &(daemon_pipe[0]), &(daemon_pipe[1]),
+ dname_arg2);
+#elif defined(WINDOWSNT)
+ sscanf (*dname_arg, "*%p", &w32_daemon_event);
+#endif
+ /* On Windows, emacsclient will never pass a daemon name, so this will always
+ be NULL. */
+ *dname_arg = *dname_arg2 ? dname_arg2 : NULL;
+}
+
/* ARGSUSED */
int
main (int argc, char **argv)
@@ -704,9 +745,7 @@ main (int argc, char **argv)
bool no_loadup = 0;
char *junk = 0;
char *dname_arg = 0;
-#ifdef DAEMON_MUST_EXEC
char dname_arg2[80];
-#endif
char *ch_to_dir = 0;
/* If we use --chdir, this records the original directory. */
@@ -980,8 +1019,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))
@@ -1090,17 +1133,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
exit (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
}
- /* In exec'd: parse special dname into pipe and name info. */
- if (!dname_arg || !strchr (dname_arg, '\n')
- || strlen (dname_arg) < 1 || strlen (dname_arg) > 70)
- {
- fprintf (stderr, "emacs daemon: daemon name absent or too long\n");
- exit (EXIT_CANNOT_INVOKE);
- }
- dname_arg2[0] = '\0';
- sscanf (dname_arg, "\n%d,%d\n%s", &(daemon_pipe[0]), &(daemon_pipe[1]),
- dname_arg2);
- dname_arg = *dname_arg2 ? dname_arg2 : NULL;
+ handle_dname_arg (&dname_arg, dname_arg2, '\n');
fcntl (daemon_pipe[1], F_SETFD, FD_CLOEXEC);
}
#endif /* DAEMON_MUST_EXEC */
@@ -1111,10 +1144,21 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
emacs_close (daemon_pipe[0]);
setsid ();
-#else /* DOS_NT */
+#elif defined(WINDOWSNT)
+ handle_dname_arg (&dname_arg, dname_arg2, '*');
+
+ if (w32_daemon_event == NULL)
+ {
+ fprintf (stderr, "Couldn't create or get event handle for daemon\n");
+ exit (1);
+ }
+
+ if (dname_arg)
+ daemon_name = xstrdup (dname_arg);
+#else /* MSDOS */
fprintf (stderr, "This platform does not support the -daemon flag.\n");
exit (1);
-#endif /* DOS_NT */
+#endif /* MSDOS */
}
#if defined HAVE_PTHREAD && !defined SYSTEM_MALLOC \
@@ -2310,23 +2354,40 @@ If the daemon was given a name argument, return that name. */)
return Qnil;
}
-DEFUN ("daemon-initialized", Fdaemon_initialized, Sdaemon_initialized, 0, 0, 0,
- doc: /* Mark the Emacs daemon as being initialized.
-This finishes the daemonization process by doing the other half of detaching
-from the parent process and its tty file descriptors. */)
- (void)
+static void
+daemon_check_preconditions (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)
+#ifdef WINDOWSNT
+ /* IS_DAEMON above already checks that w32_daemon_event != NULL, so only
+ check that we successfully started the daemon here. */
+ if (w32_daemon_event == INVALID_HANDLE_VALUE)
+#else
+ if (daemon_pipe[1] < 0 )
+#endif
error ("The daemon has already been initialized");
if (NILP (Vafter_init_time))
error ("This function can only be called after loading the init files");
+}
+
+DEFUN ("daemon-initialized", Fdaemon_initialized, Sdaemon_initialized, 0, 0, 0,
+ doc: /* Mark the Emacs daemon as being initialized.
+This finishes the daemonization process by doing the other half of detaching
+from the parent process and its tty file descriptors. */)
+ (void)
+{
+ daemon_check_preconditions ();
+#ifdef WINDOWSNT
+ /* Signal the waiting emacsclient process. */
+ SetEvent (w32_daemon_event);
+ CloseHandle (w32_daemon_event);
+ w32_daemon_event = INVALID_HANDLE_VALUE;
+#else
+ int nfd;
+ bool err = 0;
/* Get rid of stdin, stdout and stderr. */
nfd = emacs_open ("/dev/null", O_RDWR, 0);
@@ -2350,6 +2411,7 @@ from the parent process and its tty file descriptors. */)
if (err)
error ("I/O error during daemon initialization");
+#endif
return Qt;
}
diff --git a/src/keyboard.c b/src/keyboard.c
index 383c109..8f0e18f 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3829,7 +3829,11 @@ kbd_buffer_get_event (KBOARD **kbp,
if (noninteractive
/* In case we are running as a daemon, only do this before
detaching from the terminal. */
+#ifdef WINDOWSNT
+ || (IS_DAEMON && w32_daemon_event != INVALID_HANDLE_VALUE))
+#else
|| (IS_DAEMON && daemon_pipe[1] >= 0))
+#endif
{
int c = getchar ();
XSETINT (obj, c);
diff --git a/src/lisp.h b/src/lisp.h
index f5242ab..0b85817 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4215,9 +4215,14 @@ 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)
+#elif defined(WINDOWSNT)
+extern void *w32_daemon_event;
+#define IS_DAEMON (w32_daemon_event != NULL)
+#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 3408bb9..dee7ce8 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -459,7 +459,11 @@ 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. */
+#ifdef WINDOWSNT
+ || (IS_DAEMON && w32_daemon_event != INVALID_HANDLE_VALUE))
+#else
|| (IS_DAEMON && (daemon_pipe[1] >= 0)))
+#endif
&& NILP (Vexecuting_kbd_macro))
{
val = read_minibuf_noninteractive (map, initial, prompt,
[-- Attachment #4: emacs-windows-daemon-namedev.patch --]
[-- Type: application/octet-stream, Size: 12039 bytes --]
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index a04dda6..eeb9ce2 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);
@@ -1459,9 +1450,57 @@ w32_give_focus (void)
/* Start the emacs daemon and try to connect to it. */
static void
+connect_to_emacs_socket (void)
+{
+#ifdef WINDOWSNT
+ /* It's just a progress message, so don't pop a dialog if this is
+ emacsclientw. */
+ if (!w32_window_app ())
+#endif
+ 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);
+ }
+}
+
+static void
start_daemon_and_retry_set_socket (void)
{
-#ifndef WINDOWSNT
+#ifdef 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);
+
+ if (!CreateProcess (NULL, "emacs --daemon", NULL, NULL, FALSE,
+ CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
+ {
+ message (true, "%s: error starting emacs daemon\n", progname);
+ 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 (WaitForSingleObject (w32_daemon_event, INFINITE) != WAIT_OBJECT_0)
+ {
+ message (true, "Error while waiting for Windows daemon event");
+ exit (EXIT_FAILURE);
+ }
+ CloseHandle (w32_daemon_event);
+
+ /* Try connecting, the daemon should have started by now. */
+ connect_to_emacs_socket ();
+#elif !defined(WINDOWSNT)
pid_t dpid;
int status;
@@ -1479,12 +1518,7 @@ start_daemon_and_retry_set_socket (void)
}
/* Try connecting, the daemon should have started by now. */
- 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);
- }
+ connect_to_emacs_socket ();
}
else if (dpid < 0)
{
@@ -1511,7 +1545,7 @@ start_daemon_and_retry_set_socket (void)
execvp ("emacs", d_argv);
message (true, "%s: error starting emacs daemon\n", progname);
}
-#endif /* WINDOWSNT */
+#endif /* !WINDOWSNT */
}
int
diff --git a/lisp/frame.el b/lisp/frame.el
index 1d5bbf2..23bbc0d 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -536,7 +536,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 4a06374..17fe39b 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 166cd44..9585b17 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/inc/ms-w32.h b/nt/inc/ms-w32.h
index adac2e3..8439163 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/dispnew.c b/src/dispnew.c
index 3c8117e..e86fbb8 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -5974,9 +5974,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 345fe3e..2932e36 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;
@@ -980,8 +984,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))
@@ -1111,10 +1119,20 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
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 event for Windows daemon\n");
+ exit (1);
+ }
+ if (dname_arg)
+ daemon_name = xstrdup (dname_arg);
+#else /* MSDOS */
fprintf (stderr, "This platform does not support the -daemon flag.\n");
exit (1);
-#endif /* DOS_NT */
+#endif /* MSDOS */
}
#if defined HAVE_PTHREAD && !defined SYSTEM_MALLOC \
@@ -2310,23 +2328,40 @@ If the daemon was given a name argument, return that name. */)
return Qnil;
}
-DEFUN ("daemon-initialized", Fdaemon_initialized, Sdaemon_initialized, 0, 0, 0,
- doc: /* Mark the Emacs daemon as being initialized.
-This finishes the daemonization process by doing the other half of detaching
-from the parent process and its tty file descriptors. */)
- (void)
+static void
+daemon_check_preconditions (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)
+#ifdef WINDOWSNT
+ /* IS_DAEMON above already checks that w32_daemon_event != NULL, so only
+ check that we successfully started the daemon here. */
+ if (w32_daemon_event == INVALID_HANDLE_VALUE)
+#else
+ if (daemon_pipe[1] < 0 )
+#endif
error ("The daemon has already been initialized");
if (NILP (Vafter_init_time))
error ("This function can only be called after loading the init files");
+}
+
+DEFUN ("daemon-initialized", Fdaemon_initialized, Sdaemon_initialized, 0, 0, 0,
+ doc: /* Mark the Emacs daemon as being initialized.
+This finishes the daemonization process by doing the other half of detaching
+from the parent process and its tty file descriptors. */)
+ (void)
+{
+ daemon_check_preconditions ();
+#ifdef WINDOWSNT
+ /* Signal the waiting emacsclient process. */
+ SetEvent (w32_daemon_event);
+ CloseHandle (w32_daemon_event);
+ w32_daemon_event = INVALID_HANDLE_VALUE;
+#else
+ int nfd;
+ bool err = 0;
/* Get rid of stdin, stdout and stderr. */
nfd = emacs_open ("/dev/null", O_RDWR, 0);
@@ -2350,6 +2385,7 @@ from the parent process and its tty file descriptors. */)
if (err)
error ("I/O error during daemon initialization");
+#endif
return Qt;
}
diff --git a/src/keyboard.c b/src/keyboard.c
index 383c109..8f0e18f 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3829,7 +3829,11 @@ kbd_buffer_get_event (KBOARD **kbp,
if (noninteractive
/* In case we are running as a daemon, only do this before
detaching from the terminal. */
+#ifdef WINDOWSNT
+ || (IS_DAEMON && w32_daemon_event != INVALID_HANDLE_VALUE))
+#else
|| (IS_DAEMON && daemon_pipe[1] >= 0))
+#endif
{
int c = getchar ();
XSETINT (obj, c);
diff --git a/src/lisp.h b/src/lisp.h
index f5242ab..0b85817 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4215,9 +4215,14 @@ 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)
+#elif defined(WINDOWSNT)
+extern void *w32_daemon_event;
+#define IS_DAEMON (w32_daemon_event != NULL)
+#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 3408bb9..dee7ce8 100644
--- a/src/minibuf.c
+++ b/src/minibuf.c
@@ -459,7 +459,11 @@ 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. */
+#ifdef WINDOWSNT
+ || (IS_DAEMON && w32_daemon_event != INVALID_HANDLE_VALUE))
+#else
|| (IS_DAEMON && (daemon_pipe[1] >= 0)))
+#endif
&& NILP (Vexecuting_kbd_macro))
{
val = read_minibuf_noninteractive (map, initial, prompt,
next prev parent reply other threads:[~2015-01-27 8:40 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-01-25 19:18 bug#19688: [patch] add support for emacs daemon on Windows Mark Laws
2015-01-25 20:34 ` Eli Zaretskii
[not found] ` <CADemMPM+Tix-6FJ+CO3HA8y7Cq6AV0kv_e6_qn7BaSw1QMOwTQ@mail.gmail.com>
2015-01-26 6:00 ` Eli Zaretskii
2015-01-26 7:40 ` Mark Laws
2015-01-26 11:56 ` Daniel Colascione
2015-01-27 8:40 ` Mark Laws [this message]
2015-01-30 0:36 ` Mark Laws
2015-01-30 6:28 ` Eli Zaretskii
2015-02-13 0:07 ` Mark Laws
2015-02-13 8:49 ` Eli Zaretskii
2015-02-14 12:10 ` Eli Zaretskii
2015-02-14 13:16 ` Mark Laws
2015-02-14 13:28 ` Eli Zaretskii
2015-02-14 13:37 ` Mark Laws
2015-02-14 15:24 ` Eli Zaretskii
2015-02-14 16:34 ` Mark Laws
2015-02-14 16:53 ` Eli Zaretskii
2015-02-14 16:57 ` Mark Laws
2015-02-14 17:23 ` Eli Zaretskii
2015-02-14 17:30 ` Mark Laws
2015-02-14 17:42 ` Eli Zaretskii
2015-02-14 17:57 ` Mark Laws
2015-02-14 18:26 ` Eli Zaretskii
2015-02-14 19:21 ` Mark Laws
2015-02-14 19:29 ` Eli Zaretskii
2015-02-14 21:15 ` Mark Laws
2015-02-19 16:31 ` Mark Laws
2015-02-19 16:56 ` Eli Zaretskii
2015-02-21 13:03 ` Eli Zaretskii
2015-02-21 19:30 ` Mark Laws
2015-02-27 14:26 ` Eli Zaretskii
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CADemMPN8Thav55Zb7McmxhA-_h4ZNS0qpQOLgiEqXr-QkDm-5Q@mail.gmail.com \
--to=mdl@60hz.org \
--cc=19688@debbugs.gnu.org \
--cc=dancol@dancol.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).