]> code.delx.au - gnu-emacs/commitdiff
Merge from trunk.
authorEli Zaretskii <eliz@gnu.org>
Tue, 13 Nov 2012 14:17:18 +0000 (16:17 +0200)
committerEli Zaretskii <eliz@gnu.org>
Tue, 13 Nov 2012 14:17:18 +0000 (16:17 +0200)
19 files changed:
lib-src/ChangeLog
lib-src/makefile.w32-in
lisp/ChangeLog
lisp/subr.el
src/ChangeLog
src/Makefile.in
src/emacs.c
src/keyboard.c
src/lisp.h
src/makefile.w32-in
src/termhooks.h
src/w32console.c
src/w32fns.c
src/w32inevt.c
src/w32notify.c [new file with mode: 0644]
src/w32proc.c
src/w32term.c
src/w32term.h
src/w32xfns.c

index 03cddc6a0356cc31eba3d5a29838f366c8eb3d79..08c9a4176acbcec19ac753af99c50feed156a17d 100644 (file)
        (main): Retry connection with alt_display if connection with main
        display fails.
 
+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 f3ab4421fd3434982fdbac336ce7a4b9d07d4203..9fc3a507e944b30bfc90c5ca434bd942a7e38c1b 100644 (file)
@@ -126,7 +126,7 @@ obj =   dosfns.o msdos.o \
        fontset.o menu.o \
        w32.o w32console.o w32fns.o w32heap.o w32inevt.o cygw32.o \
        w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
-       font.o w32font.o w32uniscribe.o \
+       font.o w32font.o w32uniscribe.o w32notify.o \
        dispnew.o frame.o scroll.o xdisp.o window.o bidi.o \
        charset.o coding.o category.o ccl.o character.o chartab.o \
        cm.o term.o terminal.o xfaces.o \
index fc69b8643b63c1f446660d673947fab87d7e435a..5ef50e0548f88306a36a53339ceb71d1255d9ef2 100644 (file)
        (rst-indent-comment): Correct :version tag.
        (rst-official-cvs-rev): Correct version string.
 
+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 b0ac2dd2106309cbe71c3f9244c4c15a34ec48e6..48d208235dd7c0341284c69ffa2f0ed8e2f3cb3d 100644 (file)
@@ -1159,6 +1159,17 @@ The return value has the form (WIDTH . HEIGHT).  POSITION should
 be a list of the form returned by `event-start' and `event-end'."
   (nth 9 position))
 
