From c5c91b847a41ec7e09dee582816d702c8a9adcdd Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 7 Oct 2012 15:41:15 +0200 Subject: [PATCH] Final version that supports only one watch at a time. --- lib-src/ChangeLog | 4 ++ lisp/ChangeLog | 4 ++ src/ChangeLog | 34 ++++++++++++++++ src/w32fns.c | 3 -- src/w32notify.c | 102 ++++++++++++++++++++++------------------------ src/w32term.c | 4 +- 6 files changed, 94 insertions(+), 57 deletions(-) diff --git a/lib-src/ChangeLog b/lib-src/ChangeLog index 2a8ac9b8131..a9031d74758 100644 --- a/lib-src/ChangeLog +++ b/lib-src/ChangeLog @@ -1,3 +1,7 @@ +2012-10-07 Eli Zaretskii + + * makefile.w32-in (obj): Add w32notify.o. + 2012-10-01 Fabrice Popineau * make-docfile.c (write_globals): Special-case diff --git a/lisp/ChangeLog b/lisp/ChangeLog index e54e82233a2..7d40007f435 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,7 @@ +2012-10-07 Eli Zaretskii + + * subr.el (w32notify-handle-event): New function. + 2012-10-07 Glenn Morris * mail/rmailmm.el (rmail-mime-process-multipart): diff --git a/src/ChangeLog b/src/ChangeLog index 492b966a256..cb2aab2166c 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,37 @@ +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) + : 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] : 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). diff --git a/src/w32fns.c b/src/w32fns.c index 2d36da3b56b..fac6030b9b5 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -2259,8 +2259,6 @@ w32_msg_pump (deferred_msg * msg_buf) while ((w32_unicode_gui ? GetMessageW : GetMessageA) (&msg, NULL, 0, 0)) { - if (msg.message == WM_EMACS_FILENOTIFY) - DebPrint (("w32_msg_pump: File notification, hwnd = 0x%p\n", msg.hwnd)); if (msg.hwnd == NULL) { switch (msg.message) @@ -3810,7 +3808,6 @@ w32_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return retval; } case WM_EMACS_FILENOTIFY: - DebPrint (("w32_wnd_proc: File notification arrived, posting\n")); my_post_msg (&wmsg, hwnd, msg, wParam, lParam); return 1; diff --git a/src/w32notify.c b/src/w32notify.c index 0dc4d27d33f..81199b2a0a1 100644 --- a/src/w32notify.c +++ b/src/w32notify.c @@ -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 #include @@ -58,51 +106,6 @@ static Lisp_Object Qfile_name, Qdirectory_name, Qattributes, Qsize; static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time; static Lisp_Object Qsecurity_desc, Qsubtree, watch_list; -#if 0 -/* FIXME: Debugging code, should be removed eventually. */ -const wchar_t * -format_file_action (DWORD action) -{ - static const wchar_t *action_str[] = - { L"???", L"Added", L"Removed", L"Modified", L"Renamed from", L"Renamed to" }; - - if (action >= sizeof(action_str)/sizeof(action_str[0])) - action = 0; - return action_str[action]; -} - -void -parse_notifications (BYTE *info, DWORD info_size) -{ - BYTE *p = info; - FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p; - const DWORD min_size - = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t); - - if (!info_size) - { - printf ("No info in notifications!\n"); - return; - } - - while (info_size >= min_size) - { - wchar_t *fn = alloca (fni->FileNameLength + sizeof(wchar_t)); - const wchar_t *action_str; - - memcpy (fn, fni->FileName, fni->FileNameLength); - fn[fni->FileNameLength/sizeof(wchar_t)] = 0; - action_str = format_file_action (fni->Action); - wprintf (L"%s: %s\n", action_str, fn); - if (!fni->NextEntryOffset) - break; - p += fni->NextEntryOffset; - fni = (PFILE_NOTIFY_INFORMATION)p; - info_size -= fni->NextEntryOffset; - } -} -#endif /* debugging code */ - /* Signal to the main thread that we have file notifications for it to process. */ static void @@ -141,7 +144,6 @@ send_notifications (BYTE *info, DWORD info_size, HANDLE hdir, int *terminate) if (PostMessage (FRAME_W32_WINDOW (f), WM_EMACS_FILENOTIFY, 0, 0)) notification_buffer_in_use = 1; done = 1; - DebPrint (("Announced notifications of %lu bytes\n", info_size)); } leave_crit (); if (!done) @@ -205,9 +207,6 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info) } else { -#if 0 /* debugging code */ - parse_notifications (dirwatch->buf, bytes_ret); -#endif /* Tell the main thread we have notifications for it. */ send_notifications (dirwatch->buf, bytes_ret, dirwatch->dir, &dirwatch->terminate); @@ -233,7 +232,7 @@ watch_worker (LPVOID arg) dirwatch->io_info, watch_completion); if (!status) { - DebPrint (("watch_worker(1): %lu\n", GetLastError ())); + DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ())); xfree (dirwatch->buf); dirwatch->buf = NULL; xfree (dirwatch->io_info); @@ -249,11 +248,8 @@ watch_worker (LPVOID arg) could be either a change notification or a cancellation of the watch. */ sleep_result = SleepEx (INFINITE, TRUE); - if (dirwatch->terminate) - DebPrint (("watch_worker: exiting by request\n")); } while (!dirwatch->terminate); - DebPrint (("watch_worker(2): %lu\n", GetLastError ())); return 0; } diff --git a/src/w32term.c b/src/w32term.c index 1c32383098f..71e6c7235a1 100644 --- a/src/w32term.c +++ b/src/w32term.c @@ -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, break; case WM_EMACS_FILENOTIFY: - DebPrint (("w32_read_socket: File notification arrived\n")); f = x_window_to_frame (dpyinfo, msg.msg.hwnd); if (f) queue_notifications (&inev, &msg, f, &count); -- 2.39.5