comparison src/core/filesystem.cc @ 406:31ce85df55a8 default tip

filesystem: add mac os x directory watcher this code is incredibly stinky tbh we should probably be using C++ threads everywhere else just because they are SO much easier to code for than the shitty Qt threads API
author Paper <paper@tflc.us>
date Mon, 19 Jan 2026 22:48:56 -0500
parents e561b7542b7b
children
comparison
equal deleted inserted replaced
405:4562bc8bfdff 406:31ce85df55a8
12 #elif defined(HAVE_INOTIFY) 12 #elif defined(HAVE_INOTIFY)
13 /* ehhhh */ 13 /* ehhhh */
14 # include <fcntl.h> 14 # include <fcntl.h>
15 # include <unistd.h> 15 # include <unistd.h>
16 # include <sys/inotify.h> 16 # include <sys/inotify.h>
17 #elif defined(__MACH__) && defined(__APPLE__)
18 # include <CoreFoundation/CoreFoundation.h>
19 # include <CoreServices/CoreServices.h>
20 # include <mutex>
21 # include <thread>
17 #endif 22 #endif
18 23
24 #include <QDebug>
19 #include <iostream> 25 #include <iostream>
20 26
21 namespace Filesystem { 27 namespace Filesystem {
22 28
23 /* this runs fs::create_directories() on the 29 /* this runs fs::create_directories() on the
583 struct inotify_event *ev_; 589 struct inotify_event *ev_;
584 std::size_t ev_size_; 590 std::size_t ev_size_;
585 }; 591 };
586 592
587 using DefaultWatcher = InotifyWatcher; 593 using DefaultWatcher = InotifyWatcher;
594 #elif defined(__APPLE__) && defined(__MACH__)
595 class FSEventsWatcher : public StdFilesystemWatcher {
596 public:
597 FSEventsWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive)
598 : StdFilesystemWatcher(opaque, path, handler, recursive)
599 , first_(true)
600 {
601 FSEventStreamContext ctx;
602
603 ctx.copyDescription = NULL;
604 ctx.info = this;
605 ctx.release = NULL;
606 ctx.retain = NULL;
607 ctx.version = 0;
608
609 CFStringRef str = Strings::ToCFString(path.u8string());
610 CFArrayRef arr = CFArrayCreate(kCFAllocatorDefault, reinterpret_cast<const void **>(&str), 1, &kCFTypeArrayCallBacks);
611
612 stream_ = FSEventStreamCreate(kCFAllocatorDefault, callback_static, &ctx, arr, kFSEventStreamEventIdSinceNow, 0.5, 0);
613
614 // kill these off now
615 CFRelease(str);
616 CFRelease(arr);
617
618 std::thread rl(runloop_thread_static, this);
619 std::swap(runloop_thread_, rl);
620 }
621
622 virtual ~FSEventsWatcher() override
623 {
624 CFRunLoopStop(run_loop_);
625 runloop_thread_.join();
626
627 FSEventStreamUnscheduleFromRunLoop(stream_, run_loop_, kCFRunLoopDefaultMode);
628 FSEventStreamStop(stream_);
629 FSEventStreamRelease(stream_);
630 }
631
632 virtual void Process() override
633 {
634 if (first_) {
635 StdFilesystemWatcher::Process();
636 first_ = false;
637 }
638
639 const std::lock_guard<std::recursive_mutex> lock(events_mutex_);
640
641 // TODO do this properly
642 if (rescan_.size()) {
643 StdFilesystemWatcher::Process();
644 rescan_.clear();
645 }
646
647 // send all the queued events then clear it
648 for (const auto &ev : events_)
649 Watcher::handler_(Watcher::opaque_, ev.path, ev.type);
650 events_.clear();
651 }
652
653 private:
654 void callback(ConstFSEventStreamRef streamRef, std::size_t numEvents, void *eventPaths, const FSEventStreamEventFlags *eventFlags, const FSEventStreamEventId *eventIds)
655 {
656 // assert(streamRef == stream_);
657
658 #if 0
659 for (std::size_t i = 0; i < numEvents; i++) {
660 if ((eventFlags[i] == 0) || (eventFlags[i] & (kFSEventStreamEventFlagItemCreated|kFSEventStreamEventFlagItemRemoved|kFSEventStreamEventFlagMustScanSubDirs))) {
661 EventInfo ev;
662
663 ev.path = std::filesystem::path(reinterpret_cast<const char **>(eventPaths)[i]);
664
665 const std::lock_guard<std::recursive_mutex> lock(events_mutex_);
666
667 if ((eventFlags[i] == 0) || (eventFlags[i] & kFSEventStreamEventFlagMustScanSubDirs))
668 rescan_.push_back(ev.path);
669
670 if (eventFlags[i] & kFSEventStreamEventFlagItemCreated) {
671 ev.type = IWatcher::Created;
672 events_.push_back(ev);
673 }
674
675 if (eventFlags[i] & kFSEventStreamEventFlagItemRemoved) {
676 ev.type = IWatcher::Deleted;
677 events_.push_back(ev);
678 }
679 }
680 }
681 #else
682 /* I only evr get eventFlags[i] == 0 so I think this is
683 * probably the best way ??? */
684 rescan_.push_back(Watcher::path_);
685 #endif
686 }
687
688 static void callback_static(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, std::size_t numEvents, void *eventPaths, const FSEventStreamEventFlags *eventFlags, const FSEventStreamEventId *eventIds)
689 {
690 reinterpret_cast<FSEventsWatcher *>(clientCallBackInfo)->callback(streamRef, numEvents, eventPaths, eventFlags, eventIds);
691 }
692
693 void runloop_thread()
694 {
695 {
696 const std::lock_guard<std::recursive_mutex> lock(events_mutex_);
697 run_loop_ = CFRunLoopGetCurrent();
698 FSEventStreamScheduleWithRunLoop(stream_, run_loop_, kCFRunLoopDefaultMode);
699 FSEventStreamStart(stream_);
700 }
701
702 CFRunLoopRun();
703
704 // now.. loop was stopped from destructor.
705 // we don't need to do anything; destructor
706 // will handle everything from here
707 }
708
709 static void runloop_thread_static(FSEventsWatcher *e)
710 {
711 e->runloop_thread();
712 }
713
714 struct EventInfo {
715 enum Event type;
716 std::filesystem::path path;
717 };
718
719 std::vector<struct EventInfo> events_;
720 std::recursive_mutex events_mutex_;
721 FSEventStreamRef stream_;
722 std::thread runloop_thread_;
723 CFRunLoopRef run_loop_;
724 bool first_;
725 std::vector<std::filesystem::path> rescan_;
726 };
727
728 using DefaultWatcher = FSEventsWatcher;
588 #else 729 #else
589 using DefaultWatcher = StdFilesystemWatcher; 730 using DefaultWatcher = StdFilesystemWatcher;
590 #endif 731 #endif
591 732
592 IWatcher *GetRecursiveFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler) 733 IWatcher *GetRecursiveFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler)