Mercurial > minori
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) |
