FSEventStreamRef CreateEventStream( DirWatchMap path ) { if ( ( g_Stream == NULL ) && CanRunNotifications() ) { CFStringRef* pathLists = (CFStringRef*)malloc( sizeof(CFStringRef*) * path.size() ); int index = 0; for ( DirWatchMap::iterator it = path.begin() ; it != path.end(); ++it) { pathLists[index] = CFStringCreateWithFileSystemRepresentation( NULL, OsString(it->path).c_str()); index++; } CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)pathLists, index, NULL); FSEventStreamContext *callbackInfo = NULL; FSEventStreamRef stream = FSEventStreamCreate(NULL, &fsevent_callback, callbackInfo, pathsToWatch, kFSEventStreamEventIdSinceNow, 1.0, kFSEventStreamCreateFlagFileEvents ); CFRelease( pathsToWatch ); free( pathLists ); FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); if (!FSEventStreamStart(stream)) debug_warn(L"event_loop FSEventStreamStart failed!"); else return stream; } return NULL; }
Status dir_watch_Poll(DirWatchNotifications& notifications) { if(initialized == -1) return ERR::FAIL; // NOWARN if(!initialized) // XXX Fix Atlas instead of suppressing the warning return ERR::FAIL; //WARN_RETURN(ERR::LOGIC); std::vector<NotificationEvent> polled_notifications; pthread_mutex_lock(&g_mutex); g_notifications.swap(polled_notifications); pthread_mutex_unlock(&g_mutex); for(size_t i = 0; i < polled_notifications.size(); ++i) { DirWatchNotification::EType type; // TODO: code is actually a bitmask, so this is slightly incorrect switch(polled_notifications[i].code) { case IN_CLOSE_WRITE: type = DirWatchNotification::Changed; break; case IN_CREATE: type = DirWatchNotification::Created; break; case IN_DELETE: type = DirWatchNotification::Deleted; break; default: continue; } DirWatchMap::iterator it = g_paths.find(polled_notifications[i].wd); if(it != g_paths.end()) { OsPath filename = Path(OsString(it->second->path).append(polled_notifications[i].filename)); notifications.push_back(DirWatchNotification(filename, type)); } else { debug_printf("dir_watch_Poll: Notification with invalid watch descriptor wd=%d\n", polled_notifications[i].wd); } } // nothing new; try again later return INFO::OK; }
static void fsevent_callback( ConstFSEventStreamRef UNUSED(streamRef), void * UNUSED(clientCallBackInfo), size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId UNUSED(eventIds)[] ) { unsigned long i; char **paths = (char **)eventPaths; for (i=0; i<numEvents; i++) { bool isWatched = false; OsPath eventPath = OsPath(paths[i]); unsigned long eventType = eventFlags[i]; if ( eventPath.Filename().string().c_str()[0] != '.' ) { for ( DirWatchMap::iterator it = g_Paths.begin() ; it != g_Paths.end(); ++it) if ( path_is_subpath( it->path.string().c_str(), eventPath.string().c_str() ) ) isWatched = true; } if ( ! isWatched ) return; OsPath filename = Path( eventPath.string().c_str() ); if ( eventType & kFSEventStreamEventFlagItemIsFile) { if ( eventType & kFSEventStreamEventFlagItemRemoved ) g_QueuedDirs.push_back(DirWatchNotification( filename.string().c_str(), DirWatchNotification::Deleted )); else if ( eventType & kFSEventStreamEventFlagItemRenamed ) g_QueuedDirs.push_back(DirWatchNotification( filename.string().c_str(), DirWatchNotification::Deleted )); else if ( eventType & kFSEventStreamEventFlagItemCreated ) g_QueuedDirs.push_back(DirWatchNotification( filename.string().c_str(), DirWatchNotification::Created )); else if ( eventType & kFSEventStreamEventFlagItemModified ) g_QueuedDirs.push_back(DirWatchNotification( filename.string().c_str(), DirWatchNotification::Changed )); } } }
Status dir_watch_Add(const OsPath& path, PDirWatch& dirWatch) { PDirWatch tmpDirWatch(new DirWatch); dirWatch.swap(tmpDirWatch); dirWatch->path = path; dirWatch->reqnum = 0; g_Paths.push_back( *dirWatch ); bool alreadyInsideRootPath = false; for ( DirWatchMap::iterator it = g_RootPaths.begin() ; it != g_RootPaths.end(); ++it) { if ( path_is_subpath( path.string().c_str(), it->path.string().c_str() ) ) alreadyInsideRootPath = true; } if ( !alreadyInsideRootPath ) { DeleteEventStream(); g_RootPaths.push_back( *dirWatch ); } return INFO::OK; }
Status dir_watch_Add(const OsPath& path, PDirWatch& dirWatch) { char resolved[PATH_MAX + 1]; // init already failed; don't try again or complain if(initialized == -1) return ERR::FAIL; // NOWARN if(!initialized) { errno = 0; if((inotifyfd = inotify_init()) < 0) { // Check for error ? int err = errno; initialized = -1; LOGERROR("Error initializing inotify file descriptor; hotloading will be disabled, errno=%d", err); errno = err; return StatusFromErrno(); // NOWARN } errno = 0; int ret = pthread_create(&g_event_loop_thread, NULL, &inotify_event_loop, NULL); if (ret != 0) { initialized = -1; LOGERROR("Error creating inotify event loop thread; hotloading will be disabled, err=%d", ret); errno = ret; return StatusFromErrno(); // NOWARN } initialized = 1; atexit(inotify_deinit); } PDirWatch tmpDirWatch(new DirWatch); errno = 0; int wd = inotify_add_watch(inotifyfd, realpath(OsString(path).c_str(), resolved), IN_CREATE | IN_DELETE | IN_CLOSE_WRITE); if (wd < 0) WARN_RETURN(StatusFromErrno()); dirWatch.swap(tmpDirWatch); dirWatch->path = path; dirWatch->reqnum = wd; g_paths.insert(std::make_pair(wd, dirWatch)); return INFO::OK; }