w32notify.c 24.5 KB
Newer Older
Eli Zaretskii's avatar
Eli Zaretskii committed
1
/* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2

Paul Eggert's avatar
Paul Eggert committed
3
Copyright (C) 2012-2020 Free Software Foundation, Inc.
4 5

Author: Eli Zaretskii <eliz@gnu.org>
Eli Zaretskii's avatar
Eli Zaretskii committed
6 7 8 9 10

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
11 12
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
Eli Zaretskii's avatar
Eli Zaretskii committed
13 14 15 16 17 18 19

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
Paul Eggert's avatar
Paul Eggert committed
20
along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
Eli Zaretskii's avatar
Eli Zaretskii committed
21

22
/* Design overview:
23 24 25

   For each watch request, we launch a separate worker thread.  The
   worker thread runs the watch_worker function, which issues an
26 27 28 29 30 31 32
   asynchronous call to ReadDirectoryChangesW, and then calls
   WaitForSingleObjectEx to wait that an event be signaled
   to terminate the thread.
   Waiting with WaitForSingleObjectEx 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 signaling an event, see below.
33 34 35

   When the ReadDirectoryChangesW call completes, its completion
   routine watch_completion is automatically called.  watch_completion
36 37 38 39 40 41 42 43 44
   stashes the received file events in a linked list used to
   communicate them to the main thread (using a critical section, so
   that several threads could alter the same linked list), posts a
   special message, WM_EMACS_FILENOTIFY, to the Emacs's message queue,
   and returns.  That causes the WaitForSingleObjectEx function call
   inside watch_worker to return, but the thread won't terminate until
   the event telling to do so will be signaled.  The completion
   routine issued another call to ReadDirectoryChangesW as quickly as
   possible.  (Except when it does not, see below.)
45

Juanma Barranquero's avatar
Juanma Barranquero committed
46
   In a GUI session, the WM_EMACS_FILENOTIFY message posted to the
47 48
   message queue gets dispatched to the main Emacs window procedure,
   which queues it for processing by w32_read_socket.  When
49
   w32_read_socket sees this message, it accesses the linked list with file
50 51 52 53 54 55 56 57 58 59
   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
60
   w32_console_read_socket to be called, which accesses the linked list
61 62 63 64 65
   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
66
   bound to a command.  The default binding is file-notify-handle-event,
67 68
   defined on subr.el.

69 70
   Routines w32_read_socket or w32_console_read_socket process notifications
   sets as long as some are available.
71 72

   When the watch is removed by a call to w32notify-rm-watch, the main
73 74 75 76 77 78 79 80 81 82 83
   thread requests that the worker thread terminates by signaling the
   appropriate event and 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.  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.  */
84

Paul Eggert's avatar
Paul Eggert committed
85 86
#define DEFER_MS_W32_H
#include <config.h>
87

Eli Zaretskii's avatar
Eli Zaretskii committed
88 89 90
#include <stddef.h>
#include <errno.h>

Paul Eggert's avatar
Paul Eggert committed
91 92
/* Include CRT headers *before* ms-w32.h.  */
#include <ms-w32.h>
Eli Zaretskii's avatar
Eli Zaretskii committed
93 94 95 96 97

#include <windows.h>

#include "lisp.h"
#include "w32term.h"	/* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
98
#include "w32common.h"	/* for OS version data */
Eli Zaretskii's avatar
Eli Zaretskii committed
99 100 101 102 103 104
#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 */

105
#define DIRWATCH_BUFFER_SIZE 16384
106 107
#define DIRWATCH_SIGNATURE 0x01233210

Eli Zaretskii's avatar
Eli Zaretskii committed
108 109 110 111 112
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 */
113
  char *watchee;	/* the file we are interested in, UTF-8 encoded */
Eli Zaretskii's avatar
Eli Zaretskii committed
114 115
  HANDLE dir;		/* handle to the watched directory */
  HANDLE thr;		/* handle to the thread that watches */
116
  HANDLE terminate;     /* event signaling the thread to terminate */
