-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathwatch_darwin.go
104 lines (83 loc) · 2.05 KB
/
watch_darwin.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// +build darwin,cgo
// heavily inspired by https://github.com/fsnotify/fsevents
package changes
/*
#cgo LDFLAGS: -framework CoreServices
#include <CoreServices/CoreServices.h>
FSEventStreamRef fswatch_new(
FSEventStreamContext*,
CFMutableArrayRef,
FSEventStreamEventId,
CFTimeInterval,
FSEventStreamCreateFlags);
static CFMutableArrayRef fswatch_make_mutable_array() {
return CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
*/
import "C"
import (
"math/rand"
"sync"
"time"
"unsafe"
)
var (
cflags = C.FSEventStreamCreateFlags(0)
chans = make(map[string](chan string))
interval = 700 * time.Millisecond
now = C.FSEventStreamEventId((1 << 64) - 1)
lock sync.Mutex
)
func init() {
rand.Seed(time.Now().UTC().UnixNano())
}
func startScanner(dir string) {
lock.Lock()
chans[dir] = make(chan string)
lock.Unlock()
cpaths := C.fswatch_make_mutable_array()
defer C.free(unsafe.Pointer(cpaths))
path := C.CString(dir)
str := C.CFStringCreateWithCString(C.kCFAllocatorDefault, path, C.kCFStringEncodingUTF8)
defer C.free(unsafe.Pointer(path))
defer C.free(unsafe.Pointer(str))
C.CFArrayAppendValue(cpaths, unsafe.Pointer(str))
ctx := C.FSEventStreamContext{info: unsafe.Pointer(C.CString(dir))}
stream := C.fswatch_new(&ctx, cpaths, now, C.CFTimeInterval(interval/time.Second), cflags)
go func() {
C.FSEventStreamScheduleWithRunLoop(stream, C.CFRunLoopGetCurrent(), C.kCFRunLoopCommonModes)
C.FSEventStreamStart(stream)
C.CFRunLoopRun()
}()
}
func waitForNextScan(dir string) {
tick := time.Tick(900 * time.Millisecond)
fired := false
for {
lock.Lock()
ch, ok := chans[dir]
lock.Unlock()
if !ok {
return
}
select {
case <-ch:
tick = time.Tick(500 * time.Millisecond)
fired = true
case <-tick:
if fired {
return
}
}
}
}
//export cb
func cb(stream C.FSEventStreamRef, info unsafe.Pointer, count C.size_t, paths **C.char, flags *C.FSEventStreamEventFlags, ids *C.FSEventStreamEventId) {
dir := C.GoString((*C.char)(info))
lock.Lock()
ch, ok := chans[dir]
lock.Unlock()
if ok {
ch <- ""
}
}