-
Notifications
You must be signed in to change notification settings - Fork 6
/
watcher.go
123 lines (99 loc) · 3.46 KB
/
watcher.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package fsevents
/*
#cgo LDFLAGS: -framework CoreServices
#include <CoreServices/CoreServices.h>
FSEventStreamRef fswatch_stream_for_paths(CFMutableArrayRef pathsToWatch);
static CFMutableArrayRef fswatch_make_mutable_array() {
return CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
*/
import "C"
import "unsafe"
const (
FlagUnmount = uint32(C.kFSEventStreamEventFlagUnmount)
FlagMount = uint32(C.kFSEventStreamEventFlagMount)
FlagItemCreated = uint32(C.kFSEventStreamEventFlagItemCreated)
FlagItemRemoved = uint32(C.kFSEventStreamEventFlagItemRemoved)
FlagItemInodeMetaMod = uint32(C.kFSEventStreamEventFlagItemInodeMetaMod)
FlagItemRenamed = uint32(C.kFSEventStreamEventFlagItemRenamed)
FlagItemModified = uint32(C.kFSEventStreamEventFlagItemModified)
FlagItemFinderInfoMod = uint32(C.kFSEventStreamEventFlagItemFinderInfoMod)
FlagItemChangeOwner = uint32(C.kFSEventStreamEventFlagItemChangeOwner)
FlagItemXattrMod = uint32(C.kFSEventStreamEventFlagItemXattrMod)
FlagItemIsFile = uint32(C.kFSEventStreamEventFlagItemIsFile)
FlagItemIsDir = uint32(C.kFSEventStreamEventFlagItemIsDir)
FlagItemIsSymlink = uint32(C.kFSEventStreamEventFlagItemIsSymlink)
)
type watchingInfo struct {
channel chan []PathEvent
runloop C.CFRunLoopRef
}
var watchers = make(map[C.FSEventStreamRef]watchingInfo)
type PathEvent struct {
Path string
Flags uint32
}
func Unwatch(ch chan []PathEvent) {
for stream, info := range watchers {
if ch == info.channel {
C.FSEventStreamStop(stream)
C.FSEventStreamInvalidate(stream)
C.FSEventStreamRelease(stream)
C.CFRunLoopStop(info.runloop)
}
}
}
func WatchPaths(paths []string) chan []PathEvent {
type watchSuccessData struct {
runloop C.CFRunLoopRef
stream C.FSEventStreamRef
}
successChan := make(chan *watchSuccessData)
go func() {
pathsToWatch := C.fswatch_make_mutable_array()
defer C.CFRelease(C.CFTypeRef(pathsToWatch))
for _, dir := range paths {
path := C.CString(dir)
defer C.free(unsafe.Pointer(path))
str := C.CFStringCreateWithCString(nil, path, C.kCFStringEncodingUTF8)
C.CFArrayAppendValue(pathsToWatch, unsafe.Pointer(str))
}
stream := C.fswatch_stream_for_paths(pathsToWatch)
C.FSEventStreamScheduleWithRunLoop(stream, C.CFRunLoopGetCurrent(), C.kCFRunLoopCommonModes)
ok := C.FSEventStreamStart(stream) != 0
if ok {
successChan <- &watchSuccessData{
runloop: C.CFRunLoopGetCurrent(),
stream: stream,
}
C.CFRunLoopRun()
} else {
successChan <- nil
}
}()
watchingData := <-successChan
if watchingData == nil {
return nil
}
newChan := make(chan []PathEvent)
watchers[watchingData.stream] = watchingInfo{
channel: newChan,
runloop: watchingData.runloop,
}
return newChan
}
//export watchDirsCallback
func watchDirsCallback(stream C.FSEventStreamRef, count C.size_t, paths **C.char, flags *C.FSEventStreamEventFlags) {
var events []PathEvent
for i := 0; i < int(count); i++ {
cpaths := uintptr(unsafe.Pointer(paths)) + (uintptr(i) * unsafe.Sizeof(*paths))
cpath := *(**C.char)(unsafe.Pointer(cpaths))
cflags := uintptr(unsafe.Pointer(flags)) + (uintptr(i) * unsafe.Sizeof(*flags))
cflag := *(*C.FSEventStreamEventFlags)(unsafe.Pointer(cflags))
events = append(events, PathEvent{
Path: C.GoString(cpath),
Flags: uint32(cflag),
})
}
watchers[stream].channel <- events
}