From: Mark Laws <mdl@60hz.org>
To: 19688@debbugs.gnu.org
Subject: bug#19688: [patch] add support for emacs daemon on Windows
Date: Mon, 26 Jan 2015 04:18:34 +0900 [thread overview]
Message-ID: <CADemMPPXYzgmvBLzAafpZiVUSvr1RULUAtaREZOP06m7s3ik9Q@mail.gmail.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 1351 bytes --]
Hi,
I am not on this list so please CC me in any replies.
Attached is a patch which should apply cleanly on top of master
(a3689d3c661fe36df971c875760f8d500b5ae994 as of this email). It allows
Emacs to run as a daemon on Windows. Without daemon mode, emacsclient
-a "" does not work, which makes it impossible to pass elisp to Emacs
via emacsclient unless Emacs is already running. In other words, this
will now work correctly:
emacsclient -a "" -e "(ediff-merge-files-with-ancestor ...)"
Previously, -a "" produced an error message on Windows. There was no
workaround, because:
emacs -a emacs -e "(ediff-merge-files-with-ancestor ...)"
would start a new Emacs with a buffer called
"(ediff-merge-files-with-ancestor ...)".
The functionality is identical to the UNIX implementation with one
difference: I didn't allow for running multiple/named daemons. The
reason I didn't was because the code for that in emacsclient.c took
the daemon name from the socket name if one was provided, and because
filesystem sockets are not supported on Windows, I couldn't think of a
way to provide identical behavior across UNIX and Windows.
This patch has been heavily tested against emacs-24 without any
issues; it seems to work well against master as well.
Thanks to Eli Zaretskii for help with debugging.
Cheers,
Mark Laws
--
|v\ /\ |\ |< |_ /\ \^| //
[-- Attachment #2: emacs-windows-daemon.patch --]
[-- Type: application/octet-stream, Size: 14011 bytes --]
diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in
index d2705e7..b34daa4 100644
--- a/lib-src/Makefile.in
+++ b/lib-src/Makefile.in
@@ -389,12 +389,16 @@ movemail${EXEEXT}: ${srcdir}/movemail.c pop.o $(NTLIB) $(config_h)
pop.o: ${srcdir}/pop.c ${srcdir}/pop.h ${srcdir}/../lib/min-max.h $(config_h)
$(AM_V_CC)$(CC) -c ${CPP_CFLAGS} ${MOVE_FLAGS} $<
-emacsclient${EXEEXT}: ${srcdir}/emacsclient.c $(NTLIB) $(config_h)
+server-guid_h = ../src/server-guid.h
+
+emacsclient${EXEEXT}: ${srcdir}/emacsclient.c $(NTLIB) $(config_h) \
+ $(server-guid_h)
$(AM_V_CCLD)$(CC) ${ALL_CFLAGS} $< \
-DVERSION="\"${version}\"" $(NTLIB) $(LOADLIBES) $(LIB_FDATASYNC) \
$(LIB_WSOCK32) $(LIBS_ECLIENT) -o $@
-emacsclientw${EXEEXT}: ${srcdir}/emacsclient.c $(NTLIB) $(CLIENTRES) $(config_h)
+emacsclientw${EXEEXT}: ${srcdir}/emacsclient.c $(NTLIB) $(CLIENTRES) \
+ $(config_h) $(server-guid_h)
$(AM_V_CCLD)$(CC) ${ALL_CFLAGS} $(CLIENTRES) -mwindows $< \
-DVERSION="\"${version}\"" $(LOADLIBES) $(LIB_FDATASYNC) \
$(LIB_WSOCK32) $(LIBS_ECLIENT) -o $@
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index a04dda6..9d4f3ce 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -33,6 +33,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
# include <io.h>
# include <winsock2.h>
+# include "server-guid.h"
+
# define NO_SOCKETS_IN_FILE_SYSTEM
# define HSOCKET SOCKET
@@ -595,13 +597,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 +637,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 +1452,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_EMACS_SERVER_GUID);
+ 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 +1520,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 +1547,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..8227eb0 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -39,6 +39,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <mbstring.h>
#include "w32.h"
#include "w32heap.h"
+#include "server-guid.h"
#endif
#if defined WINDOWSNT || defined HAVE_NTGUI
@@ -195,9 +196,15 @@ 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
+bool w32_is_daemon;
+bool w32_daemon_is_initialized;
+static HANDLE w32_daemon_event;
+#endif
/* Save argv and argc. */
char **initial_argv;
@@ -980,8 +987,10 @@ main (int argc, char **argv)
exit (0);
}
+#ifndef WINDOWSNT
/* Make sure IS_DAEMON starts up as false. */
daemon_pipe[1] = 0;
+#endif
if (argmatch (argv, argc, "-daemon", "--daemon", 5, NULL, &skip_args)
|| argmatch (argv, argc, "-daemon", "--daemon", 5, &dname_arg, &skip_args))
@@ -1111,10 +1120,12 @@ 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)
+ w32_is_daemon = 1;
+#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 +2321,45 @@ If the daemon was given a name argument, return that name. */)
return Qnil;
}
+static void
+daemon_check_preconditions (void)
+{
+ if (!IS_DAEMON)
+ error ("This function can only be called if emacs is run as a daemon");
+
+#ifdef WINDOWSNT
+ if (w32_daemon_is_initialized)
+#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)
{
+#ifdef WINDOWSNT
+ daemon_check_preconditions ();
+
+ w32_daemon_event = CreateEvent (NULL, TRUE, FALSE, W32_EMACS_SERVER_GUID);
+ if (w32_daemon_event == NULL)
+ error ("Couldn't create event for Windows daemon");
+ if (GetLastError () == ERROR_ALREADY_EXISTS)
+ /* Signal the waiting emacsclient process. */
+ SetEvent (w32_daemon_event);
+ CloseHandle (w32_daemon_event);
+ w32_daemon_is_initialized = true;
+#else
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)
- error ("The daemon has already been initialized");
-
- if (NILP (Vafter_init_time))
- error ("This function can only be called after loading the init files");
+ daemon_check_preconditions ();
/* Get rid of stdin, stdout and stderr. */
nfd = emacs_open ("/dev/null", O_RDWR, 0);
@@ -2350,6 +2383,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..fac9615 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_is_initialized))
+#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..f9e5dc4 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4215,9 +4215,15 @@ 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 bool w32_is_daemon;
+extern bool w32_daemon_is_initialized;
+#define IS_DAEMON (w32_is_daemon != 0)
+#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..3dceb27 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_is_initialized))
+#else
|| (IS_DAEMON && (daemon_pipe[1] >= 0)))
+#endif
&& NILP (Vexecuting_kbd_macro))
{
val = read_minibuf_noninteractive (map, initial, prompt,
diff --git a/src/server-guid.h b/src/server-guid.h
new file mode 100644
index 0000000..a58ebf4
--- /dev/null
+++ b/src/server-guid.h
@@ -0,0 +1,25 @@
+/* Event GUID for when emacsclient starts the Emacs daemon on Windows.
+
+Copyright (C) 1986-1987, 1994, 1999-2014 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef SERVER_GUID_H
+#define SERVER_GUID_H
+
+#define W32_EMACS_SERVER_GUID "{0B8E5DCB-D7CF-4423-A9F1-2F6927F0D318}"
+
+#endif
next reply other threads:[~2015-01-25 19:18 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-01-25 19:18 Mark Laws [this message]
2015-01-25 20:34 ` bug#19688: [patch] add support for emacs daemon on Windows 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
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
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CADemMPPXYzgmvBLzAafpZiVUSvr1RULUAtaREZOP06m7s3ik9Q@mail.gmail.com \
--to=mdl@60hz.org \
--cc=19688@debbugs.gnu.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 external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.