117
  unsigned signature;
Eli Zaretskii's avatar
Eli Zaretskii committed
118 119 120
};

/* Used for communicating notifications to the main thread.  */
121
struct notifications_set *notifications_set_head;
Eli Zaretskii's avatar
Eli Zaretskii committed
122

123
static Lisp_Object watch_list;
Eli Zaretskii's avatar
Eli Zaretskii committed
124 125 126 127

/* Signal to the main thread that we have file notifications for it to
   process.  */
static void
128
send_notifications (struct notifications_set *ns)
Eli Zaretskii's avatar
Eli Zaretskii committed
129
{
Dmitry Antipov's avatar
Dmitry Antipov committed
130
  struct frame *f = SELECTED_FRAME ();
Eli Zaretskii's avatar
Eli Zaretskii committed
131

132 133 134
  /* We add the current notification set to the linked list.  Use the
     critical section to make sure only one thread will access the
     linked list. */
Eli Zaretskii's avatar
Eli Zaretskii committed
135
      enter_crit ();
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
  ns->next = notifications_set_head;
  ns->prev = notifications_set_head->prev;
  ns->prev->next = ns;
  notifications_set_head->prev = ns;
  leave_crit();

  /* 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);
  else if (FRAME_W32_P (f))
    PostMessage (FRAME_W32_WINDOW (f),
                 WM_EMACS_FILENOTIFY, 0, 0);
  /* When we are running in batch mode, there's no one to send a
     message, so we just signal the data is available and hope
     sys_select will be called soon and will read the data.  */
159
#if 0
160 161
  else if (FRAME_INITIAL_P (f) && noninteractive)
    ;
162
#endif
Eli Zaretskii's avatar
Eli Zaretskii committed
163 164 165 166 167 168 169
}

/* 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.)  */
170 171
VOID CALLBACK watch_end (ULONG_PTR);

Eli Zaretskii's avatar
Eli Zaretskii committed
172 173 174 175 176 177
VOID CALLBACK
watch_end (ULONG_PTR arg)
{
  HANDLE hdir = (HANDLE)arg;

  if (hdir && hdir != INVALID_HANDLE_VALUE)
178
    CancelIo (hdir);
Eli Zaretskii's avatar
Eli Zaretskii committed
179 180
}

181 182 183
/* 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
Eli Zaretskii's avatar
Eli Zaretskii committed
184
   "alertable state", i.e. waiting inside SleepEx call.  */
185 186
VOID CALLBACK watch_completion (DWORD, DWORD, OVERLAPPED *);

Eli Zaretskii's avatar
Eli Zaretskii committed
187 188 189 190
VOID CALLBACK
watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
{
  struct notification *dirwatch;
191 192 193
  DWORD _bytes;
  struct notifications_set *ns = NULL;
  BOOL terminate = FALSE;
Eli Zaretskii's avatar
Eli Zaretskii committed
194 195 196 197 198 199

  /* 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)
200 201 202 203
    {
      DebPrint(("watch_completion: io_info is null.\n"));
      return;
    }
Eli Zaretskii's avatar
Eli Zaretskii committed
204 205 206 207 208 209 210

  /* 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;
211

Eli Zaretskii's avatar
Eli Zaretskii committed
212 213 214 215
  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.
216 217 218
         We must exit, without issuing another call to
         ReadDirectoryChangesW. */
      return;
Eli Zaretskii's avatar
Eli Zaretskii committed
219
    }
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258

  /* We allocate a new set of notifications to be linked to the linked
     list of notifications set.  This will be processed by Emacs event
     loop in the main thread.  We need to duplicate the notifications
     buffer, but not the dirwatch structure.  */

  /* Implementation note: In general, allocating memory in non-main
     threads is a no-no in Emacs.  We certainly cannot call xmalloc
     and friends, because it can longjmp when allocation fails, which
     will crash Emacs because the jmp_buf is set up to a location on
     the main thread's stack.  However, we can call 'malloc' directly,
     since that is redirected to HeapAlloc that uses our private heap,
     see w32heap.c, and that is thread-safe.  */
  ns = malloc (sizeof(struct notifications_set));
  if (ns)
    {
      memset (ns, 0, sizeof(struct notifications_set));
      ns->notifications = malloc (bytes_ret);
      if (ns->notifications)
	{
	  memcpy (ns->notifications, dirwatch->buf, bytes_ret);
	  ns->size = bytes_ret;
	  ns->desc = dirwatch;
	}
      else
	{
	  free (ns);
	  ns = NULL;
	}
    }
  if (ns == NULL)
    DebPrint(("Out of memory.  Notifications lost."));

  /* Calling ReadDirectoryChangesW quickly to watch again for new
     notifications.  */
  if (!ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf,
			      DIRWATCH_BUFFER_SIZE, dirwatch->subtree,
			      dirwatch->filter, &_bytes, dirwatch->io_info,
			      watch_completion))
