[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] /srv/bzr/emacs/trunk r111172: Support for filesystem notif
From: |
Eli Zaretskii |
Subject: |
[Emacs-diffs] /srv/bzr/emacs/trunk r111172: Support for filesystem notifications on MS-Windows. |
Date: |
Mon, 10 Dec 2012 16:12:19 +0200 |
User-agent: |
Bazaar (2.5.0) |
------------------------------------------------------------
revno: 111172 [merge]
committer: Eli Zaretskii <address@hidden>
branch nick: trunk
timestamp: Mon 2012-12-10 16:12:19 +0200
message:
Support for filesystem notifications on MS-Windows.
src/w32proc.c (sys_select): If drain_message_queue returns non-zero,
and this is a TTY frame, signal the caller that keyboard input is
available.
src/w32xfns.c (drain_message_queue): Now returns an int: an
indication whether any WM_EMACS_FILENOTIFY messages were found in
the queue.
src/w32inevt.c (handle_file_notifications): New function.
(w32_console_read_socket): Call it to process file notifications.
src/w32console.c (initialize_w32_display): Record the main thread ID
in dwMainThreadId.
src/deps.mk (inotify.o): New dependency list.
src/Makefile.in (SOME_MACHINE_OBJECTS): Add w32notify.o.
src/w32term.h (WM_EMACS_FILENOTIFY): New custom message.
(WM_EMACS_END): Bump value by 1.
(notification_buffer_in_use, file_notifications)
(notifications_size, notifications_desc): Declare.
(w32_get_watch_object, lispy_file_action, globals_of_w32notify):
Add prototypes.
src/w32term.c (lispy_file_action, queue_notifications): New functions.
(syms_of_w32term) <Qadded, Qremoved, Qmodified, Qrenamed_from>
<Qrenamed_to>: New symbols.
(w32_read_socket): Handle the WM_EMACS_FILENOTIFY message.
src/w32notify.c: New file, implements file event notifications for
MS-Windows.
src/w32fns.c (w32_wnd_proc): Handle the WM_EMACS_FILENOTIFY message
by posting it to the w32_read_socket queue.
src/termhooks.h (enum event_kind) [HAVE_NTGUI]: Support
FILE_NOTIFY_EVENT.
src/makefile.w32-in (OBJ2): Add $(BLD)/w32notify.$(O).
(GLOBAL_SOURCES): Add w32notify.c
($(BLD)/w32notify.$(O)): New set of dependencies.
src/lisp.h (syms_of_w32notify) [WINDOWSNT]: Add prototype.
src/keyboard.c (kbd_buffer_get_event) [WINDOWSNT]: Handle
FILE_NOTIFY_EVENT.
(syms_of_keyboard) [HAVE_NTGUI] <Qfile_notify>: New symbol.
(keys_of_keyboard) [WINDOWSNT]: Bind file-notify to
w32notify-handle-event by default.
src/emacs.c (main) [WINDOWSNT]: Call globals_of_w32notify and
syms_of_w32notify.
lisp/subr.el (w32notify-handle-event): New function.
(inotify-handle-event): Doc fix.
lib-src/makefile.w32-in (obj): Add w32notify.o. Add missing X and Unix
sources.
added:
src/w32notify.c
modified:
etc/NEWS
lib-src/ChangeLog
lib-src/makefile.w32-in
lisp/ChangeLog
lisp/subr.el
src/ChangeLog
src/Makefile.in
src/deps.mk
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/w32proc.c
src/w32term.c
src/w32term.h
src/w32xfns.c
=== modified file 'etc/NEWS'
--- a/etc/NEWS 2012-12-10 11:17:21 +0000
+++ b/etc/NEWS 2012-12-10 12:38:49 +0000
@@ -136,7 +136,8 @@
** Support for filesystem notifications.
Emacs now supports notifications of filesystem changes, such as
creation, modification, and deletion of files. This requires the
-'inotify' API on GNU/Linux systems.
+'inotify' API on GNU/Linux systems. On MS-Windows systems, this is
+supported for Windows XP and newer versions.
** Face changes
=== modified file 'lib-src/ChangeLog'
--- a/lib-src/ChangeLog 2012-12-02 01:47:56 +0000
+++ b/lib-src/ChangeLog 2012-12-10 13:25:17 +0000
@@ -1,3 +1,8 @@
+2012-12-10 Eli Zaretskii <address@hidden>
+
+ * makefile.w32-in (obj): Add w32notify.o. Add missing X and Unix
+ sources.
+
2012-12-02 Kevin Ryde <address@hidden>
* etags.c (Lisp_functions): Skip (defvar foo) declarations unless
=== modified file 'lib-src/makefile.w32-in'
--- a/lib-src/makefile.w32-in 2012-11-17 23:16:24 +0000
+++ b/lib-src/makefile.w32-in 2012-12-10 13:25:17 +0000
@@ -123,10 +123,13 @@
#
obj = dosfns.o msdos.o \
xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \
- fontset.o menu.o \
- w32.o w32console.o w32fns.o w32heap.o w32inevt.o cygw32.o \
+ fontset.o menu.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 w32notify.o \
w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
- font.o w32font.o w32uniscribe.o \
+ w16select.o widget.o xfont.o ftfont.o xftfont.o ftxfont.o gtkutil.o \
+ xsettings.o xgselect.o termcap.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 \
=== modified file 'lisp/ChangeLog'
--- a/lisp/ChangeLog 2012-12-10 11:17:21 +0000
+++ b/lisp/ChangeLog 2012-12-10 13:25:17 +0000
@@ -1,3 +1,8 @@
+2012-12-10 Eli Zaretskii <address@hidden>
+
+ * subr.el (w32notify-handle-event): New function.
+ (inotify-handle-event): Doc fix.
+
2012-12-10 RĂ¼diger Sonderfeld <address@hidden>
* subr.el (inotify-event-p, inotify-handle-event): New functions.
=== modified file 'lisp/subr.el'
--- a/lisp/subr.el 2012-12-10 11:17:21 +0000
+++ b/lisp/subr.el 2012-12-10 12:08:02 +0000
@@ -4333,15 +4333,25 @@
;;;###autoload
(defun inotify-handle-event (event)
- "Handle file system monitoring event.
-If EVENT is a filewatch event then the callback is called. If EVENT is
-not a filewatch event then a `filewatch-error' is signaled."
+ "Handle inotify file system monitoring event.
+If EVENT is an inotify filewatch event, call its callback.
+Otherwise, signal a `filewatch-error'."
(interactive "e")
(unless (inotify-event-p event)
(signal 'filewatch-error (cons "Not a valid inotify event" event)))
-
(funcall (nth 2 event) (nth 1 event)))
+(defun w32notify-handle-event (event)
+ "Handle MS-Windows file system monitoring event.
+If EVENT is an MS-Windows filewatch event, call its callback.
+Otherwise, signal a `filewatch-error'."
+ (interactive "e")
+ (if (and (eq (car event) 'file-w32notify)
+ (= (length event) 3))
+ (funcall (nth 2 event) (nth 1 event))
+ (signal 'filewatch-error
+ (cons "Not a valid MS-Windows file-notify event" event))))
+
;;;; Comparing version strings.
=== modified file 'src/ChangeLog'
--- a/src/ChangeLog 2012-12-10 11:17:21 +0000
+++ b/src/ChangeLog 2012-12-10 13:25:17 +0000
@@ -1,5 +1,63 @@
+2012-12-10 Eli Zaretskii <address@hidden>
+
+ Support for filesystem notifications on MS-Windows.
+ * w32proc.c (sys_select): If drain_message_queue returns non-zero,
+ and this is a TTY frame, signal the caller that keyboard input is
+ available.
+
+ * w32xfns.c (drain_message_queue): Now returns an int: an
+ indication whether any WM_EMACS_FILENOTIFY messages were found in
+ the queue.
+
+ * w32inevt.c (handle_file_notifications): New function.
+ (w32_console_read_socket): Call it to process file notifications.
+
+ * w32console.c (initialize_w32_display): Record the main thread ID
+ in dwMainThreadId.
+
+ * deps.mk (inotify.o): New dependency list.
+
+ * Makefile.in (SOME_MACHINE_OBJECTS): Add w32notify.o.
+
+ * w32term.h (WM_EMACS_FILENOTIFY): New custom message.
+ (WM_EMACS_END): Bump value by 1.
+ (notification_buffer_in_use, file_notifications)
+ (notifications_size, notifications_desc): Declare.
+ (w32_get_watch_object, lispy_file_action, globals_of_w32notify):
+ Add prototypes.
+
+ * w32term.c (lispy_file_action, queue_notifications): New functions.
+ (syms_of_w32term) <Qadded, Qremoved, Qmodified, Qrenamed_from>
+ <Qrenamed_to>: New symbols.
+ (w32_read_socket): Handle the WM_EMACS_FILENOTIFY message.
+
+ * w32notify.c: New file, implements 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) [HAVE_NTGUI]: Support
+ 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) [HAVE_NTGUI] <Qfile_notify>: New symbol.
+ (keys_of_keyboard) [WINDOWSNT]: Bind file-notify to
+ w32notify-handle-event by default.
+
+ * emacs.c (main) [WINDOWSNT]: Call globals_of_w32notify and
+ syms_of_w32notify.
+
2012-12-10 RĂ¼diger Sonderfeld <address@hidden>
+ Support for filesystem notifications on GNU/Linux via inotify.
* termhooks.h (enum event_kind) [HAVE_INOTIFY]: Add
FILE_NOTIFY_EVENT.
=== modified file 'src/Makefile.in'
--- a/src/Makefile.in 2012-12-10 11:17:21 +0000
+++ b/src/Makefile.in 2012-12-10 12:08:02 +0000
@@ -358,7 +358,7 @@
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
=== modified file 'src/deps.mk'
--- a/src/deps.mk 2012-01-05 09:46:05 +0000
+++ b/src/deps.mk 2012-12-10 13:25:17 +0000
@@ -128,6 +128,7 @@
indent.o: indent.c frame.h window.h indent.h buffer.h lisp.h $(config_h) \
termchar.h termopts.h disptab.h region-cache.h character.h category.h \
keyboard.h systime.h coding.h $(INTERVALS_H) globals.h
+inotify.o: inotify.c lisp.h coding.h process.h keyboard.h frame.h termhooks.h
insdel.o: insdel.c window.h buffer.h $(INTERVALS_H) blockinput.h character.h \
atimer.h systime.h region-cache.h lisp.h globals.h $(config_h)
keyboard.o: keyboard.c termchar.h termhooks.h termopts.h buffer.h character.h \
=== modified file 'src/emacs.c'
--- a/src/emacs.c 2012-12-10 11:17:21 +0000
+++ b/src/emacs.c 2012-12-10 12:08:02 +0000
@@ -1287,6 +1287,7 @@
#ifdef WINDOWSNT
globals_of_w32 ();
+ globals_of_w32notify ();
/* Initialize environment from registry settings. */
init_environment (argv);
init_ntproc (dumping); /* must precede init_editfns. */
@@ -1452,6 +1453,7 @@
#ifdef WINDOWSNT
syms_of_ntterm ();
+ syms_of_w32notify ();
#endif /* WINDOWSNT */
syms_of_profiler ();
=== modified file 'src/keyboard.c'
--- a/src/keyboard.c 2012-12-10 11:17:21 +0000
+++ b/src/keyboard.c 2012-12-10 12:08:02 +0000
@@ -313,6 +313,9 @@
Lisp_Object Qmouse_click;
#ifdef HAVE_NTGUI
Lisp_Object Qlanguage_change;
+#ifdef WINDOWSNT
+Lisp_Object Qfile_w32notify;
+#endif
#endif
static Lisp_Object Qdrag_n_drop;
static Lisp_Object Qsave_session;
@@ -3906,6 +3909,16 @@
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_w32notify,
+ 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)
{
@@ -11348,6 +11361,7 @@
#ifdef HAVE_NTGUI
DEFSYM (Qlanguage_change, "language-change");
+ DEFSYM (Qfile_w32notify, "file-w32notify");
#endif
#ifdef HAVE_DBUS
@@ -12126,6 +12140,8 @@
#if defined (WINDOWSNT)
initial_define_lispy_key (Vspecial_event_map, "language-change",
"ignore");
+ initial_define_lispy_key (Vspecial_event_map, "file-w32notify",
+ "w32notify-handle-event");
#endif
}
=== modified file 'src/lisp.h'
--- a/src/lisp.h 2012-12-10 11:17:21 +0000
+++ b/src/lisp.h 2012-12-10 12:08:02 +0000
@@ -3516,6 +3516,11 @@
extern Lisp_Object Qfont_param;
#endif
+#ifdef WINDOWSNT
+/* Defined on w32notify.c. */
+extern void syms_of_w32notify (void);
+#endif
+
/* Defined in inotify.c */
#ifdef HAVE_INOTIFY
extern void syms_of_inotify (void);
=== modified file 'src/makefile.w32-in'
--- a/src/makefile.w32-in 2012-11-23 08:47:34 +0000
+++ b/src/makefile.w32-in 2012-12-10 12:08:02 +0000
@@ -134,6 +134,7 @@
$(BLD)/w32menu.$(O) \
$(BLD)/w32reg.$(O) \
$(BLD)/w32font.$(O) \
+ $(BLD)/w32notify.$(O) \
$(BLD)/w32uniscribe.$(O)
LIBS = $(TLIB0) \
@@ -209,7 +210,7 @@
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 \
@@ -1693,6 +1694,17 @@
$(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.
#
=== modified file 'src/termhooks.h'
--- a/src/termhooks.h 2012-12-10 11:17:21 +0000
+++ b/src/termhooks.h 2012-12-10 13:25:17 +0000
@@ -211,7 +211,7 @@
, NS_NONKEY_EVENT
#endif
-#ifdef HAVE_INOTIFY
+#if defined (HAVE_INOTIFY) || defined (HAVE_NTGUI)
/* File or directory was changed. */
, FILE_NOTIFY_EVENT
#endif
=== modified file 'src/w32console.c'
--- a/src/w32console.c 2012-10-08 12:53:18 +0000
+++ b/src/w32console.c 2012-10-12 09:47:00 +0000
@@ -746,6 +746,9 @@
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"));
=== modified file 'src/w32fns.c'
--- a/src/w32fns.c 2012-12-10 02:00:42 +0000
+++ b/src/w32fns.c 2012-12-10 12:08:02 +0000
@@ -3957,6 +3957,9 @@
return retval;
}
+ case WM_EMACS_FILENOTIFY:
+ my_post_msg (&wmsg, hwnd, msg, wParam, lParam);
+ return 1;
default:
/* Check for messages registered at runtime. */
=== modified file 'src/w32inevt.c'
--- a/src/w32inevt.c 2012-10-12 14:14:35 +0000
+++ b/src/w32inevt.c 2012-10-18 18:00:00 +0000
@@ -576,6 +576,74 @@
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 @@
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;
}
=== added file 'src/w32notify.c'
--- a/src/w32notify.c 1970-01-01 00:00:00 +0000
+++ b/src/w32notify.c 2012-12-10 12:38:49 +0000
@@ -0,0 +1,631 @@
+/* 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/>. */
+
+/* Written by Eli Zaretskii <address@hidden>.
+
+ 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 in
+ SleepEx for that call to complete. 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 are done
+ processing the notifications, they reset 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. Since watch_worker is the thread
+ procedure of the worker thread, exiting it causes the thread to
+ exit. 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);
+}
=== modified file 'src/w32proc.c'
--- a/src/w32proc.c 2012-12-08 02:30:51 +0000
+++ b/src/w32proc.c 2012-12-10 12:08:02 +0000
@@ -2018,7 +2018,24 @@
(*) 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)
{
=== modified file 'src/w32term.c'
--- a/src/w32term.c 2012-12-04 15:15:30 +0000
+++ b/src/w32term.c 2012-12-10 12:08:02 +0000
@@ -246,6 +246,8 @@
#endif
static Lisp_Object Qvendor_specific_keysyms;
+static Lisp_Object Qadded, Qremoved, Qmodified;
+static Lisp_Object Qrenamed_from, Qrenamed_to;
/***********************************************************************
@@ -3204,6 +3206,122 @@
}
+/* 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;
+}
+
+
/* Function to report a mouse movement to the mainstream Emacs code.
The input handler calls this.
@@ -4836,6 +4954,12 @@
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)
@@ -6503,6 +6627,12 @@
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. */);
=== modified file 'src/w32term.h'
--- a/src/w32term.h 2012-12-06 13:48:11 +0000
+++ b/src/w32term.h 2012-12-10 12:08:02 +0000
@@ -602,7 +602,8 @@
#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)
@@ -652,7 +653,7 @@
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 *);
@@ -662,10 +663,17 @@
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. */
@@ -759,6 +767,7 @@
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;
=== modified file 'src/w32xfns.c'
--- a/src/w32xfns.c 2012-10-07 22:31:58 +0000
+++ b/src/w32xfns.c 2012-10-12 09:47:00 +0000
@@ -315,16 +315,22 @@
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. */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Emacs-diffs] /srv/bzr/emacs/trunk r111172: Support for filesystem notifications on MS-Windows.,
Eli Zaretskii <=