wdm 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -21
- data/README.md +206 -206
- data/ext/wdm/entry.c +71 -71
- data/ext/wdm/entry.h +46 -46
- data/ext/wdm/extconf.rb +28 -27
- data/ext/wdm/memory.c +26 -26
- data/ext/wdm/memory.h +29 -29
- data/ext/wdm/monitor.c +74 -74
- data/ext/wdm/monitor.h +49 -49
- data/ext/wdm/queue.c +195 -195
- data/ext/wdm/queue.h +72 -72
- data/ext/wdm/rb_change.c +198 -198
- data/ext/wdm/rb_change.h +27 -27
- data/ext/wdm/rb_monitor.c +574 -570
- data/ext/wdm/rb_monitor.h +39 -39
- data/ext/wdm/utils.c +76 -76
- data/ext/wdm/utils.h +25 -25
- data/ext/wdm/wdm.c +46 -46
- data/ext/wdm/wdm.h +81 -71
- data/ext/wdm/wdm.sln +20 -20
- data/ext/wdm/wdm.vcxproj +105 -105
- data/ext/wdm/wdm.vcxproj.filters +73 -73
- data/lib/wdm.rb +9 -9
- metadata +20 -64
data/ext/wdm/rb_change.h
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
#ifndef WDM_RB_CHANGE_H
|
2
|
-
#define WDM_RB_CHANGE_H
|
3
|
-
|
4
|
-
#ifdef __cplusplus
|
5
|
-
extern "C" {
|
6
|
-
#endif // __cplusplus
|
7
|
-
|
8
|
-
// ----------------------------------------------------------
|
9
|
-
// Global variables
|
10
|
-
// ----------------------------------------------------------
|
11
|
-
|
12
|
-
extern VALUE cWDM_Change;
|
13
|
-
|
14
|
-
// ---------------------------------------------------------
|
15
|
-
// Prototypes
|
16
|
-
// ---------------------------------------------------------
|
17
|
-
|
18
|
-
VALUE wdm_rb_change_new_from_notification(const LPWSTR, const PFILE_NOTIFY_INFORMATION);
|
19
|
-
|
20
|
-
void wdm_rb_change_init();
|
21
|
-
|
22
|
-
// ---------------------------------------------------------
|
23
|
-
|
24
|
-
#ifdef __cplusplus
|
25
|
-
}
|
26
|
-
#endif // __cplusplus
|
27
|
-
|
1
|
+
#ifndef WDM_RB_CHANGE_H
|
2
|
+
#define WDM_RB_CHANGE_H
|
3
|
+
|
4
|
+
#ifdef __cplusplus
|
5
|
+
extern "C" {
|
6
|
+
#endif // __cplusplus
|
7
|
+
|
8
|
+
// ----------------------------------------------------------
|
9
|
+
// Global variables
|
10
|
+
// ----------------------------------------------------------
|
11
|
+
|
12
|
+
extern VALUE cWDM_Change;
|
13
|
+
|
14
|
+
// ---------------------------------------------------------
|
15
|
+
// Prototypes
|
16
|
+
// ---------------------------------------------------------
|
17
|
+
|
18
|
+
VALUE wdm_rb_change_new_from_notification(const LPWSTR, const PFILE_NOTIFY_INFORMATION);
|
19
|
+
|
20
|
+
void wdm_rb_change_init();
|
21
|
+
|
22
|
+
// ---------------------------------------------------------
|
23
|
+
|
24
|
+
#ifdef __cplusplus
|
25
|
+
}
|
26
|
+
#endif // __cplusplus
|
27
|
+
|
28
28
|
#endif // WDM_RB_CHANGE_H
|
data/ext/wdm/rb_monitor.c
CHANGED
@@ -1,571 +1,575 @@
|
|
1
|
-
#include "wdm.h"
|
2
|
-
|
3
|
-
#include "utils.h"
|
4
|
-
#include "entry.h"
|
5
|
-
#include "queue.h"
|
6
|
-
#include "monitor.h"
|
7
|
-
|
8
|
-
#include "rb_change.h"
|
9
|
-
#include "rb_monitor.h"
|
10
|
-
|
11
|
-
// ----------------------------------------------------------
|
12
|
-
// Global variables
|
13
|
-
// ----------------------------------------------------------
|
14
|
-
|
15
|
-
VALUE cWDM_Monitor;
|
16
|
-
|
17
|
-
VALUE eWDM_UnknownFlagError;
|
18
|
-
|
19
|
-
// ----------------------------------------------------------
|
20
|
-
// Cached variables
|
21
|
-
// ----------------------------------------------------------
|
22
|
-
|
23
|
-
static ID wdm_rb_sym_call;
|
24
|
-
static ID wdm_rb_sym_files;
|
25
|
-
static ID wdm_rb_sym_directories;
|
26
|
-
static ID wdm_rb_sym_attributes;
|
27
|
-
static ID wdm_rb_sym_size;
|
28
|
-
static ID wdm_rb_sym_last_write;
|
29
|
-
static ID wdm_rb_sym_last_access;
|
30
|
-
static ID wdm_rb_sym_creation;
|
31
|
-
static ID wdm_rb_sym_security;
|
32
|
-
static ID wdm_rb_sym_default;
|
33
|
-
|
34
|
-
// ---------------------------------------------------------
|
35
|
-
// Prototypes of static functions
|
36
|
-
// ---------------------------------------------------------
|
37
|
-
|
38
|
-
static void monitor_mark(LPVOID);
|
39
|
-
static void monitor_free(LPVOID);
|
40
|
-
static VALUE rb_monitor_alloc(VALUE);
|
41
|
-
|
42
|
-
static DWORD id_to_flag(ID);
|
43
|
-
static DWORD extract_flags_from_rb_array(VALUE);
|
44
|
-
static VALUE combined_watch(BOOL, int, VALUE*, VALUE);
|
45
|
-
static VALUE rb_monitor_watch(int, VALUE*, VALUE);
|
46
|
-
|
47
|
-
static VALUE rb_monitor_watch_recursively(int, VALUE*, VALUE);
|
48
|
-
|
49
|
-
static void CALLBACK handle_entry_change(DWORD, DWORD, LPOVERLAPPED);
|
50
|
-
static BOOL register_monitoring_entry(WDM_PEntry);
|
51
|
-
static DWORD WINAPI start_monitoring(LPVOID);
|
52
|
-
|
53
|
-
static VALUE wait_for_changes(LPVOID);
|
54
|
-
static void process_changes(WDM_PQueue);
|
55
|
-
static void stop_monitoring(LPVOID);
|
56
|
-
static VALUE rb_monitor_run_bang(VALUE);
|
57
|
-
|
58
|
-
static VALUE rb_monitor_stop(VALUE);
|
59
|
-
|
60
|
-
// ----------------------------------------------------------
|
61
|
-
|
62
|
-
static void
|
63
|
-
monitor_mark(LPVOID param)
|
64
|
-
{
|
65
|
-
WDM_PMonitor monitor;
|
66
|
-
WDM_PEntry entry;
|
67
|
-
|
68
|
-
monitor = (WDM_PMonitor)param;
|
69
|
-
entry = monitor->head;
|
70
|
-
|
71
|
-
while(entry != NULL) {
|
72
|
-
rb_gc_mark(entry->user_data->callback);
|
73
|
-
entry = entry->next;
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
static void
|
78
|
-
monitor_free(LPVOID param)
|
79
|
-
{
|
80
|
-
WDM_PMonitor monitor;
|
81
|
-
WDM_PEntry entry;
|
82
|
-
|
83
|
-
WDM_DEBUG("Freeing a monitor object!");
|
84
|
-
|
85
|
-
monitor = (WDM_PMonitor)param;
|
86
|
-
entry = monitor->head;
|
87
|
-
|
88
|
-
stop_monitoring(monitor); // If the monitor is already stopped, it would do nothing
|
89
|
-
|
90
|
-
while(entry != NULL) {
|
91
|
-
if ( entry->event_container.hEvent != NULL ) {
|
92
|
-
wdm_monitor_callback_param_free(
|
93
|
-
(WDM_PMonitorCallbackParam)entry->event_container.hEvent
|
94
|
-
);
|
95
|
-
}
|
96
|
-
entry = entry->next;
|
97
|
-
}
|
98
|
-
|
99
|
-
wdm_monitor_free(monitor);
|
100
|
-
}
|
101
|
-
|
102
|
-
static VALUE
|
103
|
-
rb_monitor_alloc(VALUE self)
|
104
|
-
{
|
105
|
-
WDM_DEBUG("--------------------------------");
|
106
|
-
WDM_DEBUG("Allocating a new monitor object!");
|
107
|
-
WDM_DEBUG("--------------------------------");
|
108
|
-
|
109
|
-
return Data_Wrap_Struct(self, monitor_mark, monitor_free, wdm_monitor_new());
|
110
|
-
}
|
111
|
-
|
112
|
-
static DWORD
|
113
|
-
id_to_flag(ID id)
|
114
|
-
{
|
115
|
-
if ( id == wdm_rb_sym_default ) return WDM_MONITOR_FLAGS_DEFAULT;
|
116
|
-
|
117
|
-
// TODO: Maybe reorder the if's in the frequency of use for better performance?
|
118
|
-
if ( id == wdm_rb_sym_files ) return FILE_NOTIFY_CHANGE_FILE_NAME;
|
119
|
-
if ( id == wdm_rb_sym_directories ) return FILE_NOTIFY_CHANGE_DIR_NAME;
|
120
|
-
if ( id == wdm_rb_sym_attributes ) return FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
121
|
-
if ( id == wdm_rb_sym_size ) return FILE_NOTIFY_CHANGE_SIZE;
|
122
|
-
if ( id == wdm_rb_sym_last_write ) return FILE_NOTIFY_CHANGE_LAST_WRITE;
|
123
|
-
if ( id == wdm_rb_sym_last_access ) return FILE_NOTIFY_CHANGE_LAST_ACCESS;
|
124
|
-
if ( id == wdm_rb_sym_creation ) return FILE_NOTIFY_CHANGE_CREATION;
|
125
|
-
if ( id == wdm_rb_sym_security ) return FILE_NOTIFY_CHANGE_SECURITY;
|
126
|
-
|
127
|
-
rb_raise(eWDM_UnknownFlagError, "Unknown watch flag: ':%s'", rb_id2name(id));
|
128
|
-
}
|
129
|
-
|
130
|
-
static DWORD
|
131
|
-
extract_flags_from_rb_array(VALUE flags_array)
|
132
|
-
{
|
133
|
-
VALUE flag_symbol;
|
134
|
-
DWORD flags;
|
135
|
-
|
136
|
-
flags = 0;
|
137
|
-
|
138
|
-
while ( RARRAY_LEN(flags_array) != 0 ) {
|
139
|
-
flag_symbol = rb_ary_pop(flags_array);
|
140
|
-
Check_Type(flag_symbol, T_SYMBOL);
|
141
|
-
flags |= id_to_flag( SYM2ID(flag_symbol) );
|
142
|
-
}
|
143
|
-
|
144
|
-
return flags;
|
145
|
-
}
|
146
|
-
|
147
|
-
static VALUE
|
148
|
-
combined_watch(BOOL recursively, int argc, VALUE *argv, VALUE self)
|
149
|
-
{
|
150
|
-
WDM_PMonitor monitor;
|
151
|
-
WDM_PEntry entry;
|
152
|
-
int directory_letters_count;
|
153
|
-
VALUE directory, flags, os_encoded_directory;
|
154
|
-
BOOL running;
|
155
|
-
|
156
|
-
// TODO: Maybe raise a more user-friendly error?
|
157
|
-
rb_need_block();
|
158
|
-
|
159
|
-
Data_Get_Struct(self, WDM_Monitor, monitor);
|
160
|
-
|
161
|
-
EnterCriticalSection(&monitor->lock);
|
162
|
-
running = monitor->running;
|
163
|
-
LeaveCriticalSection(&monitor->lock);
|
164
|
-
|
165
|
-
if ( running ) {
|
166
|
-
rb_raise(eWDM_MonitorRunningError, "You can't watch new directories while the monitor is running!");
|
167
|
-
}
|
168
|
-
|
169
|
-
rb_scan_args(argc, argv, "1*", &directory, &flags);
|
170
|
-
|
171
|
-
Check_Type(directory, T_STRING);
|
172
|
-
|
173
|
-
entry = wdm_entry_new();
|
174
|
-
entry->user_data->watch_childeren = recursively;
|
175
|
-
entry->user_data->callback = rb_block_proc();
|
176
|
-
entry->user_data->flags = RARRAY_LEN(flags) == 0 ? WDM_MONITOR_FLAGS_DEFAULT : extract_flags_from_rb_array(flags);
|
177
|
-
|
178
|
-
// WTF Ruby source: The original code (file.c) uses the following macro to make sure that the encoding
|
179
|
-
// of the string is ASCII-compatible, but UTF-16LE (Windows default encoding) is not!!!
|
180
|
-
//
|
181
|
-
// FilePathValue(directory);
|
182
|
-
|
183
|
-
os_encoded_directory = rb_str_encode_ospath(directory);
|
184
|
-
|
185
|
-
// RSTRING_LEN can't be used because it would return the count of bytes the string uses in its encoding (like UTF-8).
|
186
|
-
// UTF-8 might use more than one byte for the char, which is not needed for WCHAR strings.
|
187
|
-
// Also, the result of MultiByteToWideChar _includes_ the NULL char at the end, which is not true for RSTRING.
|
188
|
-
//
|
189
|
-
// Example: 'C:\Users\Maher\Desktop\تجربة' with __ENCODING__ == UTF-8
|
190
|
-
// MultiByteToWideChar => 29 (28-char + null)
|
191
|
-
// RSTRING_LEN => 33 (23-char + 10-bytes for 5 Arabic letters which take 2 bytes each)
|
192
|
-
//
|
193
|
-
directory_letters_count = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(os_encoded_directory), -1, NULL, 0);
|
194
|
-
|
195
|
-
entry->user_data->dir = ALLOCA_N(WCHAR, directory_letters_count);
|
196
|
-
|
197
|
-
MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(os_encoded_directory), -1, entry->user_data->dir, directory_letters_count);
|
198
|
-
|
199
|
-
WDM_WDEBUG("New path to watch: '%s'", entry->user_data->dir);
|
200
|
-
|
201
|
-
entry->user_data->dir = wdm_utils_full_pathname(entry->user_data->dir);
|
202
|
-
|
203
|
-
if ( entry->user_data->dir == 0 ) {
|
204
|
-
wdm_entry_free(entry);
|
205
|
-
rb_raise(eWDM_Error, "Can't get the absolute path for the passed directory: '%s'!", RSTRING_PTR(directory));
|
206
|
-
}
|
207
|
-
|
208
|
-
if ( ! wdm_utils_unicode_is_directory(entry->user_data->dir) ) {
|
209
|
-
wdm_entry_free(entry);
|
210
|
-
rb_raise(eWDM_InvalidDirectoryError, "No such directory: '%s'!", RSTRING_PTR(directory));
|
211
|
-
}
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
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
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
//
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
data_to_process->data->
|
287
|
-
|
288
|
-
//
|
289
|
-
|
290
|
-
|
291
|
-
//
|
292
|
-
|
293
|
-
|
294
|
-
//
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
entry->
|
312
|
-
entry->
|
313
|
-
|
314
|
-
entry->
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
WDM_PMonitor
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
);
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
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
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
WDM_PMonitor
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
}
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
}
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
0, // use default
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
if (
|
515
|
-
|
516
|
-
|
517
|
-
|
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
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
1
|
+
#include "wdm.h"
|
2
|
+
|
3
|
+
#include "utils.h"
|
4
|
+
#include "entry.h"
|
5
|
+
#include "queue.h"
|
6
|
+
#include "monitor.h"
|
7
|
+
|
8
|
+
#include "rb_change.h"
|
9
|
+
#include "rb_monitor.h"
|
10
|
+
|
11
|
+
// ----------------------------------------------------------
|
12
|
+
// Global variables
|
13
|
+
// ----------------------------------------------------------
|
14
|
+
|
15
|
+
VALUE cWDM_Monitor;
|
16
|
+
|
17
|
+
VALUE eWDM_UnknownFlagError;
|
18
|
+
|
19
|
+
// ----------------------------------------------------------
|
20
|
+
// Cached variables
|
21
|
+
// ----------------------------------------------------------
|
22
|
+
|
23
|
+
static ID wdm_rb_sym_call;
|
24
|
+
static ID wdm_rb_sym_files;
|
25
|
+
static ID wdm_rb_sym_directories;
|
26
|
+
static ID wdm_rb_sym_attributes;
|
27
|
+
static ID wdm_rb_sym_size;
|
28
|
+
static ID wdm_rb_sym_last_write;
|
29
|
+
static ID wdm_rb_sym_last_access;
|
30
|
+
static ID wdm_rb_sym_creation;
|
31
|
+
static ID wdm_rb_sym_security;
|
32
|
+
static ID wdm_rb_sym_default;
|
33
|
+
|
34
|
+
// ---------------------------------------------------------
|
35
|
+
// Prototypes of static functions
|
36
|
+
// ---------------------------------------------------------
|
37
|
+
|
38
|
+
static void monitor_mark(LPVOID);
|
39
|
+
static void monitor_free(LPVOID);
|
40
|
+
static VALUE rb_monitor_alloc(VALUE);
|
41
|
+
|
42
|
+
static DWORD id_to_flag(ID);
|
43
|
+
static DWORD extract_flags_from_rb_array(VALUE);
|
44
|
+
static VALUE combined_watch(BOOL, int, VALUE*, VALUE);
|
45
|
+
static VALUE rb_monitor_watch(int, VALUE*, VALUE);
|
46
|
+
|
47
|
+
static VALUE rb_monitor_watch_recursively(int, VALUE*, VALUE);
|
48
|
+
|
49
|
+
static void CALLBACK handle_entry_change(DWORD, DWORD, LPOVERLAPPED);
|
50
|
+
static BOOL register_monitoring_entry(WDM_PEntry);
|
51
|
+
static DWORD WINAPI start_monitoring(LPVOID);
|
52
|
+
|
53
|
+
static VALUE wait_for_changes(LPVOID);
|
54
|
+
static void process_changes(WDM_PQueue);
|
55
|
+
static void stop_monitoring(LPVOID);
|
56
|
+
static VALUE rb_monitor_run_bang(VALUE);
|
57
|
+
|
58
|
+
static VALUE rb_monitor_stop(VALUE);
|
59
|
+
|
60
|
+
// ----------------------------------------------------------
|
61
|
+
|
62
|
+
static void
|
63
|
+
monitor_mark(LPVOID param)
|
64
|
+
{
|
65
|
+
WDM_PMonitor monitor;
|
66
|
+
WDM_PEntry entry;
|
67
|
+
|
68
|
+
monitor = (WDM_PMonitor)param;
|
69
|
+
entry = monitor->head;
|
70
|
+
|
71
|
+
while(entry != NULL) {
|
72
|
+
rb_gc_mark(entry->user_data->callback);
|
73
|
+
entry = entry->next;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
static void
|
78
|
+
monitor_free(LPVOID param)
|
79
|
+
{
|
80
|
+
WDM_PMonitor monitor;
|
81
|
+
WDM_PEntry entry;
|
82
|
+
|
83
|
+
WDM_DEBUG("Freeing a monitor object!");
|
84
|
+
|
85
|
+
monitor = (WDM_PMonitor)param;
|
86
|
+
entry = monitor->head;
|
87
|
+
|
88
|
+
stop_monitoring(monitor); // If the monitor is already stopped, it would do nothing
|
89
|
+
|
90
|
+
while(entry != NULL) {
|
91
|
+
if ( entry->event_container.hEvent != NULL ) {
|
92
|
+
wdm_monitor_callback_param_free(
|
93
|
+
(WDM_PMonitorCallbackParam)entry->event_container.hEvent
|
94
|
+
);
|
95
|
+
}
|
96
|
+
entry = entry->next;
|
97
|
+
}
|
98
|
+
|
99
|
+
wdm_monitor_free(monitor);
|
100
|
+
}
|
101
|
+
|
102
|
+
static VALUE
|
103
|
+
rb_monitor_alloc(VALUE self)
|
104
|
+
{
|
105
|
+
WDM_DEBUG("--------------------------------");
|
106
|
+
WDM_DEBUG("Allocating a new monitor object!");
|
107
|
+
WDM_DEBUG("--------------------------------");
|
108
|
+
|
109
|
+
return Data_Wrap_Struct(self, monitor_mark, monitor_free, wdm_monitor_new());
|
110
|
+
}
|
111
|
+
|
112
|
+
static DWORD
|
113
|
+
id_to_flag(ID id)
|
114
|
+
{
|
115
|
+
if ( id == wdm_rb_sym_default ) return WDM_MONITOR_FLAGS_DEFAULT;
|
116
|
+
|
117
|
+
// TODO: Maybe reorder the if's in the frequency of use for better performance?
|
118
|
+
if ( id == wdm_rb_sym_files ) return FILE_NOTIFY_CHANGE_FILE_NAME;
|
119
|
+
if ( id == wdm_rb_sym_directories ) return FILE_NOTIFY_CHANGE_DIR_NAME;
|
120
|
+
if ( id == wdm_rb_sym_attributes ) return FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
121
|
+
if ( id == wdm_rb_sym_size ) return FILE_NOTIFY_CHANGE_SIZE;
|
122
|
+
if ( id == wdm_rb_sym_last_write ) return FILE_NOTIFY_CHANGE_LAST_WRITE;
|
123
|
+
if ( id == wdm_rb_sym_last_access ) return FILE_NOTIFY_CHANGE_LAST_ACCESS;
|
124
|
+
if ( id == wdm_rb_sym_creation ) return FILE_NOTIFY_CHANGE_CREATION;
|
125
|
+
if ( id == wdm_rb_sym_security ) return FILE_NOTIFY_CHANGE_SECURITY;
|
126
|
+
|
127
|
+
rb_raise(eWDM_UnknownFlagError, "Unknown watch flag: ':%s'", rb_id2name(id));
|
128
|
+
}
|
129
|
+
|
130
|
+
static DWORD
|
131
|
+
extract_flags_from_rb_array(VALUE flags_array)
|
132
|
+
{
|
133
|
+
VALUE flag_symbol;
|
134
|
+
DWORD flags;
|
135
|
+
|
136
|
+
flags = 0;
|
137
|
+
|
138
|
+
while ( RARRAY_LEN(flags_array) != 0 ) {
|
139
|
+
flag_symbol = rb_ary_pop(flags_array);
|
140
|
+
Check_Type(flag_symbol, T_SYMBOL);
|
141
|
+
flags |= id_to_flag( SYM2ID(flag_symbol) );
|
142
|
+
}
|
143
|
+
|
144
|
+
return flags;
|
145
|
+
}
|
146
|
+
|
147
|
+
static VALUE
|
148
|
+
combined_watch(BOOL recursively, int argc, VALUE *argv, VALUE self)
|
149
|
+
{
|
150
|
+
WDM_PMonitor monitor;
|
151
|
+
WDM_PEntry entry;
|
152
|
+
int directory_letters_count;
|
153
|
+
VALUE directory, flags, os_encoded_directory;
|
154
|
+
BOOL running;
|
155
|
+
|
156
|
+
// TODO: Maybe raise a more user-friendly error?
|
157
|
+
rb_need_block();
|
158
|
+
|
159
|
+
Data_Get_Struct(self, WDM_Monitor, monitor);
|
160
|
+
|
161
|
+
EnterCriticalSection(&monitor->lock);
|
162
|
+
running = monitor->running;
|
163
|
+
LeaveCriticalSection(&monitor->lock);
|
164
|
+
|
165
|
+
if ( running ) {
|
166
|
+
rb_raise(eWDM_MonitorRunningError, "You can't watch new directories while the monitor is running!");
|
167
|
+
}
|
168
|
+
|
169
|
+
rb_scan_args(argc, argv, "1*", &directory, &flags);
|
170
|
+
|
171
|
+
Check_Type(directory, T_STRING);
|
172
|
+
|
173
|
+
entry = wdm_entry_new();
|
174
|
+
entry->user_data->watch_childeren = recursively;
|
175
|
+
entry->user_data->callback = rb_block_proc();
|
176
|
+
entry->user_data->flags = RARRAY_LEN(flags) == 0 ? WDM_MONITOR_FLAGS_DEFAULT : extract_flags_from_rb_array(flags);
|
177
|
+
|
178
|
+
// WTF Ruby source: The original code (file.c) uses the following macro to make sure that the encoding
|
179
|
+
// of the string is ASCII-compatible, but UTF-16LE (Windows default encoding) is not!!!
|
180
|
+
//
|
181
|
+
// FilePathValue(directory);
|
182
|
+
|
183
|
+
os_encoded_directory = rb_str_encode_ospath(directory);
|
184
|
+
|
185
|
+
// RSTRING_LEN can't be used because it would return the count of bytes the string uses in its encoding (like UTF-8).
|
186
|
+
// UTF-8 might use more than one byte for the char, which is not needed for WCHAR strings.
|
187
|
+
// Also, the result of MultiByteToWideChar _includes_ the NULL char at the end, which is not true for RSTRING.
|
188
|
+
//
|
189
|
+
// Example: 'C:\Users\Maher\Desktop\تجربة' with __ENCODING__ == UTF-8
|
190
|
+
// MultiByteToWideChar => 29 (28-char + null)
|
191
|
+
// RSTRING_LEN => 33 (23-char + 10-bytes for 5 Arabic letters which take 2 bytes each)
|
192
|
+
//
|
193
|
+
directory_letters_count = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(os_encoded_directory), -1, NULL, 0);
|
194
|
+
|
195
|
+
entry->user_data->dir = ALLOCA_N(WCHAR, directory_letters_count);
|
196
|
+
|
197
|
+
MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(os_encoded_directory), -1, entry->user_data->dir, directory_letters_count);
|
198
|
+
|
199
|
+
WDM_WDEBUG("New path to watch: '%s'", entry->user_data->dir);
|
200
|
+
|
201
|
+
entry->user_data->dir = wdm_utils_full_pathname(entry->user_data->dir);
|
202
|
+
|
203
|
+
if ( entry->user_data->dir == 0 ) {
|
204
|
+
wdm_entry_free(entry);
|
205
|
+
rb_raise(eWDM_Error, "Can't get the absolute path for the passed directory: '%s'!", RSTRING_PTR(directory));
|
206
|
+
}
|
207
|
+
|
208
|
+
if ( ! wdm_utils_unicode_is_directory(entry->user_data->dir) ) {
|
209
|
+
wdm_entry_free(entry);
|
210
|
+
rb_raise(eWDM_InvalidDirectoryError, "No such directory: '%s'!", RSTRING_PTR(directory));
|
211
|
+
}
|
212
|
+
|
213
|
+
entry->dir_handle = CreateFileW(
|
214
|
+
entry->user_data->dir, // pointer to the file name
|
215
|
+
FILE_LIST_DIRECTORY, // access (read/write) mode
|
216
|
+
FILE_SHARE_READ // share mode
|
217
|
+
| FILE_SHARE_WRITE
|
218
|
+
| FILE_SHARE_DELETE,
|
219
|
+
NULL, // security descriptor
|
220
|
+
OPEN_EXISTING, // how to create
|
221
|
+
FILE_FLAG_BACKUP_SEMANTICS
|
222
|
+
| FILE_FLAG_OVERLAPPED, // file attributes
|
223
|
+
NULL
|
224
|
+
);
|
225
|
+
|
226
|
+
if ( entry->dir_handle == INVALID_HANDLE_VALUE ) {
|
227
|
+
wdm_entry_free(entry);
|
228
|
+
rb_raise(eWDM_Error, "Can't watch directory: '%s'!", RSTRING_PTR(directory));
|
229
|
+
}
|
230
|
+
|
231
|
+
// Store a reference to the entry instead of an event as the event
|
232
|
+
// won't be used when using callbacks.
|
233
|
+
entry->event_container.hEvent = wdm_monitor_callback_param_new(monitor, entry);
|
234
|
+
|
235
|
+
wdm_monitor_update_head(monitor, entry);
|
236
|
+
|
237
|
+
WDM_WDEBUG("Watching directory: '%s'", entry->user_data->dir);
|
238
|
+
|
239
|
+
return Qnil;
|
240
|
+
}
|
241
|
+
|
242
|
+
static VALUE
|
243
|
+
rb_monitor_watch(int argc, VALUE *argv, VALUE self)
|
244
|
+
{
|
245
|
+
return combined_watch(FALSE, argc, argv, self);
|
246
|
+
}
|
247
|
+
|
248
|
+
static VALUE
|
249
|
+
rb_monitor_watch_recursively(int argc, VALUE *argv, VALUE self)
|
250
|
+
{
|
251
|
+
return combined_watch(TRUE, argc, argv, self);
|
252
|
+
}
|
253
|
+
|
254
|
+
static void CALLBACK
|
255
|
+
handle_entry_change(
|
256
|
+
DWORD err_code, // completion code
|
257
|
+
DWORD bytes_transfered, // number of bytes transferred
|
258
|
+
LPOVERLAPPED event_container // I/O information buffer
|
259
|
+
) {
|
260
|
+
WDM_PMonitorCallbackParam param;
|
261
|
+
WDM_PQueueItem data_to_process;
|
262
|
+
|
263
|
+
if ( err_code == ERROR_OPERATION_ABORTED ) {
|
264
|
+
// Async operation was canceled. This shouldn't happen.
|
265
|
+
// TODO:
|
266
|
+
// 1. Maybe add a union in the queue for errors?
|
267
|
+
// 2. What's the best action when this happens?
|
268
|
+
WDM_DEBUG("Dir handler closed in the process callback!");
|
269
|
+
return;
|
270
|
+
}
|
271
|
+
|
272
|
+
if ( ! bytes_transfered ) {
|
273
|
+
WDM_DEBUG("Buffer overflow?! Changes are bigger than the buffer!");
|
274
|
+
return;
|
275
|
+
}
|
276
|
+
|
277
|
+
param = (WDM_PMonitorCallbackParam)event_container->hEvent;
|
278
|
+
data_to_process = wdm_queue_item_new(WDM_QUEUE_ITEM_TYPE_DATA);
|
279
|
+
data_to_process->data = wdm_queue_item_data_new();
|
280
|
+
|
281
|
+
WDM_WDEBUG("Change detected in '%s'", param->entry->user_data->dir);
|
282
|
+
|
283
|
+
data_to_process->data->user_data = param->entry->user_data;
|
284
|
+
|
285
|
+
// Copy change data to the backup buffer
|
286
|
+
memcpy(data_to_process->data->buffer, param->entry->buffer, bytes_transfered);
|
287
|
+
|
288
|
+
// Add the backup buffer to the change queue
|
289
|
+
wdm_queue_enqueue(param->monitor->changes, data_to_process);
|
290
|
+
|
291
|
+
// Resume watching the dir for changes
|
292
|
+
register_monitoring_entry(param->entry);
|
293
|
+
|
294
|
+
// Tell the processing thread to process the changes
|
295
|
+
if ( WaitForSingleObject(param->monitor->process_event, 0) != WAIT_OBJECT_0 ) { // Check if already signaled
|
296
|
+
SetEvent(param->monitor->process_event);
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
static BOOL
|
301
|
+
register_monitoring_entry(WDM_PEntry entry)
|
302
|
+
{
|
303
|
+
BOOL success;
|
304
|
+
DWORD bytes;
|
305
|
+
bytes = 0; // Not used because the process callback gets passed the written bytes
|
306
|
+
|
307
|
+
success = ReadDirectoryChangesW(
|
308
|
+
entry->dir_handle, // handle to directory
|
309
|
+
entry->buffer, // read results buffer
|
310
|
+
WDM_BUFFER_SIZE, // length of buffer
|
311
|
+
entry->user_data->watch_childeren, // monitoring option
|
312
|
+
entry->user_data->flags, // filter conditions
|
313
|
+
&bytes, // bytes returned
|
314
|
+
&entry->event_container, // overlapped buffer
|
315
|
+
&handle_entry_change // process callback
|
316
|
+
);
|
317
|
+
|
318
|
+
if ( ! success ) {
|
319
|
+
WDM_DEBUG("ReadDirectoryChangesW failed with error (%d): %s", GetLastError(), rb_w32_strerror(GetLastError()));
|
320
|
+
return FALSE;
|
321
|
+
}
|
322
|
+
|
323
|
+
return TRUE;
|
324
|
+
}
|
325
|
+
|
326
|
+
static DWORD WINAPI
|
327
|
+
start_monitoring(LPVOID param)
|
328
|
+
{
|
329
|
+
WDM_PMonitor monitor;
|
330
|
+
WDM_PEntry curr_entry;
|
331
|
+
|
332
|
+
monitor = (WDM_PMonitor)param;
|
333
|
+
curr_entry = monitor->head;
|
334
|
+
|
335
|
+
WDM_DEBUG("Starting the monitoring thread!");
|
336
|
+
|
337
|
+
while(curr_entry != NULL) {
|
338
|
+
if ( ! register_monitoring_entry(curr_entry) ) {
|
339
|
+
WDM_PQueueItem error_item;
|
340
|
+
int directory_bytes;
|
341
|
+
LPSTR multibyte_directory;
|
342
|
+
|
343
|
+
directory_bytes = WideCharToMultiByte(CP_UTF8, 0, curr_entry->user_data->dir, -1, NULL, 0, NULL, NULL);
|
344
|
+
multibyte_directory = ALLOCA_N(CHAR, directory_bytes);
|
345
|
+
WideCharToMultiByte(CP_UTF8, 0, curr_entry->user_data->dir, -1, multibyte_directory, directory_bytes, NULL, NULL);
|
346
|
+
|
347
|
+
error_item = wdm_queue_item_new(WDM_QUEUE_ITEM_TYPE_ERROR);
|
348
|
+
error_item->error = wdm_queue_item_error_new(
|
349
|
+
eWDM_UnwatchableDirectoryError, "Can't watch directory: '%s'!", multibyte_directory
|
350
|
+
);
|
351
|
+
|
352
|
+
wdm_queue_enqueue(monitor->changes, error_item);
|
353
|
+
SetEvent(monitor->process_event);
|
354
|
+
}
|
355
|
+
|
356
|
+
curr_entry = curr_entry->next;
|
357
|
+
}
|
358
|
+
|
359
|
+
while(monitor->running) {
|
360
|
+
// TODO: Is this the best way to do it?
|
361
|
+
if ( WaitForSingleObjectEx(monitor->stop_event, INFINITE, TRUE) == WAIT_OBJECT_0) {
|
362
|
+
WDM_DEBUG("Exiting the monitoring thread!");
|
363
|
+
ExitThread(0);
|
364
|
+
}
|
365
|
+
}
|
366
|
+
|
367
|
+
return 0;
|
368
|
+
}
|
369
|
+
|
370
|
+
static VALUE
|
371
|
+
wait_for_changes(LPVOID param)
|
372
|
+
{
|
373
|
+
HANDLE process_event;
|
374
|
+
|
375
|
+
process_event = (HANDLE)param;
|
376
|
+
|
377
|
+
return WaitForSingleObject(process_event, INFINITE) == WAIT_OBJECT_0 ? Qtrue : Qfalse;
|
378
|
+
}
|
379
|
+
|
380
|
+
static void
|
381
|
+
process_changes(WDM_PQueue changes)
|
382
|
+
{
|
383
|
+
WDM_PQueueItem item;
|
384
|
+
LPBYTE current_info_entry_offset;
|
385
|
+
PFILE_NOTIFY_INFORMATION info;
|
386
|
+
VALUE event;
|
387
|
+
|
388
|
+
WDM_DEBUG("---------------------------");
|
389
|
+
WDM_DEBUG("Process changes");
|
390
|
+
WDM_DEBUG("--------------------------");
|
391
|
+
|
392
|
+
while( ! wdm_queue_is_empty(changes) ) {
|
393
|
+
item = wdm_queue_dequeue(changes);
|
394
|
+
|
395
|
+
if ( item->type == WDM_QUEUE_ITEM_TYPE_ERROR ) {
|
396
|
+
rb_raise(item->error->exception_klass, item->error->message);
|
397
|
+
}
|
398
|
+
else {
|
399
|
+
current_info_entry_offset = (LPBYTE)item->data->buffer;
|
400
|
+
|
401
|
+
for(;;) {
|
402
|
+
info = (PFILE_NOTIFY_INFORMATION)current_info_entry_offset;
|
403
|
+
event = wdm_rb_change_new_from_notification(item->data->user_data->dir, info);
|
404
|
+
|
405
|
+
WDM_DEBUG("---------------------------");
|
406
|
+
WDM_DEBUG("Running user callback");
|
407
|
+
WDM_DEBUG("--------------------------");
|
408
|
+
|
409
|
+
rb_funcall(item->data->user_data->callback, wdm_rb_sym_call, 1, event);
|
410
|
+
|
411
|
+
WDM_DEBUG("---------------------------");
|
412
|
+
|
413
|
+
if ( ! info->NextEntryOffset ) break;
|
414
|
+
|
415
|
+
current_info_entry_offset += info->NextEntryOffset;
|
416
|
+
}
|
417
|
+
}
|
418
|
+
|
419
|
+
wdm_queue_item_free(item);
|
420
|
+
}
|
421
|
+
}
|
422
|
+
|
423
|
+
static void
|
424
|
+
stop_monitoring(LPVOID param)
|
425
|
+
{
|
426
|
+
BOOL already_stopped;
|
427
|
+
WDM_PMonitor monitor;
|
428
|
+
WDM_PEntry entry;
|
429
|
+
|
430
|
+
monitor = (WDM_PMonitor)param;
|
431
|
+
already_stopped = FALSE;
|
432
|
+
|
433
|
+
WDM_DEBUG("Stopping the monitor!");
|
434
|
+
|
435
|
+
EnterCriticalSection(&monitor->lock);
|
436
|
+
if ( ! monitor->running ) {
|
437
|
+
already_stopped = TRUE;
|
438
|
+
}
|
439
|
+
else {
|
440
|
+
monitor->running = FALSE;
|
441
|
+
}
|
442
|
+
LeaveCriticalSection(&monitor->lock);
|
443
|
+
|
444
|
+
if (already_stopped) {
|
445
|
+
WDM_DEBUG("Can't stop monitoring because it's already stopped (or it's never been started)!!");
|
446
|
+
return;
|
447
|
+
}
|
448
|
+
|
449
|
+
entry = monitor->head;
|
450
|
+
|
451
|
+
while(entry != NULL) {
|
452
|
+
CancelIo(entry->dir_handle); // Stop monitoring changes
|
453
|
+
entry = entry->next;
|
454
|
+
}
|
455
|
+
|
456
|
+
SetEvent(monitor->stop_event);
|
457
|
+
SetEvent(monitor->process_event); // The process code checks after the wait for an exit signal
|
458
|
+
WaitForSingleObject(monitor->monitoring_thread, 10000);
|
459
|
+
}
|
460
|
+
|
461
|
+
static VALUE
|
462
|
+
rb_monitor_run_bang(VALUE self)
|
463
|
+
{
|
464
|
+
BOOL already_running,
|
465
|
+
waiting_succeeded;
|
466
|
+
WDM_PMonitor monitor;
|
467
|
+
|
468
|
+
WDM_DEBUG("Running the monitor!");
|
469
|
+
|
470
|
+
Data_Get_Struct(self, WDM_Monitor, monitor);
|
471
|
+
already_running = FALSE;
|
472
|
+
|
473
|
+
EnterCriticalSection(&monitor->lock);
|
474
|
+
if ( monitor->running ) {
|
475
|
+
already_running = TRUE;
|
476
|
+
}
|
477
|
+
else {
|
478
|
+
monitor->running = TRUE;
|
479
|
+
}
|
480
|
+
LeaveCriticalSection(&monitor->lock);
|
481
|
+
|
482
|
+
if (already_running) {
|
483
|
+
WDM_DEBUG("Not doing anything because the monitor is already running!");
|
484
|
+
return Qnil;
|
485
|
+
}
|
486
|
+
|
487
|
+
// Reset events
|
488
|
+
ResetEvent(monitor->process_event);
|
489
|
+
ResetEvent(monitor->stop_event);
|
490
|
+
|
491
|
+
monitor->monitoring_thread = CreateThread(
|
492
|
+
NULL, // default security attributes
|
493
|
+
0, // use default stack size
|
494
|
+
start_monitoring, // thread function name
|
495
|
+
monitor, // argument to thread function
|
496
|
+
0, // use default creation flags
|
497
|
+
NULL // Ignore thread identifier
|
498
|
+
);
|
499
|
+
|
500
|
+
if ( monitor->monitoring_thread == NULL ) {
|
501
|
+
rb_raise(eWDM_Error, "Can't create a thread for the monitor!");
|
502
|
+
}
|
503
|
+
|
504
|
+
while ( monitor->running ) {
|
505
|
+
|
506
|
+
// Ruby 2.2 removed the 'rb_thread_blocking_region' function. Hence, we now need
|
507
|
+
// to check if the replacement function is defined and use it if it's available.
|
508
|
+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
|
509
|
+
waiting_succeeded = rb_thread_call_without_gvl(wait_for_changes, monitor->process_event, stop_monitoring, monitor);
|
510
|
+
#else
|
511
|
+
waiting_succeeded = rb_thread_blocking_region(wait_for_changes, monitor->process_event, stop_monitoring, monitor);
|
512
|
+
#endif
|
513
|
+
|
514
|
+
if ( waiting_succeeded == Qfalse ) {
|
515
|
+
rb_raise(eWDM_Error, "Failed while waiting for a change in the watched directories!");
|
516
|
+
}
|
517
|
+
|
518
|
+
if ( ! monitor->running ) {
|
519
|
+
wdm_queue_empty(monitor->changes);
|
520
|
+
return Qnil;
|
521
|
+
}
|
522
|
+
|
523
|
+
process_changes(monitor->changes);
|
524
|
+
|
525
|
+
if ( ! ResetEvent(monitor->process_event) ) {
|
526
|
+
rb_raise(eWDM_Error, "Couldn't reset system events to watch for changes!");
|
527
|
+
}
|
528
|
+
}
|
529
|
+
|
530
|
+
return Qnil;
|
531
|
+
}
|
532
|
+
|
533
|
+
static VALUE
|
534
|
+
rb_monitor_stop(VALUE self)
|
535
|
+
{
|
536
|
+
WDM_PMonitor monitor;
|
537
|
+
|
538
|
+
Data_Get_Struct(self, WDM_Monitor, monitor);
|
539
|
+
|
540
|
+
stop_monitoring(monitor);
|
541
|
+
|
542
|
+
WDM_DEBUG("Stopped the monitor!");
|
543
|
+
|
544
|
+
return Qnil;
|
545
|
+
}
|
546
|
+
|
547
|
+
void
|
548
|
+
wdm_rb_monitor_init()
|
549
|
+
{
|
550
|
+
WDM_DEBUG("Registering WDM::Monitor with Ruby!");
|
551
|
+
|
552
|
+
wdm_rb_sym_call = rb_intern("call");
|
553
|
+
wdm_rb_sym_files = rb_intern("files");
|
554
|
+
wdm_rb_sym_directories = rb_intern("directories");
|
555
|
+
wdm_rb_sym_attributes = rb_intern("attributes");
|
556
|
+
wdm_rb_sym_size = rb_intern("size");
|
557
|
+
wdm_rb_sym_last_write = rb_intern("last_write");
|
558
|
+
wdm_rb_sym_last_access = rb_intern("last_access");
|
559
|
+
wdm_rb_sym_creation = rb_intern("creation");
|
560
|
+
wdm_rb_sym_security = rb_intern("security");
|
561
|
+
wdm_rb_sym_default = rb_intern("default");
|
562
|
+
|
563
|
+
eWDM_MonitorRunningError = rb_define_class_under(mWDM, "MonitorRunningError", eWDM_Error);
|
564
|
+
eWDM_InvalidDirectoryError = rb_define_class_under(mWDM, "InvalidDirectoryError", eWDM_Error);
|
565
|
+
eWDM_UnknownFlagError = rb_define_class_under(mWDM, "UnknownFlagError", eWDM_Error);
|
566
|
+
eWDM_UnwatchableDirectoryError = rb_define_class_under(mWDM, "UnwatchableDirectoryError", eWDM_Error);
|
567
|
+
|
568
|
+
cWDM_Monitor = rb_define_class_under(mWDM, "Monitor", rb_cObject);
|
569
|
+
|
570
|
+
rb_define_alloc_func(cWDM_Monitor, rb_monitor_alloc);
|
571
|
+
rb_define_method(cWDM_Monitor, "watch", RUBY_METHOD_FUNC(rb_monitor_watch), -1);
|
572
|
+
rb_define_method(cWDM_Monitor, "watch_recursively", RUBY_METHOD_FUNC(rb_monitor_watch_recursively), -1);
|
573
|
+
rb_define_method(cWDM_Monitor, "run!", RUBY_METHOD_FUNC(rb_monitor_run_bang), 0);
|
574
|
+
rb_define_method(cWDM_Monitor, "stop", RUBY_METHOD_FUNC(rb_monitor_stop), 0);
|
571
575
|
}
|