* configure.ac (USE_CAIRO): Rename to more standard HAVE_CAIRO.
[emacs.git] / src / w32notify.c
blob576cf844dab03e78f75afc35ad40197e19f443b0
1 /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2 Copyright (C) 2012-2016 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19 /* Written by Eli Zaretskii <eliz@gnu.org>.
21 Design overview:
23 For each watch request, we launch a separate worker thread. The
24 worker thread runs the watch_worker function, which issues an
25 asynchronous call to ReadDirectoryChangesW, and then waits in
26 SleepEx for that call to complete. Waiting in SleepEx puts the
27 thread in an "alertable" state, so it wakes up when either (a) the
28 call to ReadDirectoryChangesW completes, or (b) the main thread
29 instructs the worker thread to terminate by sending it an APC, see
30 below.
32 When the ReadDirectoryChangesW call completes, its completion
33 routine watch_completion is automatically called. watch_completion
34 stashes the received file events in a buffer used to communicate
35 them to the main thread (using a critical section, so that several
36 threads could use the same buffer), posts a special message,
37 WM_EMACS_FILENOTIFY, to the Emacs's message queue, and returns.
38 That causes the SleepEx function call inside watch_worker to
39 return, and watch_worker then issues another call to
40 ReadDirectoryChangesW. (Except when it does not, see below.)
42 In a GUI session, the WM_EMACS_FILENOTIFY message posted to the
43 message queue gets dispatched to the main Emacs window procedure,
44 which queues it for processing by w32_read_socket. When
45 w32_read_socket sees this message, it accesses the buffer with file
46 notifications (using a critical section), extracts the information,
47 converts it to a series of FILE_NOTIFY_EVENT events, and stuffs
48 them into the input event queue to be processed by keyboard.c input
49 machinery (read_char via a call to kbd_buffer_get_event).
51 In a non-GUI session, we send the WM_EMACS_FILENOTIFY message to
52 the main (a.k.a. "Lisp") thread instead, since there are no window
53 procedures in console programs. That message wakes up
54 MsgWaitForMultipleObjects inside sys_select, which then signals to
55 its caller that some keyboard input is available. This causes
56 w32_console_read_socket to be called, which accesses the buffer
57 with file notifications and stuffs them into the input event queue
58 for keyboard.c to process.
60 When the FILE_NOTIFY_EVENT event is processed by keyboard.c's
61 kbd_buffer_get_event, it is converted to a Lispy event that can be
62 bound to a command. The default binding is file-notify-handle-event,
63 defined on subr.el.
65 After w32_read_socket or w32_console_read_socket are done
66 processing the notifications, they reset a flag signaling to all
67 watch worker threads that the notifications buffer is available for
68 more input.
70 When the watch is removed by a call to w32notify-rm-watch, the main
71 thread requests that the worker thread terminates by queuing an APC
72 for the worker thread. The APC specifies the watch_end function to
73 be called. watch_end calls CancelIo on the outstanding
74 ReadDirectoryChangesW call and closes the handle on which the
75 watched directory was open. When watch_end returns, the
76 watch_completion function is called one last time with the
77 ERROR_OPERATION_ABORTED status, which causes it to clean up and set
78 a flag telling watch_worker to exit without issuing another
79 ReadDirectoryChangesW call. Since watch_worker is the thread
80 procedure of the worker thread, exiting it causes the thread to
81 exit. The main thread waits for some time for the worker thread to
82 exit, and if it doesn't, terminates it forcibly. */
84 #include <stddef.h>
85 #include <errno.h>
87 /* must include CRT headers *before* config.h */
88 #include <config.h>
90 #include <windows.h>
92 #include "lisp.h"
93 #include "w32term.h" /* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
94 #include "w32common.h" /* for OS version data */
95 #include "w32.h" /* for w32_strerror */
96 #include "coding.h"
97 #include "keyboard.h"
98 #include "frame.h" /* needed by termhooks.h */
99 #include "termhooks.h" /* for FILE_NOTIFY_EVENT */
101 #define DIRWATCH_SIGNATURE 0x01233210
103 struct notification {
104 BYTE *buf; /* buffer for ReadDirectoryChangesW */
105 OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */
106 BOOL subtree; /* whether to watch subdirectories */
107 DWORD filter; /* bit mask for events to watch */
108 char *watchee; /* the file we are interested in, UTF-8 encoded */
109 HANDLE dir; /* handle to the watched directory */
110 HANDLE thr; /* handle to the thread that watches */
111 volatile int terminate; /* if non-zero, request for the thread to terminate */
112 unsigned signature;
115 /* Used for communicating notifications to the main thread. */
116 volatile int notification_buffer_in_use;
117 BYTE file_notifications[16384];
118 DWORD notifications_size;
119 void *notifications_desc;
121 static Lisp_Object watch_list;
123 /* Signal to the main thread that we have file notifications for it to
124 process. */
125 static void
126 send_notifications (BYTE *info, DWORD info_size, void *desc,
127 volatile int *terminate)
129 int done = 0;
130 struct frame *f = SELECTED_FRAME ();
132 /* A single buffer is used to communicate all notifications to the
133 main thread. Since both the main thread and several watcher
134 threads could be active at the same time, we use a critical area
135 and an "in-use" flag to synchronize them. A watcher thread can
136 only put its notifications in the buffer if it acquires the
137 critical area and finds the "in-use" flag reset. The main thread
138 resets the flag after it is done processing notifications.
140 FIXME: is there a better way of dealing with this? */
141 while (!done && !*terminate)
143 enter_crit ();
144 if (!notification_buffer_in_use)
146 if (info_size)
147 memcpy (file_notifications, info, info_size);
148 notifications_size = info_size;
149 notifications_desc = desc;
150 /* If PostMessage fails, the message queue is full. If that
151 happens, the last thing they will worry about is file
152 notifications. So we effectively discard the
153 notification in that case. */
154 if ((FRAME_TERMCAP_P (f)
155 /* We send the message to the main (a.k.a. "Lisp")
156 thread, where it will wake up MsgWaitForMultipleObjects
157 inside sys_select, causing it to report that there's
158 some keyboard input available. This will in turn cause
159 w32_console_read_socket to be called, which will pick
160 up the file notifications. */
161 && PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0))
162 || (FRAME_W32_P (f)
163 && PostMessage (FRAME_W32_WINDOW (f),
164 WM_EMACS_FILENOTIFY, 0, 0))
165 /* When we are running in batch mode, there's no one to
166 send a message, so we just signal the data is
167 available and hope sys_select will be called soon and
168 will read the data. */
169 || (FRAME_INITIAL_P (f) && noninteractive))
170 notification_buffer_in_use = 1;
171 done = 1;
173 leave_crit ();
174 if (!done)
175 Sleep (5);
179 /* An APC routine to cancel outstanding directory watch. Invoked by
180 the main thread via QueueUserAPC. This is needed because only the
181 thread that issued the ReadDirectoryChangesW call can call CancelIo
182 to cancel that. (CancelIoEx is only available since Vista, so we
183 cannot use it on XP.) */
184 VOID CALLBACK
185 watch_end (ULONG_PTR arg)
187 HANDLE hdir = (HANDLE)arg;
189 if (hdir && hdir != INVALID_HANDLE_VALUE)
191 CancelIo (hdir);
192 CloseHandle (hdir);
196 /* A completion routine (a.k.a. "APC function") for handling events
197 read by ReadDirectoryChangesW. Called by the OS when the thread
198 which issued the asynchronous ReadDirectoryChangesW call is in the
199 "alertable state", i.e. waiting inside SleepEx call. */
200 VOID CALLBACK
201 watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
203 struct notification *dirwatch;
205 /* Who knows what happened? Perhaps the OVERLAPPED structure was
206 freed by someone already? In any case, we cannot do anything
207 with this request, so just punt and skip it. FIXME: should we
208 raise the 'terminate' flag in this case? */
209 if (!io_info)
210 return;
212 /* We have a pointer to our dirwatch structure conveniently stashed
213 away in the hEvent member of the OVERLAPPED struct. According to
214 MSDN documentation of ReadDirectoryChangesW: "The hEvent member
215 of the OVERLAPPED structure is not used by the system, so you can
216 use it yourself." */
217 dirwatch = (struct notification *)io_info->hEvent;
218 if (status == ERROR_OPERATION_ABORTED)
220 /* We've been called because the main thread told us to issue
221 CancelIo on the directory we watch, and watch_end did so.
222 The directory handle is already closed. We should clean up
223 and exit, signaling to the thread worker routine not to
224 issue another call to ReadDirectoryChangesW. Note that we
225 don't free the dirwatch object itself nor the memory consumed
226 by its buffers; this is done by the main thread in
227 remove_watch. Calling malloc/free from a thread other than
228 the main thread is a no-no. */
229 dirwatch->dir = NULL;
230 dirwatch->terminate = 1;
232 else
234 /* Tell the main thread we have notifications for it. */
235 send_notifications (dirwatch->buf, bytes_ret, dirwatch,
236 &dirwatch->terminate);
240 /* Worker routine for the watch thread. */
241 static DWORD WINAPI
242 watch_worker (LPVOID arg)
244 struct notification *dirwatch = (struct notification *)arg;
246 do {
247 BOOL status;
248 DWORD bytes_ret = 0;
250 if (dirwatch->dir)
252 status = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, 16384,
253 dirwatch->subtree, dirwatch->filter,
254 &bytes_ret,
255 dirwatch->io_info, watch_completion);
256 if (!status)
258 DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
259 /* We cannot remove the dirwatch object from watch_list,
260 because we are in a separate thread. For the same
261 reason, we also cannot free memory consumed by the
262 buffers allocated for the dirwatch object. So we close
263 the directory handle, but do not free the object itself
264 or its buffers. We also don't touch the signature.
265 This way, remove_watch can still identify the object,
266 remove it, and free its memory. */
267 CloseHandle (dirwatch->dir);
268 dirwatch->dir = NULL;
269 return 1;
272 /* Sleep indefinitely until awoken by the I/O completion, which
273 could be either a change notification or a cancellation of the
274 watch. */
275 SleepEx (INFINITE, TRUE);
276 } while (!dirwatch->terminate);
278 return 0;
281 /* Launch a thread to watch changes to FILE in a directory open on
282 handle HDIR. */
283 static struct notification *
284 start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
286 struct notification *dirwatch = xzalloc (sizeof (struct notification));
288 dirwatch->signature = DIRWATCH_SIGNATURE;
289 dirwatch->buf = xmalloc (16384);
290 dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
291 /* Stash a pointer to dirwatch structure for use by the completion
292 routine. According to MSDN documentation of ReadDirectoryChangesW:
293 "The hEvent member of the OVERLAPPED structure is not used by the
294 system, so you can use it yourself." */
295 dirwatch->io_info->hEvent = dirwatch;
296 dirwatch->subtree = subdirs;
297 dirwatch->filter = flags;
298 dirwatch->watchee = xstrdup (file);
299 dirwatch->terminate = 0;
300 dirwatch->dir = hdir;
302 /* See w32proc.c where it calls CreateThread for the story behind
303 the 2nd and 5th argument in the call to CreateThread. */
304 dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
305 0x00010000, NULL);
307 if (!dirwatch->thr)
309 xfree (dirwatch->buf);
310 xfree (dirwatch->io_info);
311 xfree (dirwatch->watchee);
312 xfree (dirwatch);
313 dirwatch = NULL;
315 return dirwatch;
318 /* Called from the main thread to start watching FILE in PARENT_DIR,
319 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
320 PARENT_DIR as well. Value is a pointer to 'struct notification'
321 used by the thread that watches the changes. */
322 static struct notification *
323 add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
325 HANDLE hdir;
326 struct notification *dirwatch = NULL;
328 if (!file)
329 return NULL;
331 if (w32_unicode_filenames)
333 wchar_t dir_w[MAX_PATH], file_w[MAX_PATH];
335 filename_to_utf16 (parent_dir, dir_w);
336 if (*file)
337 filename_to_utf16 (file, file_w);
338 else
339 file_w[0] = 0;
341 hdir = CreateFileW (dir_w,
342 FILE_LIST_DIRECTORY,
343 /* FILE_SHARE_DELETE doesn't preclude other
344 processes from deleting files inside
345 parent_dir. */
346 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
347 NULL, OPEN_EXISTING,
348 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
349 NULL);
351 else
353 char dir_a[MAX_PATH], file_a[MAX_PATH];
355 filename_to_ansi (parent_dir, dir_a);
356 if (*file)
357 filename_to_ansi (file, file_a);
358 else
359 file_a[0] = '\0';
361 hdir = CreateFileA (dir_a,
362 FILE_LIST_DIRECTORY,
363 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
364 NULL, OPEN_EXISTING,
365 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
366 NULL);
368 if (hdir == INVALID_HANDLE_VALUE)
369 return NULL;
371 if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
372 CloseHandle (hdir);
374 return dirwatch;
377 /* Stop watching a directory specified by a pointer to its dirwatch object. */
378 static int
379 remove_watch (struct notification *dirwatch)
381 if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
383 int i;
384 BOOL status;
385 DWORD exit_code, err;
387 /* Only the thread that issued the outstanding I/O call can call
388 CancelIo on it. (CancelIoEx is available only since Vista.)
389 So we need to queue an APC for the worker thread telling it
390 to terminate. */
391 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
392 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
393 /* We also set the terminate flag, for when the thread is
394 waiting on the critical section that never gets acquired.
395 FIXME: is there a cleaner method? Using SleepEx there is a
396 no-no, as that will lead to recursive APC invocations and
397 stack overflow. */
398 dirwatch->terminate = 1;
399 /* Wait for the thread to exit. FIXME: is there a better method
400 that is not overly complex? */
401 for (i = 0; i < 50; i++)
403 if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
404 && exit_code == STILL_ACTIVE))
405 break;
406 Sleep (10);
408 if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
409 || exit_code == STILL_ACTIVE)
411 if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
413 TerminateThread (dirwatch->thr, 0);
414 if (dirwatch->dir)
415 CloseHandle (dirwatch->dir);
419 /* Clean up. */
420 if (dirwatch->thr)
422 CloseHandle (dirwatch->thr);
423 dirwatch->thr = NULL;
425 xfree (dirwatch->buf);
426 xfree (dirwatch->io_info);
427 xfree (dirwatch->watchee);
428 xfree (dirwatch);
430 return 0;
432 else
434 DebPrint (("Unknown dirwatch object!\n"));
435 return -1;
439 static DWORD
440 filter_list_to_flags (Lisp_Object filter_list)
442 DWORD flags = 0;
444 if (NILP (filter_list))
445 return flags;
447 if (!NILP (Fmember (Qfile_name, filter_list)))
448 flags |= FILE_NOTIFY_CHANGE_FILE_NAME;
449 if (!NILP (Fmember (Qdirectory_name, filter_list)))
450 flags |= FILE_NOTIFY_CHANGE_DIR_NAME;
451 if (!NILP (Fmember (Qattributes, filter_list)))
452 flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
453 if (!NILP (Fmember (Qsize, filter_list)))
454 flags |= FILE_NOTIFY_CHANGE_SIZE;
455 if (!NILP (Fmember (Qlast_write_time, filter_list)))
456 flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
457 if (!NILP (Fmember (Qlast_access_time, filter_list)))
458 flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
459 if (!NILP (Fmember (Qcreation_time, filter_list)))
460 flags |= FILE_NOTIFY_CHANGE_CREATION;
461 if (!NILP (Fmember (Qsecurity_desc, filter_list)))
462 flags |= FILE_NOTIFY_CHANGE_SECURITY;
464 return flags;
467 DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
468 Sw32notify_add_watch, 3, 3, 0,
469 doc: /* Add a watch for filesystem events pertaining to FILE.
471 This arranges for filesystem events pertaining to FILE to be reported
472 to Emacs. Use `w32notify-rm-watch' to cancel the watch.
474 Value is a descriptor for the added watch. If the file cannot be
475 watched for some reason, this function signals a `file-error' error.
477 FILTER is a list of conditions for reporting an event. It can include
478 the following symbols:
480 'file-name' -- report file creation, deletion, or renaming
481 'directory-name' -- report directory creation, deletion, or renaming
482 'attributes' -- report changes in attributes
483 'size' -- report changes in file-size
484 'last-write-time' -- report changes in last-write time
485 'last-access-time' -- report changes in last-access time
486 'creation-time' -- report changes in creation time
487 'security-desc' -- report changes in security descriptor
489 If FILE is a directory, and FILTER includes 'subtree', then all the
490 subdirectories will also be watched and changes in them reported.
492 When any event happens that satisfies the conditions specified by
493 FILTER, Emacs will call the CALLBACK function passing it a single
494 argument EVENT, which is of the form
496 (DESCRIPTOR ACTION FILE)
498 DESCRIPTOR is the same object as the one returned by this function.
499 ACTION is the description of the event. It could be any one of the
500 following:
502 'added' -- FILE was added
503 'removed' -- FILE was deleted
504 'modified' -- FILE's contents or its attributes were modified
505 'renamed-from' -- a file was renamed whose old name was FILE
506 'renamed-to' -- a file was renamed and its new name is FILE
508 FILE is the name of the file whose event is being reported.
510 Note that some networked filesystems, such as Samba-mounted Unix
511 volumes, might not send notifications about file changes. In these
512 cases, this function will return a valid descriptor, but notifications
513 will never come in. Volumes shared from remote Windows machines do
514 generate notifications correctly, though. */)
515 (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
517 Lisp_Object dirfn, basefn, watch_object, watch_descriptor;
518 DWORD flags;
519 BOOL subdirs = FALSE;
520 struct notification *dirwatch = NULL;
521 Lisp_Object lisp_errstr;
522 char *errstr;
524 CHECK_LIST (filter);
526 /* The underlying features are available only since XP. */
527 if (os_subtype == OS_9X
528 || (w32_major_version == 5 && w32_minor_version < 1))
530 errno = ENOSYS;
531 report_file_notify_error ("Watching filesystem events is not supported",
532 Qnil);
535 /* filenotify.el always passes us a directory, either the parent
536 directory of a file to be watched, or the directory to be
537 watched. */
538 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
539 if (NILP (Ffile_directory_p (file)))
541 /* This should only happen if we are called directly, not via
542 filenotify.el. If BASEFN is empty, the argument was the root
543 directory on its drive. */
544 dirfn = ENCODE_FILE (Ffile_name_directory (file));
545 basefn = ENCODE_FILE (Ffile_name_nondirectory (file));
546 if (*SDATA (basefn) == '\0')
547 subdirs = TRUE;
549 else
551 dirfn = ENCODE_FILE (file);
552 basefn = Qnil;
555 if (!NILP (Fmember (Qsubtree, filter)))
556 subdirs = TRUE;
558 flags = filter_list_to_flags (filter);
560 dirwatch = add_watch (SSDATA (dirfn), NILP (basefn) ? "" : SSDATA (basefn),
561 subdirs, flags);
562 if (!dirwatch)
564 DWORD err = GetLastError ();
566 errno = EINVAL;
567 if (err)
569 errstr = w32_strerror (err);
570 if (!NILP (Vlocale_coding_system))
571 lisp_errstr
572 = code_convert_string_norecord (build_unibyte_string (errstr),
573 Vlocale_coding_system, 0);
574 else
575 lisp_errstr = build_string (errstr);
576 report_file_notify_error ("Cannot watch file",
577 Fcons (lisp_errstr, Fcons (file, Qnil)));
579 else
580 report_file_notify_error ("Cannot watch file", Fcons (file, Qnil));
582 /* Store watch object in watch list. */
583 watch_descriptor = make_pointer_integer (dirwatch);
584 watch_object = Fcons (watch_descriptor, callback);
585 watch_list = Fcons (watch_object, watch_list);
587 return watch_descriptor;
590 DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch,
591 Sw32notify_rm_watch, 1, 1, 0,
592 doc: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
594 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
595 (Lisp_Object watch_descriptor)
597 Lisp_Object watch_object;
598 struct notification *dirwatch;
599 int status = -1;
601 /* Remove the watch object from watch list. Do this before freeing
602 the object, do that even if we fail to free it, watch_list is
603 kept free of junk. */
604 watch_object = Fassoc (watch_descriptor, watch_list);
605 if (!NILP (watch_object))
607 watch_list = Fdelete (watch_object, watch_list);
608 dirwatch = (struct notification *)XINTPTR (watch_descriptor);
609 if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
610 status = remove_watch (dirwatch);
613 if (status == -1)
614 report_file_notify_error ("Invalid watch descriptor",
615 Fcons (watch_descriptor, Qnil));
617 return Qnil;
620 Lisp_Object
621 w32_get_watch_object (void *desc)
623 Lisp_Object descriptor = make_pointer_integer (desc);
625 /* This is called from the input queue handling code, inside a
626 critical section, so we cannot possibly QUIT if watch_list is not
627 in the right condition. */
628 return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
631 DEFUN ("w32notify-valid-p", Fw32notify_valid_p, Sw32notify_valid_p, 1, 1, 0,
632 doc: /* "Check a watch specified by its WATCH-DESCRIPTOR for validity.
634 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.
636 A watch can become invalid if the directory it watches is deleted, or if
637 the watcher thread exits abnormally for any other reason. Removing the
638 watch by calling `w32notify-rm-watch' also makes it invalid. */)
639 (Lisp_Object watch_descriptor)
641 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
643 if (!NILP (watch_object))
645 struct notification *dirwatch =
646 (struct notification *)XINTPTR (watch_descriptor);
647 if (w32_valid_pointer_p (dirwatch, sizeof(struct notification))
648 && dirwatch->dir != NULL)
649 return Qt;
652 return Qnil;
655 void
656 globals_of_w32notify (void)
658 watch_list = Qnil;
661 void
662 syms_of_w32notify (void)
664 DEFSYM (Qfile_name, "file-name");
665 DEFSYM (Qdirectory_name, "directory-name");
666 DEFSYM (Qattributes, "attributes");
667 DEFSYM (Qlast_write_time, "last-write-time");
668 DEFSYM (Qlast_access_time, "last-access-time");
669 DEFSYM (Qcreation_time, "creation-time");
670 DEFSYM (Qsecurity_desc, "security-desc");
671 DEFSYM (Qsubtree, "subtree");
673 defsubr (&Sw32notify_add_watch);
674 defsubr (&Sw32notify_rm_watch);
675 defsubr (&Sw32notify_valid_p);
677 staticpro (&watch_list);
679 Fprovide (intern_c_string ("w32notify"), Qnil);