3c99b42015-10-14Martin Nilsson /* -*- c -*- || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. */
762a142012-06-29Bill Welliver /*! @module System */ /*! @module FSEvents */ #include "module.h"
8e7c8e2018-01-18Martin Nilsson #include "interpret.h"
762a142012-06-29Bill Welliver #include "threads.h" #include "pike_types.h" #include "builtin_functions.h" #include "fsevents_config.h" #define ADD_ICONST(name) do { \ add_integer_constant(#name, name, 0); \ } while(0); #ifdef HAVE_FRAMEWORK_CORESERVICES #import <CoreServices/CoreServices.h> #endif /* HAVE_FRAMEWORK_CORESERVICES */ DECLARATIONS #ifdef HAVE_FRAMEWORK_CORESERVICES
c9eefb2014-08-21Martin Nilsson void low_stop(void);
762a142012-06-29Bill Welliver struct pike_string * string_from_cfstring(CFStringRef cfString);
13670c2015-05-25Martin Nilsson struct event_callback_ctx
762a142012-06-29Bill Welliver { ConstFSEventStreamRef streamRef; void *clientCallBackInfo; size_t numEvents; void *eventPaths; const FSEventStreamEventFlags * eventFlags; const FSEventStreamEventId * eventIds; }; static void do_event_callback(struct event_callback_ctx * ctx); static void event_callback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]); /*! @class EventStream */ PIKECLASS EventStream { CVAR CFRunLoopRef runLoop; CVAR FSEventStreamRef stream; CVAR int isRunning; CVAR CFArrayRef _paths; CVAR FSEventStreamEventId _sinceWhen; CVAR FSEventStreamCreateFlags _flags; CVAR CFAbsoluteTime _latency; PIKEVAR function callback_func; /*! @decl int is_started() *! Has start() been called? */ PIKEFUN int is_started() { RETURN(THIS->isRunning); } /*! @decl void flush_async() *!
13670c2015-05-25Martin Nilsson  *! Requests that the FS Events service to flush out any events that have occurred but have not yet been *! delivered, due to the latency parameter that was supplied when the stream was created.
762a142012-06-29Bill Welliver  *! *! This flushing occurs asynchronously -- events most likely will not have been delivered
13670c2015-05-25Martin Nilsson  *! by the time this call returns.
762a142012-06-29Bill Welliver  *!
13670c2015-05-25Martin Nilsson  *! @note
762a142012-06-29Bill Welliver  *! Only call this function after the stream has been started, via start(). */ PIKEFUN void flush_async() { if(THIS->stream) FSEventStreamFlushAsync(THIS->stream); else Pike_error("FSEvents.EventStream: not started\n"); } /*! @decl void flush_sync() *!
13670c2015-05-25Martin Nilsson  *! Requests that the FS Events service to flush out any events that have occurred but have not yet been *! delivered, due to the latency parameter that was supplied when the stream was created.
762a142012-06-29Bill Welliver  *!
13670c2015-05-25Martin Nilsson  *! Flushing synchronously when using this method; clients will have received all the callbacks by
762a142012-06-29Bill Welliver  *! the time this call returns to them. *!
13670c2015-05-25Martin Nilsson  *! @note
762a142012-06-29Bill Welliver  *! Only call this function after the stream has been started, via start(). *! */ PIKEFUN void flush_sync() { if(THIS->stream) FSEventStreamFlushSync(THIS->stream); else Pike_error("FSEvents.EventStream: not started\n"); } /*! @decl void create(array(string) paths, float latency, int|void since_when, int|void flags) *! Creates a new Public.System.FSEvents.EventStream object *! *! @param paths
13670c2015-05-25Martin Nilsson  *! An array with each element containing a path to a directory, signifying the root of a
762a142012-06-29Bill Welliver  *! filesystem hierarchy to be watched for modifications. *! *! Additional paths may be added later using @[add_path()], though only if the stream is stopped. *! *! @param latency
13670c2015-05-25Martin Nilsson  *! The number of seconds the service should wait after hearing about an event from the kernel *! before passing it along to the client via its callback. Specifying a larger value may *! result in more effective temporal coalescing, resulting in fewer callbacks and greater
762a142012-06-29Bill Welliver  *! overall efficiency. *! *! @param since_when *! The service will supply events that have happened after the given event ID. To ask for events *! "since now" pass the constant kFSEventStreamEventIdSinceNow. Do not pass zero for this value *! unless you want to receive events for the requested directories "since the beginning of time". *! *! @param flags *! Flags that modify the behavior of the stream being created. See Apple's FSEvents documentation *! for details of the various flags available. */ PIKEFUN void create(array paths, float latency, int|void sinceWhen, int|void flags) { int idx = 0, cnt = 0;
13670c2015-05-25Martin Nilsson 
762a142012-06-29Bill Welliver  THIS->isRunning = 0; THIS->_latency = latency;
832df62014-07-19Per Hedbor  if(sinceWhen && TYPEOF(*sinceWhen) == T_INT)
762a142012-06-29Bill Welliver  { THIS->_sinceWhen = sinceWhen->u.integer; } else { THIS->_sinceWhen = kFSEventStreamEventIdSinceNow; }
832df62014-07-19Per Hedbor  if(flags && TYPEOF(*flags) == T_INT)
762a142012-06-29Bill Welliver  { THIS->_flags = flags->u.integer; } else { THIS->_flags = kFSEventStreamCreateFlagNone; }
13670c2015-05-25Martin Nilsson 
762a142012-06-29Bill Welliver  THIS->_paths = CFArrayCreateMutable(NULL, 0, NULL); CFRetain(THIS->_paths); if(paths && paths->size) { for(idx = 0; idx < paths->size; idx++) { struct svalue sv; CFStringRef str;
832df62014-07-19Per Hedbor  if(TYPEOF(ITEM(paths)[idx]) != T_STRING) continue;
762a142012-06-29Bill Welliver  sv = ITEM(paths)[idx]; push_svalue(&sv); f_string_to_utf8(1); str = CFStringCreateWithBytes(NULL, (const UInt8 *)(Pike_sp[-1].u.string->str), (CFIndex)Pike_sp[-1].u.string->len, kCFStringEncodingUTF8, false); pop_stack(); CFArrayInsertValueAtIndex( (CFMutableArrayRef)THIS->_paths, cnt, str); cnt++; } } } /*! @decl void add_path(string path) *! Add a path to the monitor list. *! *! @param path *! *! @note *! this can only be called when the monitor is stopped. */ PIKEFUN void add_path(string path) { if(THIS->isRunning) { Pike_error("Cannot add paths while monitor is started.\n"); } if(path && path->len) { int size; CFStringRef str; f_string_to_utf8(1); str = CFStringCreateWithBytes(NULL, (const UInt8 *)(Pike_sp[-1].u.string->str), (CFIndex)Pike_sp[-1].u.string->len, kCFStringEncodingUTF8, false); size = CFArrayGetCount(THIS->_paths); CFArrayInsertValueAtIndex( (CFMutableArrayRef)THIS->_paths, size, str); } } /*! @decl void set_callback(function callback) *! Sets the function that will be called when a file notification event is received.
13670c2015-05-25Martin Nilsson  *!
762a142012-06-29Bill Welliver  *! The method signature for the callback is:
13670c2015-05-25Martin Nilsson  *!
762a142012-06-29Bill Welliver  *! void event_callback(string path, int flags, int event_id) */ PIKEFUN void set_callback(function callback) { assign_svalue(&THIS->callback_func, callback); } /* * TODO we should allow the runloop to be specified. */ /*! @decl void start() *! *! Requests that new events be delivered to this EventStream.
13670c2015-05-25Martin Nilsson  *! *! If a value was supplied for the since_when parameter then "historical" events will be sent via *! your callback first, then a HistoryDone event, then "contemporary" events will be sent on
762a142012-06-29Bill Welliver  *! an ongoing basis. */ PIKEFUN void start() { FSEventStreamContext context; if(THIS->isRunning) {
f2bcbd2012-07-11Bill Welliver  Pike_error("EventStream: monitor is already running.\n");
762a142012-06-29Bill Welliver  }
f2bcbd2012-07-11Bill Welliver  if(!THIS->_paths) Pike_error("EventStream: no paths.\n"); if(!THIS->_sinceWhen) Pike_error("EventStream: no startdate.\n");
13670c2015-05-25Martin Nilsson 
f2bcbd2012-07-11Bill Welliver  if(!THIS->_latency) Pike_error("EventStream: no latency.\n");
762a142012-06-29Bill Welliver  if(CFArrayGetCount(THIS->_paths))
13670c2015-05-25Martin Nilsson  {
762a142012-06-29Bill Welliver  THIS->runLoop = CFRunLoopGetCurrent(); CFRetain(THIS->runLoop);
13670c2015-05-25Martin Nilsson  context.version = 0;
762a142012-06-29Bill Welliver  context.info = THIS; context.retain = NULL; context.release = NULL; context.copyDescription = NULL; THIS->stream = FSEventStreamCreate(kCFAllocatorDefault, &event_callback, &context, THIS->_paths, THIS->_sinceWhen, THIS->_latency, THIS->_flags | kFSEventStreamCreateFlagUseCFTypes ); THIS->isRunning = 1; FSEventStreamScheduleWithRunLoop(THIS->stream, THIS->runLoop, kCFRunLoopDefaultMode); FSEventStreamStart(THIS->stream); } else {
f2bcbd2012-07-11Bill Welliver  Pike_error("EventStream: No paths registered for monitoring.\n");
762a142012-06-29Bill Welliver  } } /*! @decl void stop() *! Stops watching for new events. */ PIKEFUN void stop() { low_stop(); }
c9eefb2014-08-21Martin Nilsson void low_stop(void)
762a142012-06-29Bill Welliver { if(THIS->isRunning) { FSEventStreamStop(THIS->stream); FSEventStreamUnscheduleFromRunLoop(THIS->stream, THIS->runLoop, kCFRunLoopDefaultMode); FSEventStreamInvalidate(THIS->stream); FSEventStreamRelease(THIS->stream); CFRelease(THIS->runLoop); THIS->isRunning = 0; } } /** * FSEvents callback function. The frequency at which this callback is * called depends upon the notification latency value. This callback is usually * called with more than one event and so multiple calls to the callback occur. * * @param streamRef The calling stream reference * @param clientCallBackInfo Any client callback info that was supplied when the stream was created * @param numEvents The number of events being supplied * @param eventPaths An array of the event's paths * @param eventFlags An array of flags associated with the events * @param eventIds An array of IDs associated with the events */ static void event_callback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) { struct event_callback_ctx ctx; struct event_callback_ctx * ctx_ptr; ctx.streamRef = streamRef; ctx.clientCallBackInfo = clientCallBackInfo; ctx.numEvents = numEvents; ctx.eventPaths = eventPaths; ctx.eventFlags = eventFlags; ctx.eventIds = eventIds;
13670c2015-05-25Martin Nilsson 
762a142012-06-29Bill Welliver  ctx_ptr = &ctx;
13670c2015-05-25Martin Nilsson  call_with_interpreter((void *)&do_event_callback, (void *)ctx_ptr);
762a142012-06-29Bill Welliver /* do_event_callback(streamRef, clientCallBackInfo, numEvents, eventPaths, eventFlags, eventIds); */ } static void do_event_callback(struct event_callback_ctx * ctx) { size_t cnt = 0; for(cnt = 0; cnt < ctx->numEvents; cnt++) { CFStringRef eventPath; struct pike_string * str; const char * u8s;
de6d882014-05-10Per Hedbor  struct FSEvents_EventStream_struct * eventStreamObj;
762a142012-06-29Bill Welliver  eventPath = CFArrayGetValueAtIndex(ctx->eventPaths, (CFIndex)cnt); str = string_from_cfstring(eventPath); push_string(str); f_utf8_to_string(1); push_int(ctx->eventFlags[cnt]); push_int(ctx->eventIds[cnt]);
de6d882014-05-10Per Hedbor  eventStreamObj = (struct FSEvents_EventStream_struct *)(ctx->clientCallBackInfo);
762a142012-06-29Bill Welliver  apply_svalue(&eventStreamObj->callback_func, 3); } } struct pike_string * string_from_cfstring(CFStringRef cfString) { const char *useUTF8StringPtr = NULL; UInt8 *freeUTF8StringPtr = NULL; struct pike_string * str = NULL; long utf8Length; CFIndex stringLength = CFStringGetLength(cfString), usedBytes = 0L; if((useUTF8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8)) == NULL) { if((freeUTF8StringPtr = malloc(stringLength + 1L)) != NULL) { CFStringGetBytes(cfString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', false, freeUTF8StringPtr, stringLength, &usedBytes); freeUTF8StringPtr[usedBytes] = 0; useUTF8StringPtr = (const char *)freeUTF8StringPtr; } } utf8Length = (long)((freeUTF8StringPtr != NULL) ? usedBytes : stringLength); if(useUTF8StringPtr != NULL) { /* * useUTF8StringPtr points to a NULL terminated UTF8 encoded string. * utf8Length contains the length of the UTF8 string. */ str = make_shared_binary_string(useUTF8StringPtr, utf8Length); } if(freeUTF8StringPtr != NULL) { free(freeUTF8StringPtr); freeUTF8StringPtr = NULL; } return str; } INIT { } EXIT { #ifdef HAVE_FRAMEWORK_CORESERVICES low_stop(); if(THIS->_paths) CFRelease(THIS->_paths); #endif /* HAVE_FRAMEWORK_CORESERVICES */ } }
ad33802017-06-26Henrik Grubbström (Grubba) /*! @endclass */
ad396b2014-10-22Arne Goedeke #endif /* HAVE_FRAMEWORK_CORESERVICES */
762a142012-06-29Bill Welliver PIKE_MODULE_INIT { INIT;
13670c2015-05-25Martin Nilsson /*! @decl constant kFSEventStreamEventFlagNone
762a142012-06-29Bill Welliver  */
13670c2015-05-25Martin Nilsson /*! @decl constant kFSEventStreamEventFlagMustScanSubDirs
762a142012-06-29Bill Welliver  */
13670c2015-05-25Martin Nilsson /*! @decl constant kFSEventStreamEventFlagUserDropped
762a142012-06-29Bill Welliver  */ /*! @decl constant kFSEventStreamEventFlagKernelDropped */
13670c2015-05-25Martin Nilsson /*! @decl constant kFSEventStreamEventFlagEventIdsWrapped
762a142012-06-29Bill Welliver  */ /*! @decl constant kFSEventStreamEventFlagHistoryDone */ /*! @decl constant kFSEventStreamEventFlagRootChanged */ /*! @decl constant kFSEventStreamEventFlagMount */ /*! @decl constant kFSEventStreamEventFlagUnmount */ /*! @decl constant kFSEventStreamEventFlagItemCreated *! Available in MacOS X 10.7 and newer. */
13670c2015-05-25Martin Nilsson /*! @decl constant kFSEventStreamEventFlagItemRemoved
762a142012-06-29Bill Welliver  *! Available in MacOS X 10.7 and newer. */ /*! @decl constant kFSEventStreamEventFlagInodeMetaMod *! Available in MacOS X 10.7 and newer. */ /*! @decl constant kFSEventStreamEventFlagRenamed *! Available in MacOS X 10.7 and newer. */
13670c2015-05-25Martin Nilsson /*! @decl constant kFSEventStreamEventFlagItemModified
762a142012-06-29Bill Welliver  *! Available in MacOS X 10.7 and newer. */ /*! @decl constant kFSEventStreamEventFlagFinderInfoMod *! Available in MacOS X 10.7 and newer. */ /*! @decl constant kFSEventStreamEventFlagChangeOwner *! Available in MacOS X 10.7 and newer. */
13670c2015-05-25Martin Nilsson /*! @decl constant kFSEventStreamEventFlagXattrMod
762a142012-06-29Bill Welliver  *! Available in MacOS X 10.7 and newer. */ /*! @decl constant kFSEventStreamEventFlagIsFile *! Available in MacOS X 10.7 and newer. */ /*! @decl constant kFSEventStreamEventFlagIsDir *! Available in MacOS X 10.7 and newer. */ /*! @decl constant kFSEventStreamEventFlagIsSymlink *! Available in MacOS X 10.7 and newer. */
13670c2015-05-25Martin Nilsson /*! @decl constant kFSEventStreamCreateFlagFileEvents
762a142012-06-29Bill Welliver  *! Available in MacOS X 10.7 and newer. */ /*! @decl constant kFSEventStreamCreateFlagIgnoreSelf *! Available in MacOS X 10.6 and newer. */ /*! @decl constant kFSEventStreamCreateFlagWatchRoot */ /*! @decl constant kFSEventStreamCreateFlagNoDefer */ /*! @decl constant kFSEventStreamCreateFlagNone */ /*! @decl constant kFSEventStreamEventIdSinceNow */
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGNONE
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagNone); #endif
104c702015-10-16Martin Karlgren #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGMUSTSCANSUBDIRS
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagMustScanSubDirs);
13670c2015-05-25Martin Nilsson #endif
762a142012-06-29Bill Welliver 
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGUSERDROPPED
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagUserDropped); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGKERNELDROPPED
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagKernelDropped); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGEVENTIDSWRAPPED
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagEventIdsWrapped); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGHISTORYDONE
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagHistoryDone); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGROOTCHANGED
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagRootChanged); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGMOUNT
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagMount); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGUNMOUNT
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagUnmount); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMCREATED
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemCreated); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMREMOVED
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemRemoved);
13670c2015-05-25Martin Nilsson #endif
762a142012-06-29Bill Welliver 
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGINODEMETAMOD
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemInodeMetaMod);
13670c2015-05-25Martin Nilsson #endif
762a142012-06-29Bill Welliver 
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMRENAMED
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemRenamed); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMMODIFIED
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemModified); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGFINDERINFOMOD
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemFinderInfoMod); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMCHANGEOWNER
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemChangeOwner); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMXATTRMOD
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemXattrMod); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMISFILE
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemIsFile); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMISDIR
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemIsDir); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMISSYMLINK
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventFlagItemIsSymlink); #endif /* * flags for the stream creation */
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGFILEEVENTS
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamCreateFlagFileEvents); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGIGNORESELF
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamCreateFlagIgnoreSelf); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGWATCHROOT
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamCreateFlagWatchRoot); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGNODEFER
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamCreateFlagNoDefer); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGNONE
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamCreateFlagNone); #endif
3ee17c2012-07-01Arne Goedeke #if HAVE_DECL_KFSEVENTSTREAMEVENTIDSINCENOW
762a142012-06-29Bill Welliver  ADD_ICONST(kFSEventStreamEventIdSinceNow); #endif } PIKE_MODULE_EXIT { EXIT; } /*! @endmodule */ /*! @endmodule */