unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#19688: [patch] add support for emacs daemon on Windows
@ 2015-01-25 19:18 Mark Laws
  2015-01-25 20:34 ` Eli Zaretskii
  0 siblings, 1 reply; 31+ messages in thread
From: Mark Laws @ 2015-01-25 19:18 UTC (permalink / raw)
  To: 19688

[-- 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

^ permalink raw reply related	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2015-02-27 14:26 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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
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

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).