unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
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,

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