Commit c5c91b84 authored by Eli Zaretskii's avatar Eli Zaretskii
Browse files

Final version that supports only one watch at a time.

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