+(defun w32notify-handle-event (event)
+  "Handle file system monitoring event.
+If EVENT is a file-notification event, then its callback is called.
+Otherwise, a `filewatch-error' is signaled."
+  (interactive "e")
+
+  (if (and (eq (car event) 'file-notify)
+          (= (length event) 3))
+      (funcall (nth 2 event) (nth 1 event))
+    (signal 'filewatch-error (cons "Not a valid file-notify event" event))))
+
 \f
 ;;;; Obsolescent names for functions.
 
index 1d94cef65779cdcd23185c7e865ab869ab2fa5a0..e3be3f9149d02906bf5711a3ebf9b3fe915ec559 100644 (file)
        (x_set_icon_name, ns_set_name, ns_set_name_as_filename):
        Remove ns_in_resize check.
 
+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  Paul Eggert  <eggert@cs.ucla.edu>
 
        Improve sys_siglist detection.
index c24e421bbbc6528890ddf5e515894f4ace3b93f1..9d8e9698ec8c233aec77fc0447a2dad197653d3d 100644 (file)
@@ -357,7 +357,7 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
   xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \
   fontset.o dbusbind.o cygw32.o \
   nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o \
-  w32.o w32console.o w32fns.o w32heap.o w32inevt.o \
+  w32.o w32console.o w32fns.o w32heap.o w32inevt.o w32notify.o \
   w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
   w16select.o widget.o xfont.o ftfont.o xftfont.o ftxfont.o gtkutil.o \
   xsettings.o xgselect.o termcap.o
index fee9c332c55a2d46eb8bda3f09baefc4f031ffb5..1afc7c189df9bfeac14d7e52d1374d2db821ed49 100644 (file)
@@ -1292,6 +1292,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
 
 #ifdef WINDOWSNT
   globals_of_w32 ();
+  globals_of_w32notify ();
   /* Initialize environment from registry settings.  */
   init_environment (argv);
   init_ntproc (dumping); /* must precede init_editfns.  */
@@ -1453,6 +1454,7 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
 
 #ifdef WINDOWSNT
       syms_of_ntterm ();
+      syms_of_w32notify ();
 #endif /* WINDOWSNT */
 
       syms_of_profiler ();
index 8f3a206139d25b2184116092524d6c3a47e64b4c..98c1cc0b78b21549be620688dddec8c6a82d2d1c 100644 (file)
@@ -315,6 +315,9 @@ static Lisp_Object Qfunction_key;
 Lisp_Object Qmouse_click;
 #ifdef HAVE_NTGUI
 Lisp_Object Qlanguage_change;
+#ifdef WINDOWSNT
+Lisp_Object Qfile_notify;
+#endif
 #endif
 static Lisp_Object Qdrag_n_drop;
 static Lisp_Object Qsave_session;
@@ -3922,6 +3925,16 @@ kbd_buffer_get_event (KBOARD **kbp,
                              make_number (event->modifiers)));
          kbd_fetch_ptr = event + 1;
        }
+      else if (event->kind == FILE_NOTIFY_EVENT)
+       {
+         /* Make an event (file-notify (DESCRIPTOR ACTION FILE) CALLBACK).  */
+         obj = Fcons (Qfile_notify,
+                      list2 (list3 (make_number (event->code),
+                                    XCAR (event->arg),
+                                    XCDR (event->arg)),
+                             event->frame_or_window));
+         kbd_fetch_ptr = event + 1;
+       }
 #endif
       else if (event->kind == SAVE_SESSION_EVENT)
         {
@@ -11363,6 +11376,7 @@ syms_of_keyboard (void)
 
 #ifdef HAVE_NTGUI
   DEFSYM (Qlanguage_change, "language-change");
+  DEFSYM (Qfile_notify, "file-notify");
 #endif
 
 #ifdef HAVE_DBUS
@@ -12134,6 +12148,8 @@ keys_of_keyboard (void)
 #if defined (WINDOWSNT)
   initial_define_lispy_key (Vspecial_event_map, "language-change",
                            "ignore");
+  initial_define_lispy_key (Vspecial_event_map, "file-notify",
+                           "w32notify-handle-event");
 #endif
 }
 
index 72e38fa46533f069adb4aaba906e85589729dadc..c909287b7cdb124691028798f00991cd37c49556 100644 (file)
@@ -3517,6 +3517,11 @@ extern void syms_of_fontset (void);
 extern Lisp_Object Qfont_param;
 #endif
 
+#ifdef WINDOWSNT
+/* Defined on w32notify.c.  */
+extern void syms_of_w32notify (void);
+#endif
+
 /* Defined in xfaces.c.  */
 extern Lisp_Object Qdefault, Qtool_bar, Qfringe;
 extern Lisp_Object Qheader_line, Qscroll_bar, Qcursor;
index f5cab34d7dcaaab7b0e72045b007314705cb1c13..50e5e2ec4df16fa29bc9f7158184ecfbe0f39a3f 100644 (file)
@@ -134,6 +134,7 @@ OBJ2 =  $(BLD)/sysdep.$(O)          \
        $(BLD)/w32menu.$(O)             \
        $(BLD)/w32reg.$(O)              \
        $(BLD)/w32font.$(O)             \
+       $(BLD)/w32notify.$(O)           \
        $(BLD)/w32uniscribe.$(O)
 
 LIBS =  $(TLIB0)       \
@@ -209,7 +210,7 @@ GLOBAL_SOURCES =   dosfns.c msdos.c \
        fontset.c menu.c dbusbind.c \
        w32.c w32console.c w32fns.c w32heap.c w32inevt.c cygw32.c \
        w32menu.c w32proc.c w32reg.c w32select.c w32term.c w32xfns.c \
-       font.c w32font.c w32uniscribe.c \
+       font.c w32font.c w32uniscribe.c w32notify.c \
        dispnew.c frame.c scroll.c xdisp.c window.c bidi.c \
        charset.c coding.c category.c ccl.c character.c chartab.c \
        cm.c term.c terminal.c xfaces.c \
@@ -1691,6 +1692,17 @@ $(BLD)/w32uniscribe.$(O) : \
        $(W32FONT_H) \
        $(W32TERM_H)
 
+$(BLD)/w32notify.$(O) : \
+       $(SRC)/w32notify.c \
+       $(SRC)/w32common.h \
+       $(CODING_H) \
+       $(CONFIG_H) \
+       $(FRAME_H) \
+       $(KEYBOARD_H) \
+       $(LISP_H) \
+       $(TERMHOOKS_H) \
+       $(W32TERM_H)
+
 # Each object file depends on stamp_BLD, because in parallel builds we must
 # make sure $(BLD) exists before starting compilations.
 #
index b35c927fc539ed13a5a24aec6b1a22882efb6985..d1ee7138d6741c54f4829e3cb7eefd451ea3d5ee 100644 (file)
@@ -201,6 +201,7 @@ enum event_kind
      On X, the window manager seems to grab the keys it wants
      first, so this is not a problem there.  */
   , MULTIMEDIA_KEY_EVENT
+  , FILE_NOTIFY_EVENT
 #endif
 
 #ifdef HAVE_NS
index f0574689bf17feaf4e321efe7ab22702fcfc3aab..f201ff190c2a4bb324ad0fc9579cff7f7f7ccd1d 100644 (file)
@@ -746,6 +746,9 @@ initialize_w32_display (struct terminal *term)
   else
     w32_console_unicode_input = 0;
 
+  /* This is needed by w32notify.c:send_notifications.  */
+  dwMainThreadId = GetCurrentThreadId ();
+
   /* Setup w32_display_info structure for this frame. */
 
   w32_initialize_display_info (build_string ("Console"));
index ed5625e802ca6b56034d1703efbd49db9eb62272..6458054013fc1115e9a04360004ad28c2d3b69e7 100644 (file)
@@ -3958,6 +3958,9 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 
        return retval;
       }
+    case WM_EMACS_FILENOTIFY:
+      my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+      return 1;
 
     default:
       /* Check for messages registered at runtime. */
index 899a6fb89bfff5b469c75b0f20160ed465456099..2cbf31efda3c481d38cc95abf95c6d9fef4526f6 100644 (file)
@@ -576,6 +576,74 @@ maybe_generate_resize_event (void)
                     0, 0, 0);
 }
 
+static int
+handle_file_notifications (struct input_event *hold_quit)
+{
+  BYTE *p = file_notifications;
+  FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
+  const DWORD min_size
+    = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
+  struct input_event inev;
+  int nevents = 0;
+
+  /* We cannot process notification before Emacs is fully initialized,
+     since we need the UTF-16LE coding-system to be set up.  */
+  if (!initialized)
+    {
+      notification_buffer_in_use = 0;
+      return nevents;
+    }
+
+  enter_crit ();
+  if (notification_buffer_in_use)
+    {
+      DWORD info_size = notifications_size;
+      Lisp_Object cs = intern ("utf-16le");
+      Lisp_Object obj = w32_get_watch_object (notifications_desc);
+
+      /* notifications_size could be zero when the buffer of
+        notifications overflowed on the OS level, or when the
+        directory being watched was itself deleted.  Do nothing in
+        that case.  */
+      if (info_size
+         && !NILP (obj) && CONSP (obj))
+       {
+         Lisp_Object callback = XCDR (obj);
+
+         EVENT_INIT (inev);
+
+         while (info_size >= min_size)
+           {
+             Lisp_Object utf_16_fn
+               = make_unibyte_string ((char *)fni->FileName,
+                                      fni->FileNameLength);
+             /* Note: mule-conf is preloaded, so utf-16le must
+                already be defined at this point.  */
+             Lisp_Object fname
+               = code_convert_string_norecord (utf_16_fn, cs, 0);
+             Lisp_Object action = lispy_file_action (fni->Action);
+
+             inev.kind = FILE_NOTIFY_EVENT;
+             inev.code = (ptrdiff_t)XINT (XIL ((EMACS_INT)notifications_desc));
+             inev.timestamp = GetTickCount ();
+             inev.modifiers = 0;
+             inev.frame_or_window = callback;
+             inev.arg = Fcons (action, fname);
+             kbd_buffer_store_event_hold (&inev, hold_quit);
+
+             if (!fni->NextEntryOffset)
+               break;
+             p += fni->NextEntryOffset;
+             fni = (PFILE_NOTIFY_INFORMATION)p;
+             info_size -= fni->NextEntryOffset;
+           }
+       }
+      notification_buffer_in_use = 0;
+    }
+  leave_crit ();
+  return nevents;
+}
+
 /* Here's an overview of how Emacs input works in non-GUI sessions on
    MS-Windows.  (For description of the GUI input, see the commentary
    before w32_msg_pump in w32fns.c.)
@@ -619,12 +687,16 @@ w32_console_read_socket (struct terminal *terminal,
 
   for (;;)
     {
+      int nfnotify = handle_file_notifications (hold_quit);
+
       nev = fill_queue (0);
       if (nev <= 0)
         {
          /* If nev == -1, there was some kind of error
-            If nev == 0 then waitp must be zero and no events were available
+            If nev == 0 then no events were available
             so return.  */
+         if (nfnotify)
+           nev = 0;
          break;
         }
 
diff --git a/src/w32notify.c b/src/w32notify.c
new file mode 100644 (file)
index 0000000..afa0349
--- /dev/null
@@ -0,0 +1,626 @@
+/* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
+   Copyright (C) 2012  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/>.  */
+
+/* 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.)
+
+   In a GUI session, 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).
+
+   In a non-GUI session, we send the WM_EMACS_FILENOTIFY message to
+   the main (a.k.a. "Lisp") thread instead, since there are no window
+   procedures in console programs.  That message wakes up
+   MsgWaitForMultipleObjects inside sys_select, which then signals to
+   its caller that some keyboard input is available.  This causes
+   w32_console_read_socket to be called, which accesses the buffer
+   with file notifications and stuffs them into the input event queue
+   for keyboard.c to process.
+
+   When the FILE_NOTIFY_EVENT event is processed by keyboard.c's
+   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 or w32_console_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 some time
+   for the worker thread to exit, and if it doesn't, terminates it
+   forcibly.  */
+
+#include <stddef.h>
+#include <errno.h>
+
+/* must include CRT headers *before* config.h */
+#include <config.h>
+
+#include <windows.h>
+
+#include "lisp.h"
+#include "w32term.h"   /* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
+#include "w32common.h" /* for OS version data */
+#include "w32.h"       /* for w32_strerror */
+#include "coding.h"
+#include "keyboard.h"
+#include "frame.h"     /* needed by termhooks.h */
+#include "termhooks.h" /* for FILE_NOTIFY_EVENT */
+
+#define DIRWATCH_SIGNATURE 0x01233210
+
+struct notification {
+  BYTE *buf;           /* buffer for ReadDirectoryChangesW */
+  OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */
+  BOOL subtree;                /* whether to watch subdirectories */
+  DWORD filter;                /* bit mask for events to watch */
+  char *watchee;       /* the file we are interested in */
+  HANDLE dir;          /* handle to the watched directory */
+  HANDLE thr;          /* handle to the thread that watches */
+  volatile int terminate; /* if non-zero, request for the thread to terminate */
+  unsigned signature;
+};
+
+/* Used for communicating notifications to the main thread.  */
+volatile int notification_buffer_in_use;
+BYTE file_notifications[16384];
+DWORD notifications_size;
+void *notifications_desc;
+
+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;
+
+/* Signal to the main thread that we have file notifications for it to
+   process.  */
+static void
+send_notifications (BYTE *info, DWORD info_size, void *desc,
+                   volatile int *terminate)
+{
+  int done = 0;
+  FRAME_PTR f = SELECTED_FRAME ();
+
+  /* A single buffer is used to communicate all notifications to the
+     main thread.  Since both the main thread and several watcher
+     threads could be active at the same time, we use a critical area
+     and an "in-use" flag to synchronize them.  A watcher thread can
+     only put its notifications in the buffer if it acquires the
+     critical area and finds the "in-use" flag reset.  The main thread
+     resets the flag after it is done processing notifications.
+
+     FIXME: is there a better way of dealing with this?  */
+  while (!done && !*terminate)
+    {
+      enter_crit ();
+      if (!notification_buffer_in_use)
+       {
+         if (info_size)
+           memcpy (file_notifications, info, info_size);
+         notifications_size = info_size;
+         notifications_desc = desc;
+         /* If PostMessage fails, the message queue is full.  If that
+            happens, the last thing they will worry about is file
+            notifications.  So we effectively discard the
+            notification in that case.  */
+         if ((FRAME_TERMCAP_P (f)
+              /* We send the message to the main (a.k.a. "Lisp")
+                 thread, where it will wake up MsgWaitForMultipleObjects
+                 inside sys_select, causing it to report that there's
+                 some keyboard input available.  This will in turn cause
+                 w32_console_read_socket to be called, which will pick
+                 up the file notifications.  */
+              && PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0))
+             || (FRAME_W32_P (f)
+                 && PostMessage (FRAME_W32_WINDOW (f),
+                                 WM_EMACS_FILENOTIFY, 0, 0)))
+           notification_buffer_in_use = 1;
+         done = 1;
+       }
+      leave_crit ();
+      if (!done)
+       Sleep (5);
+    }
+}
+
+/* An APC routine to cancel outstanding directory watch.  Invoked by
+   the main thread via QueueUserAPC.  This is needed because only the
+   thread that issued the ReadDirectoryChangesW call can call CancelIo
+   to cancel that.  (CancelIoEx is only available since Vista, so we
+   cannot use it on XP.)  */
+VOID CALLBACK
+watch_end (ULONG_PTR arg)
+{
+  HANDLE hdir = (HANDLE)arg;
+
+  if (hdir && hdir != INVALID_HANDLE_VALUE)
+    {
+      CancelIo (hdir);
+      CloseHandle (hdir);
+    }
+}
+
+/* A completion routine (a.k.a. APC function) for handling events read
+   by ReadDirectoryChangesW.  Called by the OS when the thread which
+   issued the asynchronous ReadDirectoryChangesW call is in the
+   "alertable state", i.e. waiting inside SleepEx call.  */
+VOID CALLBACK
+watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
+{
+  struct notification *dirwatch;
+
+  /* Who knows what happened?  Perhaps the OVERLAPPED structure was
+     freed by someone already?  In any case, we cannot do anything
+     with this request, so just punt and skip it.  FIXME: should we
+     raise the 'terminate' flag in this case?  */
+  if (!io_info)
+    return;
+
+  /* We have a pointer to our dirwatch structure conveniently stashed
+     away in the hEvent member of the OVERLAPPED struct.  According to
+     MSDN documentation of ReadDirectoryChangesW: "The hEvent member
+     of the OVERLAPPED structure is not used by the system, so you can
+     use it yourself."  */
+  dirwatch = (struct notification *)io_info->hEvent;
+  if (status == ERROR_OPERATION_ABORTED)
+    {
+      /* We've been called because the main thread told us to issue
+        CancelIo on the directory we watch, and watch_end did so.
+        The directory handle is already closed.  We should clean up
+        and exit, signalling to the thread worker routine not to
+        issue another call to ReadDirectoryChangesW.  Note that we
+        don't free the dirwatch object itself nor the memory consumed
+        by its buffers; this is done by the main thread in
+        remove_watch.  Calling malloc/free from a thread other than
+        the main thread is a no-no.  */
+      dirwatch->dir = NULL;
+      dirwatch->terminate = 1;
+    }
+  else
+    {
+      /* Tell the main thread we have notifications for it.  */
+      send_notifications (dirwatch->buf, bytes_ret, dirwatch,
+                         &dirwatch->terminate);
+    }
+}
+
+/* Worker routine for the watch thread.  */
+static DWORD WINAPI
+watch_worker (LPVOID arg)
+{
+  struct notification *dirwatch = (struct notification *)arg;
+
+  do {
+    BOOL status;
+    DWORD sleep_result;
+    DWORD bytes_ret = 0;
+
+    if (dirwatch->dir)
+      {
+       status = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, 16384,
+                                       dirwatch->subtree, dirwatch->filter,
+                                       &bytes_ret,
+                                       dirwatch->io_info, watch_completion);
+       if (!status)
+         {
+           DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
+           /* We cannot remove the dirwatch object from watch_list,
+              because we are in a separate thread.  For the same
+              reason, we also cannot free memory consumed by the
+              buffers allocated for the dirwatch object.  So we close
+              the directory handle, but do not free the object itself
+              or its buffers.  We also don't touch the signature.
+              This way, remove_watch can still identify the object,
+              remove it, and free its memory.  */
+           CloseHandle (dirwatch->dir);
+           dirwatch->dir = NULL;
+           return 1;
+         }
+      }
+    /* Sleep indefinitely until awoken by the I/O completion, which
+       could be either a change notification or a cancellation of the
+       watch.  */
+    sleep_result = SleepEx (INFINITE, TRUE);
+  } while (!dirwatch->terminate);
+
+  return 0;
+}
+
+/* Launch a thread to watch changes to FILE in a directory open on
+   handle HDIR.  */
+static struct notification *
+start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
+{
+  struct notification *dirwatch = xzalloc (sizeof (struct notification));
+  HANDLE thr;
+
+  dirwatch->signature = DIRWATCH_SIGNATURE;
+  dirwatch->buf = xmalloc (16384);
+  dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
+  /* Stash a pointer to dirwatch structure for use by the completion
+     routine.  According to MSDN documentation of ReadDirectoryChangesW:
+     "The hEvent member of the OVERLAPPED structure is not used by the
+     system, so you can use it yourself." */
+  dirwatch->io_info->hEvent = dirwatch;
+  dirwatch->subtree = subdirs;
+  dirwatch->filter = flags;
+  dirwatch->watchee = xstrdup (file);
+  dirwatch->terminate = 0;
+  dirwatch->dir = hdir;
+
+  /* See w32proc.c where it calls CreateThread for the story behind
+     the 2nd and 5th argument in the call to CreateThread.  */
+  dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
+                               0x00010000, NULL);
+
+  if (!dirwatch->thr)
+    {
+      xfree (dirwatch->buf);
+      xfree (dirwatch->io_info);
+      xfree (dirwatch->watchee);
+      xfree (dirwatch);
+      dirwatch = NULL;
+    }
+  return dirwatch;
+}
+
+/* Called from the main thread to start watching FILE in PARENT_DIR,
+   subject to FLAGS.  If SUBDIRS is TRUE, watch the subdirectories of
+   PARENT_DIR as well.  Value is a pointer to 'struct notification'
+   used by the thread that watches the changes.  */
+static struct notification *
+add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
+{
+  HANDLE hdir;
+  struct notification *dirwatch = NULL;
+
+  if (!file || !*file)
+    return NULL;
+
+  hdir = CreateFile (parent_dir,
+                    FILE_LIST_DIRECTORY,
+                    /* FILE_SHARE_DELETE doesn't preclude other
+                       processes from deleting files inside
+                       parent_dir.  */
+                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                    NULL, OPEN_EXISTING,
+                    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
+                    NULL);
+  if (hdir == INVALID_HANDLE_VALUE)
+    return NULL;
+
+  if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
+    CloseHandle (hdir);
+
+  return dirwatch;
+}
+
+/* Stop watching a directory specified by a pointer to its dirwatch object.  */
+static int
+remove_watch (struct notification *dirwatch)
+{
+  if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
+    {
+      int i;
+      BOOL status;
+      DWORD exit_code, err;
+
+      /* Only the thread that issued the outstanding I/O call can call
+        CancelIo on it.  (CancelIoEx is available only since Vista.)
+        So we need to queue an APC for the worker thread telling it
+        to terminate.  */
+      if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
+       DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
+      /* We also set the terminate flag, for when the thread is
+        waiting on the critical section that never gets acquired.
+        FIXME: is there a cleaner method?  Using SleepEx there is a
+        no-no, as that will lead to recursive APC invocations and
+        stack overflow.  */
+      dirwatch->terminate = 1;
+      /* Wait for the thread to exit.  FIXME: is there a better method
+        that is not overly complex?  */
+      for (i = 0; i < 50; i++)
+       {
+         if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
+               && exit_code == STILL_ACTIVE))
+           break;
+         Sleep (10);
+       }
+      if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
+         || exit_code == STILL_ACTIVE)
+       {
+         if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
+           {
+             TerminateThread (dirwatch->thr, 0);
+             if (dirwatch->dir)
+               CloseHandle (dirwatch->dir);
+           }
+       }
+
+      /* Clean up.  */
+      if (dirwatch->thr)
+       {
+         CloseHandle (dirwatch->thr);
+         dirwatch->thr = NULL;
+       }
+      xfree (dirwatch->buf);
+      xfree (dirwatch->io_info);
+      xfree (dirwatch->watchee);
+      xfree (dirwatch);
+
+      return 0;
+    }
+  else
+    {
+      DebPrint (("Unknown dirwatch object!\n"));
+      return -1;
+    }
+}
+
+static DWORD
+filter_list_to_flags (Lisp_Object filter_list)
+{
+  DWORD flags = 0;
+
+  if (NILP (filter_list))
+    return flags;
+
+  if (!NILP (Fmember (Qfile_name, filter_list)))
+    flags |= FILE_NOTIFY_CHANGE_FILE_NAME;
+  if (!NILP (Fmember (Qdirectory_name, filter_list)))
+    flags |= FILE_NOTIFY_CHANGE_DIR_NAME;
+  if (!NILP (Fmember (Qattributes, filter_list)))
+    flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+  if (!NILP (Fmember (Qsize, filter_list)))
+    flags |= FILE_NOTIFY_CHANGE_SIZE;
+  if (!NILP (Fmember (Qlast_write_time, filter_list)))
+    flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+  if (!NILP (Fmember (Qlast_access_time, filter_list)))
+    flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+  if (!NILP (Fmember (Qcreation_time, filter_list)))
+    flags |= FILE_NOTIFY_CHANGE_CREATION;
+  if (!NILP (Fmember (Qsecurity_desc, filter_list)))
+    flags |= FILE_NOTIFY_CHANGE_SECURITY;
+
+  return flags;
+}
+
+DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
+       Sw32notify_add_watch, 3, 3, 0,
+       doc: /* Add a watch for filesystem events pertaining to FILE.
+
+This arranges for filesystem events pertaining to FILE to be reported
+to Emacs.  Use `w32notify-rm-watch' to cancel the watch.
+
+Value is a descriptor for the added watch, or nil if the file
+cannot be watched.
+
+FILTER is a list of conditions for reporting an event.  It can include
+the following symbols:
+
+  'file-name'          -- report file creation, deletion, or renaming
+  'directory-name'     -- report directory creation, deletion, or renaming
+  'attributes'         -- report changes in attributes
+  'size'               -- report changes in file-size
+  'last-write-time'    -- report changes in last-write time
+  'last-access-time'   -- report changes in last-access time
+  'creation-time'      -- report changes in creation time
+  'security-desc'      -- report changes in security descriptor
+
+If FILE is a directory, and FILTER includes 'subtree', then all the
+subdirectories will also be watched and changes in them reported.
+
+When any event happens that satisfies the conditions specified by
+FILTER, Emacs will call the CALLBACK function passing it a single
+argument EVENT, which is of the form
+
+  (DESCRIPTOR ACTION FILE)
+
+DESCRIPTOR is the same object as the one returned by this function.
+ACTION is the description of the event.  It could be any one of the
+following:
+
+  'added'        -- FILE was added
+  'removed'      -- FILE was deleted
+  'modified'     -- FILE's contents or its attributes were modified
+  'renamed-from' -- a file was renamed whose old name was FILE
+  'renamed-to'   -- a file was renamed and its new name is FILE
+
+FILE is the name of the file whose event is being reported.  */)
+  (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
+{
+  Lisp_Object encoded_file, watch_object, watch_descriptor;
+  char parent_dir[MAX_PATH], *basename;
+  size_t fn_len;
+  DWORD flags;
+  BOOL subdirs = FALSE;
+  struct notification *dirwatch = NULL;
+  Lisp_Object lisp_errstr;
+  char *errstr;
+
+  CHECK_LIST (filter);
+
+  /* The underlying features are available only since XP.  */
+  if (os_subtype == OS_9X
+      || (w32_major_version == 5 && w32_major_version < 1))
+    {
+      errno = ENOSYS;
+      report_file_error ("Watching filesystem events is not supported",
+                        Qnil);
+    }
+
+  /* We need a full absolute file name of FILE, and we need to remove
+     any trailing slashes from it, so that GetFullPathName below gets
+     the basename part correctly.  */
+  file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
+  encoded_file = ENCODE_FILE (file);
+
+  fn_len = GetFullPathName (SDATA (encoded_file), MAX_PATH, parent_dir,
+                           &basename);
+  if (!fn_len)
+    {
+      errstr = w32_strerror (0);
+      errno = EINVAL;
+      if (!NILP (Vlocale_coding_system))
+       lisp_errstr
+         = code_convert_string_norecord (build_unibyte_string (errstr),
+                                         Vlocale_coding_system, 0);
+      else
+       lisp_errstr = build_string (errstr);
+      report_file_error ("GetFullPathName failed",
+                        Fcons (lisp_errstr, Fcons (file, Qnil)));
+    }
+  /* We need the parent directory without the slash that follows it.
+     If BASENAME is NULL, the argument was the root directory on its
+     drive.  */
+  if (basename)
+    basename[-1] = '\0';
+  else
+    subdirs = TRUE;
+
+  if (!NILP (Fmember (Qsubtree, filter)))
+    subdirs = TRUE;
+
+  flags = filter_list_to_flags (filter);
+
+  dirwatch = add_watch (parent_dir, basename, subdirs, flags);
+  if (!dirwatch)
+    {
+      DWORD err = GetLastError ();
+
+      errno = EINVAL;
+      if (err)
+       {
+         errstr = w32_strerror (err);
+         if (!NILP (Vlocale_coding_system))
+           lisp_errstr
+             = code_convert_string_norecord (build_unibyte_string (errstr),
+                                             Vlocale_coding_system, 0);
+         else
+           lisp_errstr = build_string (errstr);
+         report_file_error ("Cannot watch file",
+                            Fcons (lisp_errstr, Fcons (file, Qnil)));
+       }
+      else
+       report_file_error ("Cannot watch file", Fcons (file, Qnil));
+    }
+  /* Store watch object in watch list. */
+  watch_descriptor = XIL ((EMACS_INT)dirwatch);
+  watch_object = Fcons (watch_descriptor, callback);
+  watch_list = Fcons (watch_object, watch_list);
+
+  return watch_descriptor;
+}
+
+DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch,
+       Sw32notify_rm_watch, 1, 1, 0,
+       doc: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
+
+WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.  */)
+     (Lisp_Object watch_descriptor)
+{
+  Lisp_Object watch_object;
+  struct notification *dirwatch;
+  int status = -1;
+
+  /* Remove the watch object from watch list.  Do this before freeing
+     the object, do that even if we fail to free it, watch_list is
+     kept free of junk.  */
+  watch_object = Fassoc (watch_descriptor, watch_list);
+  if (!NILP (watch_object))
+    {
+      watch_list = Fdelete (watch_object, watch_list);
+      dirwatch = (struct notification *)XLI (watch_descriptor);
+      if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
+       status = remove_watch (dirwatch);
+    }
+
+  if (status == -1)
+    report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor,
+                                                         Qnil));
+
+  return Qnil;
+}
+
+Lisp_Object
+w32_get_watch_object (void *desc)
+{
+  Lisp_Object descriptor = XIL ((EMACS_INT)desc);
+
+  /* This is called from the input queue handling code, inside a
+     critical section, so we cannot possibly QUIT if watch_list is not
+     in the right condition.  */
+  return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
+}
+
+void
+globals_of_w32notify (void)
+{
+  watch_list = Qnil;
+}
+
+void
+syms_of_w32notify (void)
+{
+  DEFSYM (Qfile_name, "file-name");
+  DEFSYM (Qdirectory_name, "directory-name");
+  DEFSYM (Qattributes, "attributes");
+  DEFSYM (Qsize, "size");
+  DEFSYM (Qlast_write_time, "last-write-time");
+  DEFSYM (Qlast_access_time, "last-access-time");
+  DEFSYM (Qcreation_time, "creation-time");
+  DEFSYM (Qsecurity_desc, "security-desc");
+  DEFSYM (Qsubtree, "subtree");
+
+  defsubr (&Sw32notify_add_watch);
+  defsubr (&Sw32notify_rm_watch);
+
+  staticpro (&watch_list);
+
+  Fprovide (intern_c_string ("w32notify"), Qnil);
+}
index f35a2da537cbc5b2330829b6ad1466cd3378c384..e074ece020d26fc4fc722bf93a82a3478a19a061 100644 (file)
@@ -1967,7 +1967,24 @@ count_children:
             (*) Note that MsgWaitForMultipleObjects above is an
             internal dispatch point for messages that are sent to
             windows created by this thread.  */
