1// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "platform/globals.h"
6#if defined(HOST_OS_WINDOWS)
7
8#include "bin/file_system_watcher.h"
9
10#include <WinIoCtl.h> // NOLINT
11
12#include "bin/builtin.h"
13#include "bin/eventhandler.h"
14#include "bin/utils.h"
15#include "bin/utils_win.h"
16#include "platform/syslog.h"
17
18namespace dart {
19namespace bin {
20
21bool FileSystemWatcher::IsSupported() {
22 return true;
23}
24
25intptr_t FileSystemWatcher::Init() {
26 return 0;
27}
28
29void FileSystemWatcher::Close(intptr_t id) {
30 USE(id);
31}
32
33intptr_t FileSystemWatcher::WatchPath(intptr_t id,
34 Namespace* namespc,
35 const char* path,
36 int events,
37 bool recursive) {
38 USE(id);
39 Utf8ToWideScope name(path);
40 HANDLE dir = CreateFileW(
41 name.wide(), FILE_LIST_DIRECTORY,
42 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
43 OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
44
45 if (dir == INVALID_HANDLE_VALUE) {
46 return -1;
47 }
48
49 int list_events = 0;
50 if ((events & (kCreate | kMove | kDelete)) != 0) {
51 list_events |= FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME;
52 }
53 if ((events & kModifyContent) != 0) {
54 list_events |= FILE_NOTIFY_CHANGE_LAST_WRITE;
55 }
56
57 DirectoryWatchHandle* handle =
58 new DirectoryWatchHandle(dir, list_events, recursive);
59 // Issue a read directly, to be sure events are tracked from now on. This is
60 // okay, since in Dart, we create the socket and start reading immidiately.
61 handle->EnsureInitialized(EventHandler::delegate());
62 handle->IssueRead();
63 return reinterpret_cast<intptr_t>(handle);
64}
65
66void FileSystemWatcher::UnwatchPath(intptr_t id, intptr_t path_id) {
67 USE(id);
68 DirectoryWatchHandle* handle =
69 reinterpret_cast<DirectoryWatchHandle*>(path_id);
70 handle->Stop();
71}
72
73intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) {
74 USE(id);
75 return path_id;
76}
77
78Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id, intptr_t path_id) {
79 USE(id);
80 const intptr_t kEventSize = sizeof(FILE_NOTIFY_INFORMATION);
81 DirectoryWatchHandle* dir = reinterpret_cast<DirectoryWatchHandle*>(path_id);
82 intptr_t available = dir->Available();
83 intptr_t max_count = available / kEventSize + 1;
84 Dart_Handle events = Dart_NewList(max_count);
85 uint8_t* buffer = Dart_ScopeAllocate(available);
86 intptr_t bytes = dir->Read(buffer, available);
87 intptr_t offset = 0;
88 intptr_t i = 0;
89 while (offset < bytes) {
90 FILE_NOTIFY_INFORMATION* e =
91 reinterpret_cast<FILE_NOTIFY_INFORMATION*>(buffer + offset);
92
93 Dart_Handle event = Dart_NewList(5);
94 int mask = 0;
95 if (e->Action == FILE_ACTION_ADDED) {
96 mask |= kCreate;
97 }
98 if (e->Action == FILE_ACTION_REMOVED) {
99 mask |= kDelete;
100 }
101 if (e->Action == FILE_ACTION_MODIFIED) {
102 mask |= kModifyContent;
103 }
104 if (e->Action == FILE_ACTION_RENAMED_OLD_NAME) {
105 mask |= kMove;
106 }
107 if (e->Action == FILE_ACTION_RENAMED_NEW_NAME) {
108 mask |= kMove;
109 }
110 Dart_ListSetAt(event, 0, Dart_NewInteger(mask));
111 // Move events come in pairs. Just 'enable' by default.
112 Dart_ListSetAt(event, 1, Dart_NewInteger(1));
113 Dart_ListSetAt(
114 event, 2,
115 Dart_NewStringFromUTF16(reinterpret_cast<uint16_t*>(e->FileName),
116 e->FileNameLength / 2));
117 Dart_ListSetAt(event, 3, Dart_NewBoolean(true));
118 Dart_ListSetAt(event, 4, Dart_NewInteger(path_id));
119 Dart_ListSetAt(events, i, event);
120 i++;
121 if (e->NextEntryOffset == 0) {
122 break;
123 }
124 offset += e->NextEntryOffset;
125 }
126 return events;
127}
128
129} // namespace bin
130} // namespace dart
131
132#endif // defined(HOST_OS_WINDOWS)
133