Eli Zaretskii's avatar
Eli Zaretskii committed
259
    {
260 261 262 263 264 265
      DebPrint (("ReadDirectoryChangesW error: %lu\n", GetLastError ()));
      /* If this call fails, it means that the directory is not
         watchable any more.  We need to terminate the worker thread.
         Still, we will wait until the current notifications have been
         sent to the main thread.  */
      terminate = TRUE;
Eli Zaretskii's avatar
Eli Zaretskii committed
266
    }
267 268 269 270 271 272 273

  if (ns)
    send_notifications(ns);

  /* If we were asked to terminate the thread, then fire the event. */
  if (terminate)
    SetEvent(dirwatch->terminate);
Eli Zaretskii's avatar
Eli Zaretskii committed
274 275 276 277 278 279 280
}

/* Worker routine for the watch thread.  */
static DWORD WINAPI
watch_worker (LPVOID arg)
{
  struct notification *dirwatch = (struct notification *)arg;
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
  BOOL bErr;
  DWORD _bytes = 0;
  DWORD status;

  if (dirwatch->dir)
    {
      bErr = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf,
				    DIRWATCH_BUFFER_SIZE, dirwatch->subtree,
				    dirwatch->filter, &_bytes,
				    dirwatch->io_info, watch_completion);
      if (!bErr)
	{
	  DebPrint (("ReadDirectoryChangesW: %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;
	}
    }
Eli Zaretskii's avatar
Eli Zaretskii committed
307 308

  do {
309 310 311 312 313 314
    status = WaitForSingleObjectEx(dirwatch->terminate, INFINITE, TRUE);
  } while (status == WAIT_IO_COMPLETION);

  /* The thread is about to terminate, so we clean up the dir handle.  */
  CloseHandle (dirwatch->dir);
  dirwatch->dir = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
315 316 317 318 319

  return 0;
}
/* Launch a thread to watch changes to FILE in a directory open on
   handle HDIR.  */
320
static struct notification *
Eli Zaretskii's avatar
Eli Zaretskii committed
321 322
start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
{
323 324 325
  struct notification *dirwatch = xzalloc (sizeof (struct notification));

  dirwatch->signature = DIRWATCH_SIGNATURE;
326
  dirwatch->buf = xmalloc (DIRWATCH_BUFFER_SIZE);
327
  dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
Eli Zaretskii's avatar
Eli Zaretskii committed
328 329 330 331
  /* 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." */
332 333 334 335
  dirwatch->io_info->hEvent = dirwatch;
  dirwatch->subtree = subdirs;
  dirwatch->filter = flags;
  dirwatch->watchee = xstrdup (file);
336 337 338

  dirwatch->terminate = CreateEvent(NULL, FALSE, FALSE, NULL);

339
  dirwatch->dir = hdir;
Eli Zaretskii's avatar
Eli Zaretskii committed
340 341 342

  /* See w32proc.c where it calls CreateThread for the story behind
     the 2nd and 5th argument in the call to CreateThread.  */
343 344
  dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
				0x00010000, NULL);
Eli Zaretskii's avatar
Eli Zaretskii committed
345

346
  if (!dirwatch->thr)
Eli Zaretskii's avatar
Eli Zaretskii committed
347
    {
348
      CloseHandle(dirwatch->terminate);
349 350 351 352
      xfree (dirwatch->buf);
      xfree (dirwatch->io_info);
      xfree (dirwatch->watchee);
      xfree (dirwatch);
Eli Zaretskii's avatar
Eli Zaretskii committed
353
    }
354
  return dirwatch;
Eli Zaretskii's avatar
Eli Zaretskii committed
355 356 357 358
}