-         drain_message_queue ();
+         if (drain_message_queue ()
+             /* If drain_message_queue returns non-zero, that means
+                we received a WM_EMACS_FILENOTIFY message.  If this
+                is a TTY frame, we must signal the caller that keyboard
+                input is available, so that w32_console_read_socket
+                will be called to pick up the notifications.  If we
+                don't do that, file notifications will only work when
+                the Emacs TTY frame has focus.  */
+             && FRAME_TERMCAP_P (SELECTED_FRAME ())
+             /* they asked for stdin reads */
+             && FD_ISSET (0, &orfds)
+             /* the stdin handle is valid */
+             && keyboard_handle)
+           {
+             FD_SET (0, rfds);
+             if (nr == 0)
+               nr = 1;
+           }
        }
       else if (active >= nh)
        {
index 032912c27f4629114d1683010749191b15a39115..e53fa5e7756d3cb60c90344bf6f070a0cffe3e7b 100644 (file)
@@ -246,6 +246,8 @@ static void x_check_font (struct frame *, struct font *);
 #endif
 
 static Lisp_Object Qvendor_specific_keysyms;
+static Lisp_Object Qadded, Qremoved, Qmodified;
+static Lisp_Object Qrenamed_from, Qrenamed_to;
 
 \f
 /***********************************************************************
@@ -3213,6 +3215,122 @@ construct_drag_n_drop (struct input_event *result, W32Msg *msg, struct frame *f)
   return Qnil;
 }
 
+\f
+/* File event notifications (see w32notify.c).  */
+
+Lisp_Object
+lispy_file_action (DWORD action)
+{
+  static char unknown_fmt[] = "unknown-action(%d)";
+  Lisp_Object retval;
+
+  switch (action)
+    {
+    case FILE_ACTION_ADDED:
+      retval = Qadded;
+      break;
+    case FILE_ACTION_REMOVED:
+      retval = Qremoved;
+      break;
+    case FILE_ACTION_MODIFIED:
+      retval = Qmodified;
+      break;
+    case FILE_ACTION_RENAMED_OLD_NAME:
+      retval = Qrenamed_from;
+      break;
+    case FILE_ACTION_RENAMED_NEW_NAME:
+      retval = Qrenamed_to;
+      break;
+    default:
+      {
+       char buf[sizeof(unknown_fmt) - 1 + INT_STRLEN_BOUND (DWORD)];
+
+       sprintf (buf, unknown_fmt, action);
+       retval = intern (buf);
+      }
+      break;
+    }
+
+  return retval;
+}
+
+/* Put file notifications into the Emacs input event queue.  This
+   function runs when the WM_EMACS_FILENOTIFY message arrives from a
+   watcher thread.  */
+static void
+queue_notifications (struct input_event *event, W32Msg *msg, struct frame *f,
+                    int *evcount)
+{
+  BYTE *p = file_notifications;
+  FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
+  const DWORD min_size
+    = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
+  Lisp_Object frame;
+
+  /* We cannot process notification before Emacs is fully initialized,
+     since we need the UTF-16LE coding-system to be set up.  */
+  if (!initialized)
+    {
+      notification_buffer_in_use = 0;
+      return;
+    }
+
+  XSETFRAME (frame, f);
+
+  enter_crit ();
+  if (notification_buffer_in_use)
+    {
+      DWORD info_size = notifications_size;
+      Lisp_Object cs = intern ("utf-16le");
+      Lisp_Object obj = w32_get_watch_object (notifications_desc);
+
+      /* notifications_size could be zero when the buffer of
+        notifications overflowed on the OS level, or when the
+        directory being watched was itself deleted.  Do nothing in
+        that case.  */
+      if (info_size
+         && !NILP (obj) && CONSP (obj))
+       {
+         Lisp_Object callback = XCDR (obj);
+
+         while (info_size >= min_size)
+           {
+             Lisp_Object utf_16_fn
+               = make_unibyte_string ((char *)fni->FileName,
+                                      fni->FileNameLength);
+             /* Note: mule-conf is preloaded, so utf-16le must
+                already be defined at this point.  */
+             Lisp_Object fname
+               = code_convert_string_norecord (utf_16_fn, cs, 0);
+             Lisp_Object action = lispy_file_action (fni->Action);
+
+             event->kind = FILE_NOTIFY_EVENT;
+             event->code
+               = (ptrdiff_t)XINT (XIL ((EMACS_INT)notifications_desc));
+             event->timestamp = msg->msg.time;
+             event->modifiers = 0;
+             event->frame_or_window = callback;
+             event->arg = Fcons (action, fname);
+             kbd_buffer_store_event (event);
+             (*evcount)++;
+
+             if (!fni->NextEntryOffset)
+               break;
+             p += fni->NextEntryOffset;
+             fni = (PFILE_NOTIFY_INFORMATION)p;
+             info_size -= fni->NextEntryOffset;
+           }
+       }
+      notification_buffer_in_use = 0;
+    }
+  else
+    DebPrint (("We were promised notifications, but in-use flag is zero!\n"));
+  leave_crit ();
+
+  /* We've stuffed all the events ourselves, so w32_read_socket shouldn't.  */
+  event->kind = NO_EVENT;
+}
+
 \f
 /* Function to report a mouse movement to the mainstream Emacs code.
    The input handler calls this.
@@ -4846,6 +4964,12 @@ w32_read_socket (struct terminal *terminal,
          check_visibility = 1;
          break;
 
+       case WM_EMACS_FILENOTIFY:
+         f = x_window_to_frame (dpyinfo, msg.msg.hwnd);
+         if (f)
+           queue_notifications (&inev, &msg, f, &count);
+         break;
+
        default:
          /* Check for messages registered at runtime.  */
          if (msg.msg.message == msh_mousewheel)
@@ -6515,6 +6639,12 @@ syms_of_w32term (void)
 
   DEFSYM (Qvendor_specific_keysyms, "vendor-specific-keysyms");
 
+  DEFSYM (Qadded, "added");
+  DEFSYM (Qremoved, "removed");
+  DEFSYM (Qmodified, "modified");
+  DEFSYM (Qrenamed_from, "renamed-from");
+  DEFSYM (Qrenamed_to, "renamed-to");
+
   DEFVAR_INT ("w32-num-mouse-buttons",
              w32_num_mouse_buttons,
              doc: /* Number of physical mouse buttons.  */);
index 72fb8a76e35703af0344cab2abccc5b46f82aaa1..98186a7d363eff6dd53ac0488e40ce4841747d61 100644 (file)
@@ -614,7 +614,8 @@ do { \
 #define WM_EMACS_PAINT                 (WM_EMACS_START + 20)
 #define WM_EMACS_BRINGTOTOP            (WM_EMACS_START + 21)
 #define WM_EMACS_INPUT_READY           (WM_EMACS_START + 22)
-#define WM_EMACS_END                   (WM_EMACS_START + 23)
+#define WM_EMACS_FILENOTIFY            (WM_EMACS_START + 23)
+#define WM_EMACS_END                   (WM_EMACS_START + 24)
 
 #define WND_FONTWIDTH_INDEX    (0)
 #define WND_LINEHEIGHT_INDEX   (4)
@@ -664,7 +665,7 @@ extern void deselect_palette (struct frame * f, HDC hdc);
 extern HDC get_frame_dc (struct frame * f);
 extern int release_frame_dc (struct frame * f, HDC hDC);
 
-extern void drain_message_queue (void);
+extern int drain_message_queue (void);
 
 extern BOOL get_next_msg (W32Msg *, BOOL);
 extern BOOL post_msg (W32Msg *);
@@ -674,10 +675,17 @@ extern BOOL parse_button (int, int, int *, int *);
 
 extern void w32_sys_ring_bell (struct frame *f);
 extern void x_delete_display (struct w32_display_info *dpyinfo);
+
+extern volatile int notification_buffer_in_use;
+extern BYTE file_notifications[16384];
+extern DWORD notifications_size;
+extern void *notifications_desc;
+extern Lisp_Object w32_get_watch_object (void *);
+extern Lisp_Object lispy_file_action (DWORD);
+
 extern void w32_initialize_display_info (Lisp_Object);
 extern void initialize_w32_display (struct terminal *);
 
-
 /* Keypad command key support.  W32 doesn't have virtual keys defined
    for the function keys on the keypad (they are mapped to the standard
    function keys), so we define our own.  */
@@ -756,6 +764,7 @@ extern void syms_of_w32fns (void);
 
 extern void globals_of_w32menu (void);
 extern void globals_of_w32fns (void);
+extern void globals_of_w32notify (void);
 
 #ifdef CYGWIN
 extern int w32_message_fd;
index cb452571665e765a1b302d2bd58bf269348708d3..8820edda6c2608e23a53bd93eb2057ea5fccd7b7 100644 (file)
@@ -315,16 +315,22 @@ prepend_msg (W32Msg *lpmsg)
   return (TRUE);
 }
 
-/* Process all messages in the current thread's queue.  */
-void
+/* Process all messages in the current thread's queue.  Value is 1 if
+   one of these messages was WM_EMACS_FILENOTIFY, zero otherwise.  */
+int
 drain_message_queue (void)
 {
   MSG msg;
+  int retval = 0;
+
   while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
     {
+      if (msg.message == WM_EMACS_FILENOTIFY)
+       retval = 1;
       TranslateMessage (&msg);
       DispatchMessage (&msg);
     }
+  return retval;
 }
 
 /* x_sync is a no-op on W32.  */