Final version that supports only one watch at a time.
authorEli Zaretskii <eliz@gnu.org>
Sun, 7 Oct 2012 13:41:15 +0000 (15:41 +0200)
committerEli Zaretskii <eliz@gnu.org>
Sun, 7 Oct 2012 13:41:15 +0000 (15:41 +0200)
lib-src/ChangeLog
lisp/ChangeLog
src/ChangeLog
src/w32fns.c
src/w32notify.c
src/w32term.c

index 2a8ac9b..a9031d7 100644 (file)
@@ -1,3 +1,7 @@
+2012-10-07  Eli Zaretskii  <eliz@gnu.org>
+
+       * makefile.w32-in (obj): Add w32notify.o.
+
 2012-10-01  Fabrice Popineau  <fabrice.popineau@gmail.com>
 
        * make-docfile.c (write_globals): Special-case
index e54e822..7d40007 100644 (file)
@@ -1,3 +1,7 @@
+2012-10-07  Eli Zaretskii  <eliz@gnu.org>
+
+       * subr.el (w32notify-handle-event): New function.
+
 2012-10-07  Glenn Morris  <rgm@gnu.org>
 
        * mail/rmailmm.el (rmail-mime-process-multipart):
index 492b966..cb2aab2 100644 (file)
@@ -1,3 +1,37 @@
+2012-10-07  Eli Zaretskii  <eliz@gnu.org>
+
+       * w32term.h (WM_EMACS_FILENOTIFY): New custom message.
+       (WM_EMACS_END): Bump value by 1.
+
+       * w32term.c (lispy_file_action, queue_notifications): New functions.
+       (syms_of_w32term) <Qadded, Qremoved, Qmodified, Qrenamed_from>
+       <Qrenamed_to>: New symbols.
+
+       * w32notify.c: New file, implement file event notifications for
+       MS-Windows.
+
+       * w32fns.c (w32_wnd_proc): Handle the WM_EMACS_FILENOTIFY message
+       by posting it to the w32_read_socket queue.
+
+       * termhooks.h (enum event_kind) [WINDOWSNT]: New event kind
+       FILE_NOTIFY_EVENT.
+
+       * makefile.w32-in (OBJ2): Add $(BLD)/w32notify.$(O).
+       (GLOBAL_SOURCES): Add w32notify.c
+       ($(BLD)/w32notify.$(O)): New set of dependencies.
+
+       * lisp.h (syms_of_w32notify) [WINDOWSNT]: Add prototype.
+
+       * keyboard.c (kbd_buffer_get_event) [WINDOWSNT]: Handle
+       FILE_NOTIFY_EVENT.
+       (syms_of_keyboard) [WINDOWSNT] <Qfile_notify>: New symbol.
+       (keys_of_keyboard) [WINDOWSNT]: Bind file-notify to
+       w32notify-handle-event by default.
+
+       * emacs.c (main) [WINDOWSNT]: Call syms_of_w32notify.
+
+       * alloc.c (NSTATICS): Enlarge to 0x660.
+
 2012-10-07  Jan Djärv  <jan.h.d@swipnet.se>
 
        * nsterm.m (ns_dumpglyphs_image): Only draw slize of image (Bug#12506).
index 2d36da3..fac6030 100644 (file)
@@ -2259,8 +2259,6 @@ w32_msg_pump (deferred_msg * msg_buf)
 
   while ((w32_unicode_gui ? GetMessageW : GetMessageA) (&msg, NULL, 0, 0))
     {
-      if (msg.message == WM_EMACS_FILENOTIFY)
-       DebPrint (("w32_msg_pump: File notification, hwnd = 0x%p\n", msg.hwnd));
       if (msg.hwnd == NULL)
        {
          switch (msg.message)
@@ -3810,7 +3808,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
        return retval;
       }
     case WM_EMACS_FILENOTIFY:
-      DebPrint (("w32_wnd_proc: File notification arrived, posting\n"));
       my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
       return 1;
 
index 0dc4d27..81199b2 100644 (file)
@@ -16,6 +16,54 @@ 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/>.  */
 
+/* Design overview:
+
+   For each watch request, we launch a separate worker thread.  The
+   worker thread runs the watch_worker function, which issues an
+   asynchronous call to ReadDirectoryChangesW, and then waits for that
+   call to complete in SleepEx.  Waiting in SleepEx puts the thread in
+   an alertable state, so it wakes up when either (a) the call to
+   ReadDirectoryChangesW completes, or (b) the main thread instructs
+   the worker thread to terminate by sending it an APC, see below.
+
+   When the ReadDirectoryChangesW call completes, its completion
+   routine watch_completion is automatically called.  watch_completion
+   stashes the received file events in a buffer used to communicate
+   them to the main thread (using a critical section, so that several
+   threads could use the same buffer), posts a special message,
+   WM_EMACS_FILENOTIFY, to the Emacs's message queue, and returns.
+   That causes the SleepEx function call inside watch_worker to
+   return, and watch_worker then issues another call to
+   ReadDirectoryChangesW.  (Except when it does not, see below.)
+
+   The WM_EMACS_FILENOTIFY message, posted to the message queue gets
+   dispatched to the main Emacs window procedure, which queues it for
+   processing by w32_read_socket.  When w32_read_socket sees this
+   message, it accesses the buffer with file notifications (using a
+   critical section), extracts the information, converts it to a
+   series of FILE_NOTIFY_EVENT events, and stuffs them into the input
+   event queue to be processed by keyboard.c input machinery
+   (read_char via a call to kbd_buffer_get_event).  When the
+   FILE_NOTIFY_EVENT event is processed by kbd_buffer_get_event, it is
+   converted to a Lispy event that can be bound to a command.  The
+   default binding is w32notify-handle-event, defined on subr.el.
+
+   After w32_read_socket is done processing the notifications, it
+   resets a flag signaling to all watch worker threads that the
+   notifications buffer is available for more input.
+
+   When the watch is removed by a call to w32notify-rm-watch, the main
+   thread requests that the worker thread terminates by queuing an APC
+   for the worker thread.  The APC specifies the watch_end function to
+   be called.  watch_end calls CancelIo on the outstanding
+   ReadDirectoryChangesW call and closes the handle on which the
+   watched directory was open.  When watch_end returns, the
+   watch_completion function is called one last time with the
+   ERROR_OPERATION_ABORTED status, which causes it to clean up and set
+   a flag telling watch_worker to exit without issuing another
+   ReadDirectoryChangesW call.  The main thread waits for the worker
+   thread to exit, and if it doesn't, terminate it forcibly.  */
+
 #include <stddef.h>
 #include <errno.h>
 
@@ -58,51 +106,6 @@ static Lisp_Object Qfile_name, Qdirectory_name, Qattributes, Qsize;
 static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time;
 static Lisp_Object Qsecurity_desc, Qsubtree, watch_list;
 
-#if 0
-/* FIXME: Debugging code, should be removed eventually.  */
-const wchar_t *
-format_file_action (DWORD action)
-{
-  static const wchar_t *action_str[] =
-    { L"???", L"Added", L"Removed", L"Modified", L"Renamed from", L"Renamed to" };
-
-  if (action >= sizeof(action_str)/sizeof(action_str[0]))
-    action = 0;
-  return action_str[action];
-}
-
-void
-parse_notifications (BYTE *info, DWORD info_size)
-{
-  BYTE *p = info;
-  FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
-  const DWORD min_size
-    = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
-
-  if (!info_size)
-    {
-      printf ("No info in notifications!\n");
-      return;
-    }
-
-  while (info_size >= min_size)
-    {
-      wchar_t *fn = alloca (fni->FileNameLength + sizeof(wchar_t));
-      const wchar_t *action_str;
-
-      memcpy (fn, fni->FileName, fni->FileNameLength);
-      fn[fni->FileNameLength/sizeof(wchar_t)] = 0;
-      action_str = format_file_action (fni->Action);
-      wprintf (L"%s: %s\n", action_str, fn);
-      if (!fni->NextEntryOffset)
-       break;
-      p += fni->NextEntryOffset;
-      fni = (PFILE_NOTIFY_INFORMATION)p;
-      info_size -= fni->NextEntryOffset;
-    }
-}
-#endif /* debugging code */
-
 /* Signal to the main thread that we have file notifications for it to
    process.  */
 static void
@@ -141,7 +144,6 @@ send_notifications (BYTE *info, DWORD info_size, HANDLE hdir, int *terminate)
          if (PostMessage (FRAME_W32_WINDOW (f), WM_EMACS_FILENOTIFY, 0, 0))
            notification_buffer_in_use = 1;
          done = 1;
-         DebPrint (("Announced notifications of %lu bytes\n", info_size));
        }
       leave_crit ();
       if (!done)
@@ -205,9 +207,6 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
     }
   else
     {
-#if 0  /* debugging code */
-      parse_notifications (dirwatch->buf, bytes_ret);
-#endif
       /* Tell the main thread we have notifications for it.  */
       send_notifications (dirwatch->buf, bytes_ret, dirwatch->dir,
                          &dirwatch->terminate);
@@ -233,7 +232,7 @@ watch_worker (LPVOID arg)
                                        dirwatch->io_info, watch_completion);
        if (!status)
          {
-           DebPrint (("watch_worker(1): %lu\n", GetLastError ()));
+           DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
            xfree (dirwatch->buf);
            dirwatch->buf = NULL;
            xfree (dirwatch->io_info);
@@ -249,11 +248,8 @@ watch_worker (LPVOID arg)
        could be either a change notification or a cancellation of the
        watch.  */
     sleep_result = SleepEx (INFINITE, TRUE);
-    if (dirwatch->terminate)
-      DebPrint (("watch_worker: exiting by request\n"));
   } while (!dirwatch->terminate);
 
-  DebPrint (("watch_worker(2): %lu\n", GetLastError ()));
   return 0;
 }
 
index 1c32383..71e6c72 100644 (file)
@@ -3204,6 +3204,9 @@ construct_drag_n_drop (struct input_event *result, W32Msg *msg, struct frame *f)
   return Qnil;
 }
 
+\f
+/* File event notifications (see w32notify.c).  */
+
 static Lisp_Object
 lispy_file_action (DWORD action)
 {
@@ -4945,7 +4948,6 @@ w32_read_socket (struct terminal *terminal,
          break;
 
        case WM_EMACS_FILENOTIFY:
-         DebPrint (("w32_read_socket: File notification arrived\n"));
          f = x_window_to_frame (dpyinfo, msg.msg.hwnd);
          if (f)
            queue_notifications (&inev, &msg, f, &count);