/* Called from the main thread to start watching FILE in PARENT_DIR,
   subject to FLAGS.  If SUBDIRS is TRUE, watch the subdirectories of
359 360 361
   PARENT_DIR as well.  Value is a pointer to 'struct notification'
   used by the thread that watches the changes.  */
static struct notification *
Eli Zaretskii's avatar
Eli Zaretskii committed
362 363 364
add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
{
  HANDLE hdir;
365
  struct notification *dirwatch = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
366

367
  if (!file)
Eli Zaretskii's avatar
Eli Zaretskii committed
368 369
    return NULL;

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
  if (w32_unicode_filenames)
    {
      wchar_t dir_w[MAX_PATH], file_w[MAX_PATH];

      filename_to_utf16 (parent_dir, dir_w);
      if (*file)
	filename_to_utf16 (file, file_w);
      else
	file_w[0] = 0;

      hdir = CreateFileW (dir_w,
			  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);
    }
  else
    {
      char dir_a[MAX_PATH], file_a[MAX_PATH];

      filename_to_ansi (parent_dir, dir_a);
      if (*file)
	filename_to_ansi (file, file_a);
      else
	file_a[0] = '\0';

      hdir = CreateFileA (dir_a,
			  FILE_LIST_DIRECTORY,
			  FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
			  NULL, OPEN_EXISTING,
			  FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
			  NULL);
    }
Eli Zaretskii's avatar
Eli Zaretskii committed
407 408 409
  if (hdir == INVALID_HANDLE_VALUE)
    return NULL;

410
  if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
411 412 413 414
    {
      CloseHandle (hdir);
      dirwatch->dir = NULL;
    }
Eli Zaretskii's avatar
Eli Zaretskii committed
415

416
  return dirwatch;
Eli Zaretskii's avatar
Eli Zaretskii committed
417 418
}

419
/* Stop watching a directory specified by a pointer to its dirwatch object.  */
Eli Zaretskii's avatar
Eli Zaretskii committed
420
static int
421
remove_watch (struct notification *dirwatch)
Eli Zaretskii's avatar
Eli Zaretskii committed
422
{
423
  if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
Eli Zaretskii's avatar
Eli Zaretskii committed
424 425 426
    {
      int i;
      BOOL status;
427
      DWORD exit_code = 0, err = 0;
Eli Zaretskii's avatar
Eli Zaretskii committed
428 429 430 431 432

      /* 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.  */
433
      if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
Eli Zaretskii's avatar
Eli Zaretskii committed
434
	DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
435 436 437 438

      /* We also signal the thread that it can terminate.  */
      SetEvent(dirwatch->terminate);

Eli Zaretskii's avatar
Eli Zaretskii committed
439 440 441 442
      /* Wait for the thread to exit.  FIXME: is there a better method
	 that is not overly complex?  */
      for (i = 0; i < 50; i++)
	{
443
	  if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
Eli Zaretskii's avatar
Eli Zaretskii committed
444 445 446 447
		&& exit_code == STILL_ACTIVE))
	    break;
	  Sleep (10);
	}
448

Eli Zaretskii's avatar
Eli Zaretskii committed
449 450 451 452
      if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
	  || exit_code == STILL_ACTIVE)
	{
	  if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
453
	    {
454
              DebPrint(("Forcing thread termination.\n"));
455 456 457 458
	      TerminateThread (dirwatch->thr, 0);
	      if (dirwatch->dir)
		CloseHandle (dirwatch->dir);
	    }
Eli Zaretskii's avatar
Eli Zaretskii committed
459 460 461
	}

      /* Clean up.  */
