// Start listening to an event stream. func (es *EventStream) Start() { cPaths := C.ArrayCreateMutable(C.int(len(es.Paths))) defer C.CFRelease(C.CFTypeRef(cPaths)) for _, p := range es.Paths { p, _ = filepath.Abs(p) cpath := C.CString(p) defer C.free(unsafe.Pointer(cpath)) str := C.CFStringCreateWithCString(nil, cpath, C.kCFStringEncodingUTF8) C.CFArrayAppendValue(cPaths, unsafe.Pointer(str)) } since := C.FSEventStreamEventId(EventIDSinceNow) if es.Resume { since = C.FSEventStreamEventId(es.EventID) } if es.Events == nil { es.Events = make(chan []Event) } es.registryID = registry.Add(es) context := C.FSEventStreamContext{} info := C.uintptr_t(es.registryID) latency := C.CFTimeInterval(float64(es.Latency) / float64(time.Second)) if es.Device != 0 { es.stream = C.EventStreamCreateRelativeToDevice(&context, info, C.dev_t(es.Device), cPaths, since, latency, C.FSEventStreamCreateFlags(es.Flags)) } else { es.stream = C.EventStreamCreate(&context, info, cPaths, since, latency, C.FSEventStreamCreateFlags(es.Flags)) } started := make(chan struct{}) go func() { runtime.LockOSThread() es.rlref = C.CFRunLoopGetCurrent() C.FSEventStreamScheduleWithRunLoop(es.stream, es.rlref, C.kCFRunLoopDefaultMode) C.FSEventStreamStart(es.stream) close(started) C.CFRunLoopRun() }() if !es.hasFinalizer { runtime.SetFinalizer(es, finalizer) es.hasFinalizer = true } <-started }
//export gostream func gostream(_, ctx unsafe.Pointer, n C.size_t, paths, flags, ids uintptr) { const ( offchar = unsafe.Sizeof((*C.char)(nil)) offflag = unsafe.Sizeof(C.FSEventStreamEventFlags(0)) offid = unsafe.Sizeof(C.FSEventStreamEventId(0)) ) if n == 0 { return } ev := make([]FSEvent, 0, int(n)) for i := uintptr(0); i < uintptr(n); i++ { switch flags := *(*uint32)(unsafe.Pointer((flags + i*offflag))); { case flags&uint32(FSEventsEventIdsWrapped) != 0: atomic.StoreUint64(&since, uint64(C.FSEventsGetCurrentEventId())) default: ev = append(ev, FSEvent{ Path: C.GoString(*(**C.char)(unsafe.Pointer(paths + i*offchar))), Flags: flags, ID: *(*uint64)(unsafe.Pointer(ids + i*offid)), }) } } (*(*streamFunc)(ctx))(ev) }
func New(dev Device, since EventID, interval time.Duration, flags CreateFlags, paths ...string) *Stream { cpaths := C.fswatch_make_mutable_array() defer C.free(unsafe.Pointer(cpaths)) for _, dir := range paths { path := C.CString(dir) defer C.free(unsafe.Pointer(path)) str := C.CFStringCreateWithCString(nil, path, C.kCFStringEncodingUTF8) defer C.free(unsafe.Pointer(str)) C.CFArrayAppendValue(cpaths, unsafe.Pointer(str)) } csince := C.FSEventStreamEventId(since) cinterval := C.CFTimeInterval(interval / time.Second) cflags := C.FSEventStreamCreateFlags(flags &^ CF_USECFTYPES) s := new(Stream) s.Chan = make(chan []Event) ctx := C.FSEventStreamContext{info: unsafe.Pointer(&s.Chan)} var cstream C.FSEventStreamRef if dev == 0 { cstream = C.fswatch_create(&ctx, cpaths, csince, cinterval, cflags) } else { cdev := C.dev_t(dev) cstream = C.fswatch_create_relative_to_device( cdev, &ctx, cpaths, csince, cinterval, cflags) } s.cstream = cstream return s }
// Start creates a FSEventStream for the given path and schedules it with // global runloop. It's a nop if the stream was already started. func (s *stream) Start() error { if s.ref != nilstream { return nil } wg.Wait() p := C.CFStringCreateWithCStringNoCopy(nil, C.CString(s.path), C.kCFStringEncodingUTF8, nil) path := C.CFArrayCreate(nil, (*unsafe.Pointer)(unsafe.Pointer(&p)), 1, nil) ref := C.FSEventStreamCreate(nil, (C.FSEventStreamCallback)(C.gostream), &s.ctx, path, C.FSEventStreamEventId(atomic.LoadUint64(&since)), latency, flags) if ref == nilstream { return errCreate } C.FSEventStreamScheduleWithRunLoop(ref, runloop, C.kCFRunLoopDefaultMode) if C.FSEventStreamStart(ref) == C.Boolean(0) { C.FSEventStreamInvalidate(ref) return errStart } C.CFRunLoopWakeUp(runloop) s.ref = ref return nil }
} */ 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))