762a14 | 2012-06-29 | Bill Welliver | | /*! @module System
*/
/*! @module FSEvents
*/
#include "global.h"
#include "interpret.h"
#include "module.h"
#include "program.h"
#include "stralloc.h"
#include "svalue.h"
#include "threads.h"
#include "object.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
void low_stop();
struct pike_string * string_from_cfstring(CFStringRef cfString);
struct event_callback_ctx
{
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()
*!
*! 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.
*!
*! This flushing occurs asynchronously -- events most likely will not have been delivered
*! by the time this call returns.
*!
*! @note
*! 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()
*!
*! 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.
*!
*! Flushing synchronously when using this method; clients will have received all the callbacks by
*! the time this call returns to them.
*!
*! @note
*! 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
*! An array with each element containing a path to a directory, signifying the root of a
*! 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
*! 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
*! 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;
THIS->isRunning = 0;
THIS->_latency = latency;
if(sinceWhen && sinceWhen->type == T_INT)
{
THIS->_sinceWhen = sinceWhen->u.integer;
}
else
{
THIS->_sinceWhen = kFSEventStreamEventIdSinceNow;
}
if(flags && flags->type == T_INT)
{
THIS->_flags = flags->u.integer;
}
else
{
THIS->_flags = kFSEventStreamCreateFlagNone;
}
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;
if(ITEM(paths)[idx].type != T_STRING) continue;
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++;
}
}
pop_n_elems(args);
return;
}
/*! @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);
}
pop_stack();
}
/*! @decl void set_callback(function callback)
*! Sets the function that will be called when a file notification event is received.
*!
*! The method signature for the callback is:
*!
*! void event_callback(string path, int flags, int event_id)
*/
PIKEFUN void set_callback(function callback)
{
assign_svalue(&THIS->callback_func, callback);
pop_stack();
}
/*
* TODO we should allow the runloop to be specified.
*/
/*! @decl void start()
*!
*! Requests that new events be delivered to this EventStream.
*!
*! 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
*! an ongoing basis.
*/
PIKEFUN void start()
{
FSEventStreamContext context;
if(THIS->isRunning)
{
|
f2bcbd | 2012-07-11 | Bill Welliver | | Pike_error("EventStream: monitor is already running.\n");
|
762a14 | 2012-06-29 | Bill Welliver | | }
|
f2bcbd | 2012-07-11 | Bill Welliver | | if(!THIS->_paths)
Pike_error("EventStream: no paths.\n");
if(!THIS->_sinceWhen)
Pike_error("EventStream: no startdate.\n");
if(!THIS->_latency)
Pike_error("EventStream: no latency.\n");
|
762a14 | 2012-06-29 | Bill Welliver | | if(CFArrayGetCount(THIS->_paths))
{
THIS->runLoop = CFRunLoopGetCurrent();
CFRetain(THIS->runLoop);
context.version = 0;
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
{
|
f2bcbd | 2012-07-11 | Bill Welliver | | Pike_error("EventStream: No paths registered for monitoring.\n");
|
762a14 | 2012-06-29 | Bill Welliver | | }
}
/*! @decl void stop()
*! Stops watching for new events.
*/
PIKEFUN void stop()
{
low_stop();
}
void low_stop()
{
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;
ctx_ptr = &ctx;
call_with_interpreter((void *)&do_event_callback, (void *)ctx_ptr);
/*
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;
struct EventStream_struct * eventStreamObj;
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]);
eventStreamObj = (struct EventStream_struct *)(ctx->clientCallBackInfo);
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;
}
#endif /* HAVE_FRAMEWORK_CORESERVICES */
INIT
{
}
EXIT
{
#ifdef HAVE_FRAMEWORK_CORESERVICES
low_stop();
if(THIS->_paths) CFRelease(THIS->_paths);
#endif /* HAVE_FRAMEWORK_CORESERVICES */
}
}
PIKE_MODULE_INIT
{
INIT;
/*! @decl constant kFSEventStreamEventFlagNone
*/
/*! @decl constant kFSEventStreamEventFlagMustScanSubDirs
*/
/*! @decl constant kFSEventStreamEventFlagUserDropped
*/
/*! @decl constant kFSEventStreamEventFlagKernelDropped
*/
/*! @decl constant kFSEventStreamEventFlagEventIdsWrapped
*/
/*! @decl constant kFSEventStreamEventFlagHistoryDone
*/
/*! @decl constant kFSEventStreamEventFlagRootChanged
*/
/*! @decl constant kFSEventStreamEventFlagMount
*/
/*! @decl constant kFSEventStreamEventFlagUnmount
*/
/*! @decl constant kFSEventStreamEventFlagItemCreated
*! Available in MacOS X 10.7 and newer.
*/
/*! @decl constant kFSEventStreamEventFlagItemRemoved
*! 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.
*/
/*! @decl constant kFSEventStreamEventFlagModified
*! 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.
*/
/*! @decl constant kFSEventStreamEventFlagXattrMod
*! 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.
*/
/*! @decl constant kFSEventStreamCreateFlagFileEvents
*! 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
*/
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGNONE
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagNone);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTMUSTSCANSUBDIRS
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagMustScanSubDirs);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGUSERDROPPED
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagUserDropped);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGKERNELDROPPED
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagKernelDropped);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGEVENTIDSWRAPPED
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagEventIdsWrapped);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGHISTORYDONE
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagHistoryDone);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGROOTCHANGED
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagRootChanged);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGMOUNT
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagMount);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGUNMOUNT
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagUnmount);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMCREATED
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemCreated);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMREMOVED
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemRemoved);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGINODEMETAMOD
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemInodeMetaMod);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMRENAMED
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemRenamed);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMMODIFIED
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemModified);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGFINDERINFOMOD
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemFinderInfoMod);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMCHANGEOWNER
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemChangeOwner);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMXATTRMOD
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemXattrMod);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMISFILE
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemIsFile);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMISDIR
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemIsDir);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTFLAGITEMISSYMLINK
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventFlagItemIsSymlink);
#endif
/*
* flags for the stream creation
*/
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGFILEEVENTS
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamCreateFlagFileEvents);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGIGNORESELF
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamCreateFlagIgnoreSelf);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGWATCHROOT
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamCreateFlagWatchRoot);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGNODEFER
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamCreateFlagNoDefer);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMCREATEFLAGNONE
|
762a14 | 2012-06-29 | Bill Welliver | |
ADD_ICONST(kFSEventStreamCreateFlagNone);
#endif
|
3ee17c | 2012-07-01 | Arne Goedeke | | #if HAVE_DECL_KFSEVENTSTREAMEVENTIDSINCENOW
|
762a14 | 2012-06-29 | Bill Welliver | | ADD_ICONST(kFSEventStreamEventIdSinceNow);
#endif
}
PIKE_MODULE_EXIT
{
EXIT;
}
/*! @endclass
*/
/*! @endmodule
*/
/*! @endmodule
*/
|