462
      if (dirwatch->thr)
Eli Zaretskii's avatar
Eli Zaretskii committed
463
	{
464 465
	  CloseHandle (dirwatch->thr);
	  dirwatch->thr = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
466
	}
467
      CloseHandle(dirwatch->terminate);
468 469 470 471
      xfree (dirwatch->buf);
      xfree (dirwatch->io_info);
      xfree (dirwatch->watchee);
      xfree (dirwatch);
Eli Zaretskii's avatar
Eli Zaretskii committed
472 473 474 475
      return 0;
    }
  else
    {
476
      DebPrint (("Unknown dirwatch object!\n"));
Eli Zaretskii's avatar
Eli Zaretskii committed
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
      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.

516 517
Value is a descriptor for the added watch.  If the file cannot be
watched for some reason, this function signals a `file-error' error.
Eli Zaretskii's avatar
Eli Zaretskii committed
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549

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

550 551 552 553 554 555 556
FILE is the name of the file whose event is being reported.

Note that some networked filesystems, such as Samba-mounted Unix
volumes, might not send notifications about file changes.  In these
cases, this function will return a valid descriptor, but notifications
will never come in.  Volumes shared from remote Windows machines do
generate notifications correctly, though.  */)
Eli Zaretskii's avatar
Eli Zaretskii committed
557 558
  (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
{
559
  Lisp_Object dirfn, basefn, watch_object, watch_descriptor;
Eli Zaretskii's avatar
Eli Zaretskii committed
560 561
  DWORD flags;
  BOOL subdirs = FALSE;
562
  struct notification *dirwatch = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
563 564 565 566 567 568 569
  Lisp_Object lisp_errstr;
  char *errstr;

  CHECK_LIST (filter);

  /* The underlying features are available only since XP.  */
  if (os_subtype == OS_9X
570
      || (w32_major_version == 5 && w32_minor_version < 1))
Eli Zaretskii's avatar
Eli Zaretskii committed
571 572
    {
      errno = ENOSYS;
573 574
      report_file_notify_error ("Watching filesystem events is not supported",
				Qnil);
Eli Zaretskii's avatar
Eli Zaretskii committed
575 576
    }

577 578 579
  /* filenotify.el always passes us a directory, either the parent
     directory of a file to be watched, or the directory to be
     watched.  */
580 581
  file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
  if (NILP (Ffile_directory_p (file)))
582 583
    {
      /* This should only happen if we are called directly, not via
584 585 586 587 588
	 filenotify.el.  If BASEFN is empty, the argument was the root
	 directory on its drive.  */
      dirfn = ENCODE_FILE (Ffile_name_directory (file));
      basefn = ENCODE_FILE (Ffile_name_nondirectory (file));
      if (*SDATA (basefn) == '\0')
589 590
	subdirs = TRUE;
    }
591 592 593 594 595
  else
    {
      dirfn = ENCODE_FILE (file);
      basefn = Qnil;
    }
Eli Zaretskii's avatar
Eli Zaretskii committed
596 597 598 599 600 601

  if (!NILP (Fmember (Qsubtree, filter)))
    subdirs = TRUE;

  flags = filter_list_to_flags (filter);

602 603
  dirwatch = add_watch (SSDATA (dirfn), NILP (basefn) ? "" : SSDATA (basefn),
			subdirs, flags);
604
  if (!dirwatch)
Eli Zaretskii's avatar
Eli Zaretskii committed
605 606 607 608 609 610 611 612 613 614 615 616 617
    {
      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);
618 619
	  report_file_notify_error ("Cannot watch file",
				    Fcons (lisp_errstr, Fcons (file, Qnil)));
Eli Zaretskii's avatar
Eli Zaretskii committed
620 621
	}
      else
622
	report_file_notify_error ("Cannot watch file", Fcons (file, Qnil));
Eli Zaretskii's avatar
Eli Zaretskii committed
623 624
    }
  /* Store watch object in watch list. */
Eli Zaretskii's avatar
Eli Zaretskii committed
625
  watch_descriptor = make_mint_ptr (dirwatch);
Eli Zaretskii's avatar
Eli Zaretskii committed
626 627 628 629 630 631 632 633 634 635 636 637 638 639
  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;
640 641
  struct notification *dirwatch;
  int status = -1;
Eli Zaretskii's avatar
Eli Zaretskii committed
642

643 644 645
  /* 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.  */
646
  watch_object = Fassoc (watch_descriptor, watch_list, Qnil);
Eli Zaretskii's avatar
Eli Zaretskii committed
647
  if (!NILP (watch_object))
648 649
    {
      watch_list = Fdelete (watch_object, watch_list);
Eli Zaretskii's avatar
Eli Zaretskii committed
650
      dirwatch = (struct notification *)xmint_pointer (watch_descriptor);
651 652 653
      if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
	status = remove_watch (dirwatch);
    }
Eli Zaretskii's avatar
Eli Zaretskii committed
654

655
  if (status == -1)
656 657
    report_file_notify_error ("Invalid watch descriptor",
			      Fcons (watch_descriptor, Qnil));
658

Eli Zaretskii's avatar
Eli Zaretskii committed
659 660 661 662
  return Qnil;
}

Lisp_Object
663
w32_get_watch_object (void *desc)
664
{
Eli Zaretskii's avatar
Eli Zaretskii committed
665
  Lisp_Object descriptor = make_mint_ptr (desc);
666

667
  /* This is called from the input queue handling code, inside a
Paul Eggert's avatar
Paul Eggert committed
668
     critical section, so we cannot possibly quit if watch_list is not
669
     in the right condition.  */
670
  return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
671 672
}

Eli Zaretskii's avatar
Eli Zaretskii committed
673
DEFUN ("w32notify-valid-p", Fw32notify_valid_p, Sw32notify_valid_p, 1, 1, 0,
Michael Albinus's avatar
Michael Albinus committed
674
       doc: /* Check a watch specified by its WATCH-DESCRIPTOR for validity.
Eli Zaretskii's avatar
Eli Zaretskii committed
675 676 677 678

WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.

A watch can become invalid if the directory it watches is deleted, or if
679 680
the watcher thread exits abnormally for any other reason.  Removing the
watch by calling `w32notify-rm-watch' also makes it invalid.  */)
Eli Zaretskii's avatar
Eli Zaretskii committed
681 682
     (Lisp_Object watch_descriptor)
{
683
  Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list, Qnil);
Eli Zaretskii's avatar
Eli Zaretskii committed
684 685 686 687

  if (!NILP (watch_object))
    {
      struct notification *dirwatch =
Eli Zaretskii's avatar
Eli Zaretskii committed
688
	(struct notification *)xmint_pointer (watch_descriptor);
Eli Zaretskii's avatar
Eli Zaretskii committed
689 690 691 692 693 694 695 696
      if (w32_valid_pointer_p (dirwatch, sizeof(struct notification))
	  && dirwatch->dir != NULL)
	return Qt;
    }

  return Qnil;
}

697 698
void
globals_of_w32notify (void)
Eli Zaretskii's avatar
Eli Zaretskii committed
699
{
700
  watch_list = Qnil;
Eli Zaretskii's avatar
Eli Zaretskii committed
701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
}

void
syms_of_w32notify (void)
{
  DEFSYM (Qfile_name, "file-name");
  DEFSYM (Qdirectory_name, "directory-name");
  DEFSYM (Qattributes, "attributes");
  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);
Eli Zaretskii's avatar
Eli Zaretskii committed
717
  defsubr (&Sw32notify_valid_p);
Eli Zaretskii's avatar
Eli Zaretskii committed
718 719 720 721 722

  staticpro (&watch_list);

  Fprovide (intern_c_string ("w32notify"), Qnil);
}