├── artwork └── panoptes-logo.jpg ├── external └── catch │ └── CMakeLists.txt ├── include └── pfw │ ├── osx │ ├── OSXHeader.h │ ├── RunLoop.h │ └── FSEventsService.h │ ├── win32 │ ├── WindowsHeader.h │ ├── Controller.h │ ├── Watcher.h │ └── Collector.h │ ├── FileSystemWatcher.h │ ├── Filter.h │ ├── NativeInterface.h │ ├── linux │ ├── Collector.h │ ├── InotifyTree.h │ ├── InotifyService.h │ ├── InotifyEventLoop.h │ └── InotifyNode.h │ ├── internal │ └── definitions.h │ ├── Listener.h │ ├── SingleshotSemaphore.h │ └── Event.h ├── test ├── catch_wrapper.h ├── main.cpp ├── testutil │ ├── test_helper.h │ ├── CoreFoundationHelper.h │ └── FileSandbox.h ├── CMakeLists.txt └── unit │ └── u_FileWatcher.cpp ├── .editorconfig ├── src ├── FileSystemWatcher.cpp ├── NativeInterface.cpp ├── Filter.cpp ├── win32 │ ├── Controller.cpp │ ├── Collector.cpp │ └── Watcher.cpp ├── osx │ ├── RunLoop.cpp │ └── FSEventsService.cpp ├── CMakeLists.txt └── linux │ ├── Collector.cpp │ ├── InotifyService.cpp │ ├── InotifyTree.cpp │ ├── InotifyNode.cpp │ └── InotifyEventLoop.cpp ├── console ├── CMakeLists.txt └── main.cpp ├── .travis.yml ├── .clang-format ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── CMakeLists.txt ├── conan └── conanfile.py ├── cmake └── modules │ ├── RunCatchTest.cmake │ └── RegisterCatchTests.cmake └── README.md /artwork/panoptes-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neXenio/panoptes/HEAD/artwork/panoptes-logo.jpg -------------------------------------------------------------------------------- /external/catch/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(Catch INTERFACE) 2 | target_include_directories(Catch INTERFACE ${CMAKE_CURRENT_LIST_DIR}) 3 | -------------------------------------------------------------------------------- /include/pfw/osx/OSXHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_OSX_HEADER_H 2 | #define PFW_OSX_HEADER_H 3 | 4 | #include 5 | 6 | #endif /* PFW_OSX_HEADER_H */ 7 | -------------------------------------------------------------------------------- /include/pfw/win32/WindowsHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_WINDOWS_HEADER_H 2 | #define PFW_WINDOWS_HEADER_H 3 | 4 | #include 5 | #include 6 | 7 | #endif /* PFW_WINDOWS_HEADER_H */ 8 | -------------------------------------------------------------------------------- /test/catch_wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_CATCH_WRAPPER_H 2 | #define TEST_CATCH_WRAPPER_H 3 | 4 | #define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER 5 | 6 | #include 7 | 8 | #endif // TEST_CATCH_WRAPPER_H 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | # charset = utf-8 10 | indent_style = space 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_RUNNER 2 | 3 | #include "catch_wrapper.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | Catch::Session session; 8 | 9 | int return_code = session.applyCommandLine(argc, argv); 10 | if (return_code != 0) { 11 | return return_code; 12 | } 13 | 14 | return session.run(); 15 | } 16 | -------------------------------------------------------------------------------- /src/FileSystemWatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "pfw/FileSystemWatcher.h" 2 | 3 | using namespace pfw; 4 | 5 | FileSystemWatcher::FileSystemWatcher(const fs::path & path, 6 | std::chrono::milliseconds sleepDuration, 7 | CallBackSignatur callback) 8 | : NativeInterface(path, sleepDuration, callback) 9 | { 10 | } 11 | 12 | FileSystemWatcher::~FileSystemWatcher() {} 13 | -------------------------------------------------------------------------------- /console/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set (UNIT_CONSOLE_NAME "PanoptesConsole") 2 | 3 | set (PANOPTES_CONSOLE_SOURCES 4 | "main.cpp" 5 | ) 6 | 7 | add_executable (${UNIT_CONSOLE_NAME} ${PANOPTES_CONSOLE_SOURCES}) 8 | 9 | target_include_directories(${UNIT_CONSOLE_NAME} PUBLIC ${PANOPTES_CONSOLE}) 10 | 11 | set_target_properties(${UNIT_CONSOLE_NAME} PROPERTIES CXX_STANDARD 17) 12 | 13 | target_link_libraries(${UNIT_CONSOLE_NAME} 14 | PUBLIC ${CMAKE_THREAD_LIBS_INIT} 15 | ${PANOPTES_LIBRARY_NAME}) 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | matrix: 4 | include: 5 | - os: linux 6 | dist: bionic 7 | compiler: gcc 8 | addons: 9 | apt: 10 | sources: 11 | - sourceline: "ppa:ubuntu-toolchain-r/test" 12 | packages: 13 | - g++-9 14 | - gcc-9 15 | env: 16 | - CC=gcc-9 17 | - CXX=g++-9 18 | - os: osx 19 | osx_image: xcode11.5 20 | - os: windows 21 | 22 | script: 23 | - mkdir build && cd build 24 | - cmake -DBUILD_TESTS=yes .. 25 | - cmake --build . --target parallel_check -------------------------------------------------------------------------------- /src/NativeInterface.cpp: -------------------------------------------------------------------------------- 1 | #include "pfw/NativeInterface.h" 2 | 3 | using namespace pfw; 4 | 5 | NativeInterface::NativeInterface(const fs::path & path, 6 | const std::chrono::milliseconds latency, 7 | CallBackSignatur callback) 8 | : _filter(std::make_shared(callback)) 9 | { 10 | _nativeInterface.reset(new NativeImplementation(_filter, path, latency)); 11 | } 12 | 13 | NativeInterface::~NativeInterface() { _nativeInterface.reset(); } 14 | 15 | bool NativeInterface::isWatching() { return _nativeInterface->isWatching(); } 16 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | 4 | AlignConsecutiveAssignments: true 5 | AllowShortBlocksOnASingleLine: true 6 | AlignConsecutiveDeclarations: true 7 | AlwaysBreakTemplateDeclarations: true 8 | BinPackParameters: false 9 | BreakBeforeInheritanceComma: true 10 | BreakConstructorInitializers: BeforeComma 11 | 12 | BraceWrapping: 13 | AfterClass: true 14 | AfterFunction: true 15 | BreakBeforeBraces: Custom 16 | 17 | IndentWidth: 4 18 | NamespaceIndentation: None 19 | PointerAlignment: Right 20 | PointerBindsToType: true 21 | SpacesBeforeTrailingComments: 2 22 | -------------------------------------------------------------------------------- /include/pfw/FileSystemWatcher.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_FILESYSTEM_WATCHER_H 2 | #define PFW_FILESYSTEM_WATCHER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "pfw/Filter.h" 12 | #include "pfw/NativeInterface.h" 13 | 14 | namespace pfw { 15 | 16 | class FileSystemWatcher : public NativeInterface 17 | { 18 | public: 19 | FileSystemWatcher(const fs::path &path, 20 | std::chrono::milliseconds sleepDuration, 21 | CallBackSignatur callback); 22 | ~FileSystemWatcher(); 23 | }; 24 | 25 | } // namespace pfw 26 | 27 | #endif /* PFW_FILESYSTEM_WATCHER_H */ 28 | -------------------------------------------------------------------------------- /src/Filter.cpp: -------------------------------------------------------------------------------- 1 | #include "pfw/Filter.h" 2 | #include 3 | 4 | #pragma unmanaged 5 | 6 | using namespace pfw; 7 | 8 | Filter::Filter(CallBackSignatur callBack) 9 | { 10 | mCallbackHandle = registerCallback(callBack); 11 | } 12 | 13 | Filter::~Filter() { deregisterCallback(mCallbackHandle); } 14 | 15 | void Filter::sendError(const std::string &errorMsg) 16 | { 17 | std::vector events; 18 | events.emplace_back(std::make_unique(EventType::FAILED, errorMsg)); 19 | notify(std::move(events)); 20 | } 21 | 22 | void Filter::filterAndNotify(std::vector &&events) 23 | { 24 | if (events.empty()) { 25 | return; 26 | } 27 | notify(std::move(events)); 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ${{ matrix.os }} 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | os: [windows-latest, macos-latest, ubuntu-20.04] 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: ilammy/msvc-dev-cmd@v1 13 | if: runner.os == 'Windows' 14 | - name: Generating Makefiles 15 | shell: bash 16 | run: | 17 | if [[ "${{ runner.os }}" == "Windows" ]] ; then 18 | cmake . -G "NMake Makefiles" -DBUILD_TESTS=ON 19 | else 20 | cmake . -G "Unix Makefiles" -DBUILD_TESTS=ON 21 | fi 22 | - name: Building 23 | shell: bash 24 | run: | 25 | cmake --build . --target parallel_check 26 | -------------------------------------------------------------------------------- /include/pfw/Filter.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_FILTER_H 2 | #define PFW_FILTER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "pfw/Event.h" 10 | #include "pfw/Listener.h" 11 | 12 | namespace pfw { 13 | 14 | using CallBackSignatur = 15 | std::function> &&)>; 16 | 17 | class Filter : public Listener 18 | { 19 | public: 20 | Filter(CallBackSignatur callBack); 21 | ~Filter(); 22 | 23 | void sendError(const std::string &errorMsg); 24 | void filterAndNotify(std::vector &&events); 25 | 26 | private: 27 | Listener::CallbackHandle mCallbackHandle; 28 | }; 29 | 30 | using FilterPtr = std::shared_ptr; 31 | 32 | } // namespace pfw 33 | 34 | #endif /* PFW_FILTER_H */ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | build 29 | lib 30 | install 31 | 32 | # extraneous operating system items 33 | .DS_Store 34 | 35 | # ignore test folders 36 | nsfw-stress-test 37 | mockfs 38 | compiled 39 | -------------------------------------------------------------------------------- /include/pfw/win32/Controller.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_CONTROLLER_H 2 | #define PFW_CONTROLLER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pfw/win32/Collector.h" 9 | #include "pfw/win32/Watcher.h" 10 | #include "pfw/win32/WindowsHeader.h" 11 | 12 | namespace pfw { 13 | 14 | class Controller 15 | { 16 | public: 17 | Controller(FilterPtr filter, 18 | const std::filesystem::path & path, 19 | const std::chrono::milliseconds latency); 20 | ~Controller(); 21 | 22 | bool isWatching(); 23 | 24 | private: 25 | std::unique_ptr mWatcher; 26 | std::shared_ptr mCollector; 27 | 28 | HANDLE openDirectory(const std::filesystem::path &path); 29 | HANDLE mDirectoryHandle; 30 | }; 31 | 32 | } // namespace pfw 33 | 34 | #endif /* PFW_CONTROLLER_H */ 35 | -------------------------------------------------------------------------------- /include/pfw/win32/Watcher.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_WATCHER_H 2 | #define PFW_WATCHER_H 3 | 4 | #include 5 | 6 | #include "pfw/SingleshotSemaphore.h" 7 | #include "pfw/win32/Collector.h" 8 | #include "pfw/win32/WindowsHeader.h" 9 | 10 | namespace pfw { 11 | 12 | class Watcher 13 | { 14 | public: 15 | Watcher(std::shared_ptr collector, HANDLE dirHandle); 16 | ~Watcher(); 17 | 18 | bool isRunning() const { return mRunning; } 19 | 20 | private: 21 | void run(); 22 | bool pollDirectoryChanges(); 23 | void start(); 24 | void stop(); 25 | 26 | void eventCallback(DWORD errorCode); 27 | void handleEvents(); 28 | 29 | std::atomic mRunning; 30 | SingleshotSemaphore mHasStartedSemaphore; 31 | 32 | std::shared_ptr mCollector; 33 | HANDLE mDirectoryHandle; 34 | 35 | std::vector mBuffer; 36 | OVERLAPPED mOverlapped; 37 | 38 | std::thread mRunner; 39 | }; 40 | 41 | } // namespace pfw 42 | 43 | #endif /* PFW_WATCHER_H */ 44 | -------------------------------------------------------------------------------- /include/pfw/win32/Collector.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_COLLECTOR_H 2 | #define PFW_COLLECTOR_H 3 | 4 | #include "pfw/win32/WindowsHeader.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "pfw/Filter.h" 12 | 13 | namespace pfw { 14 | 15 | class Collector 16 | { 17 | public: 18 | Collector(FilterPtr filter, std::chrono::milliseconds sleepDuration); 19 | ~Collector(); 20 | 21 | void sendError(const std::string &errorMsg); 22 | void insert(std::vector &&events); 23 | void push_back(EventType type, const std::filesystem::path &relativePath); 24 | 25 | private: 26 | void sendEvents(); 27 | 28 | std::mutex _event_input_mutex; 29 | std::vector _inputVector; 30 | 31 | FilterPtr _filter; 32 | 33 | std::thread _runner; 34 | std::chrono::milliseconds _sleepDuration; 35 | HANDLE _stopEvent; 36 | std::atomic _inDestruction{false}; 37 | }; 38 | 39 | } // namespace pfw 40 | 41 | #endif /* PFW_COLLECTOR_H */ 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 neXenio 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /include/pfw/NativeInterface.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_NATIVE_INTERFACE_H 2 | #define PFW_NATIVE_INTERFACE_H 3 | 4 | #include "pfw/internal/definitions.h" 5 | 6 | #ifdef PFW_WINDOWS 7 | #include "pfw/win32/Controller.h" 8 | using NativeImplementation = pfw::Controller; 9 | #elif PFW_APPLE 10 | #include "pfw/osx/FSEventsService.h" 11 | using NativeImplementation = pfw::FSEventsService; 12 | #elif PFW_LINUX 13 | #include "pfw/linux/InotifyService.h" 14 | using NativeImplementation = pfw::InotifyService; 15 | #endif 16 | 17 | #include "pfw/Filter.h" 18 | #include 19 | 20 | namespace pfw { 21 | namespace fs = std::filesystem; 22 | 23 | class NativeInterface 24 | { 25 | public: 26 | NativeInterface(const fs::path & path, 27 | const std::chrono::milliseconds latency, 28 | CallBackSignatur callback); 29 | ~NativeInterface(); 30 | 31 | bool isWatching(); 32 | 33 | private: 34 | std::shared_ptr _filter; 35 | std::unique_ptr _nativeInterface; 36 | }; 37 | } // namespace pfw 38 | 39 | #endif /* PFW_NATIVE_INTERFACE_H */ 40 | -------------------------------------------------------------------------------- /include/pfw/linux/Collector.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_COLLECTOR_H 2 | #define PFW_COLLECTOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "pfw/Filter.h" 11 | #include "pfw/SingleshotSemaphore.h" 12 | 13 | namespace pfw { 14 | 15 | class Collector 16 | { 17 | public: 18 | Collector(std::shared_ptr filter, 19 | std::chrono::milliseconds sleepDuration); 20 | ~Collector(); 21 | 22 | static void finish(void *args); 23 | static void *work(void *args); 24 | 25 | void sendError(const std::string &errorMsg); 26 | void insert(std::vector &&events); 27 | void push_back(EventType type, const std::filesystem::path &relativePath); 28 | 29 | private: 30 | void sendEvents(); 31 | 32 | std::shared_ptr mFilter; 33 | std::chrono::milliseconds mSleepDuration; 34 | pthread_t mRunner; 35 | std::atomic mStopped; 36 | std::vector inputVector; 37 | std::mutex event_input_mutex; 38 | }; 39 | 40 | } // namespace pfw 41 | 42 | #endif /* PFW_COLLECTOR_H */ -------------------------------------------------------------------------------- /include/pfw/osx/RunLoop.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_RUN_LOOP_H 2 | #define PFW_RUN_LOOP_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pfw/SingleshotSemaphore.h" 9 | #include "pfw/osx/OSXHeader.h" 10 | 11 | namespace pfw { 12 | 13 | class FSEventsService; 14 | 15 | class RunLoop 16 | { 17 | public: 18 | RunLoop(FSEventsService * eventsService, 19 | const std::filesystem::path & path, 20 | const std::chrono::milliseconds &latency); 21 | 22 | bool isLooping(); 23 | 24 | ~RunLoop(); 25 | 26 | private: 27 | void work(); 28 | 29 | FSEventsService * mEventsService; 30 | FSEventStreamRef mEventStream; 31 | std::atomic mExited; 32 | std::filesystem::path mPath; 33 | const std::chrono::milliseconds mLatency; 34 | CFRunLoopRef mRunLoop; 35 | std::thread mRunLoopThread; 36 | SingleshotSemaphore mReadyForCleanup; 37 | std::atomic mStarted; 38 | std::atomic mShutdown; 39 | }; 40 | 41 | } // namespace pfw 42 | 43 | #endif /* PFW_RUN_LOOP_H */ 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.7.0 FATAL_ERROR) 2 | set (PROJECT_NAME "Panoptes") 3 | project (${PROJECT_NAME}) 4 | 5 | message (STATUS "Running CMake version ${CMAKE_VERSION}") 6 | message (STATUS "Compiler version is: ${CMAKE_CXX_COMPILER_VERSION}") 7 | 8 | # 9 | # Add some cxx flags 10 | # 11 | 12 | set(CMAKE_CXX_STANDARD 17) 13 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 14 | 15 | # 16 | # Define some options 17 | # 18 | 19 | option (BUILD_TESTS "Build the ${PROJECT_NAME} test binaries" OFF) 20 | 21 | # 22 | # Configure Sources. 23 | # 24 | 25 | set (PANOPTES_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") 26 | set (PANOPTES_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src") 27 | set (PANOPTES_CONSOLE "${CMAKE_CURRENT_SOURCE_DIR}/console") 28 | set (PANOPTES_TESTING "${CMAKE_CURRENT_SOURCE_DIR}/test") 29 | set (PANOPTES_LIBRARY_NAME "PanoptesFW") 30 | set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") 31 | 32 | if (APPLE) 33 | find_library(CORE_SERVICES CoreServices) 34 | if (NOT CORE_SERVICES) 35 | message(FATAL ERROR "Did not find required macOS frameworks.") 36 | endif (NOT CORE_SERVICES) 37 | endif (APPLE) 38 | 39 | add_subdirectory (${PANOPTES_SRC}) 40 | add_subdirectory (${PANOPTES_CONSOLE}) 41 | if (BUILD_TESTS) 42 | enable_testing() 43 | add_subdirectory (${PANOPTES_TESTING}) 44 | endif (BUILD_TESTS) 45 | -------------------------------------------------------------------------------- /console/main.cpp: -------------------------------------------------------------------------------- 1 | #include "pfw/FileSystemWatcher.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Listener 9 | { 10 | public: 11 | Listener(std::filesystem::path path) 12 | { 13 | _watcher = std::make_unique( 14 | path, std::chrono::milliseconds(1), 15 | std::bind(&Listener::listenerFunction, this, 16 | std::placeholders::_1)); 17 | } 18 | void listenerFunction(std::vector events) 19 | { 20 | for (const auto &event : events) { 21 | std::bitset<16> typeBits(event->type); 22 | std::cout << event->relativePath << " with the type: " << typeBits 23 | << std::endl; 24 | } 25 | } 26 | 27 | private: 28 | std::unique_ptr _watcher; 29 | }; 30 | 31 | int main(int argc, char *argv[]) 32 | { 33 | if (argc < 2) { 34 | std::cout << "one input parameter is needed" << std::endl; 35 | return 1; 36 | } 37 | 38 | const auto path = std::filesystem::path(argv[1]); 39 | 40 | std::cout << "observed path is '" << path.string() << "'" << std::endl; 41 | 42 | auto listenerInstance = Listener(path); 43 | 44 | std::cout << "Press any key to finish the observation!" << std::endl; 45 | std::cin.ignore(); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/win32/Controller.cpp: -------------------------------------------------------------------------------- 1 | #include "pfw/win32/Controller.h" 2 | 3 | namespace fs = std::filesystem; 4 | using namespace pfw; 5 | 6 | HANDLE Controller::openDirectory(const fs::path &path) 7 | { 8 | return CreateFileW(path.c_str(), FILE_LIST_DIRECTORY, 9 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 10 | NULL, OPEN_EXISTING, 11 | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 12 | } 13 | 14 | Controller::Controller(FilterPtr filter, 15 | const fs::path & path, 16 | const std::chrono::milliseconds latency) 17 | : mDirectoryHandle(INVALID_HANDLE_VALUE) 18 | , mWatcher(nullptr) 19 | , mCollector(std::make_shared(filter, latency)) 20 | { 21 | mDirectoryHandle = openDirectory(path); 22 | 23 | if (mDirectoryHandle == INVALID_HANDLE_VALUE) { 24 | mCollector->sendError("Failed to open directory."); 25 | return; 26 | } 27 | 28 | mWatcher.reset(new Watcher(mCollector, mDirectoryHandle)); 29 | } 30 | 31 | Controller::~Controller() 32 | { 33 | mWatcher.reset(); 34 | CancelIo(mDirectoryHandle); 35 | CloseHandle(mDirectoryHandle); 36 | mDirectoryHandle = INVALID_HANDLE_VALUE; 37 | } 38 | 39 | bool Controller::isWatching() 40 | { 41 | return (bool)mWatcher && mWatcher->isRunning(); 42 | } 43 | -------------------------------------------------------------------------------- /include/pfw/internal/definitions.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_DEFINITIONS_H 2 | #define PFW_DEFINITIONS_H 3 | 4 | /** 5 | * Figure out which platform we are compiling on. 6 | * Afterwards one (or more) of the following constants will be defined: 7 | * * PFW_WINDOWS - any kind of windows platform 8 | * * PFW_WIN32 - 32bit windows 9 | * * PFW_WIN64 - 64bit windows 10 | * * PFW_POSIX - POSIX compliant operating system 11 | * * PFW_UNIX - Unix 12 | * * PFW_APPLE - macOS 13 | * * PFW_LINUX - Linux 14 | */ 15 | #ifdef _WIN32 16 | #define PFW_WINDOWS 1 17 | #ifdef _WIN64 18 | #define PFW_WIN64 1 19 | #else 20 | #define PFW_WIN32 1 21 | #endif 22 | #elif __APPLE__ 23 | #include "TargetConditionals.h" 24 | #if TARGET_OS_MAC 25 | #define PFW_APPLE 1 26 | #define PFW_UNIX 1 27 | #define PFW_POSIX 1 28 | #else 29 | #error "unknown or unsupported Apple platform" 30 | #endif 31 | #elif __linux__ 32 | #define PFW_UNIX 1 33 | #define PFW_LINUX 1 34 | #define PFW_POSIX 1 35 | #elif __unix__ 36 | #define PFW_UNIX 1 37 | #define PFW_POSIX 1 38 | #elif defined(_POSIX_VERSION) 39 | #define PFW_POSIX 1 40 | #else 41 | #error "unknown or unsupported platform" 42 | #endif 43 | 44 | #ifdef __clang__ 45 | #define PFW_CLANG 1 46 | #elif __GNUC__ 47 | #define PFW_GCC 1 48 | #elif _MSC_VER 49 | #define PFW_MSVC 1 50 | #else 51 | #error "unknown or unsupported compiler" 52 | #endif 53 | 54 | #endif /* PFW_DEFINITIONS_H */ 55 | -------------------------------------------------------------------------------- /include/pfw/osx/FSEventsService.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_FS_EVENTS_SERVICE_H 2 | #define PFW_FS_EVENTS_SERVICE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "pfw/Filter.h" 13 | #include "pfw/osx/OSXHeader.h" 14 | 15 | namespace pfw { 16 | 17 | class RunLoop; 18 | 19 | class FSEventsService 20 | { 21 | public: 22 | FSEventsService(std::shared_ptr filter, 23 | std::filesystem::path path, 24 | const std::chrono::milliseconds &latency); 25 | 26 | static void 27 | FSEventsServiceCallback(ConstFSEventStreamRef streamRef, 28 | void * clientCallBackInfo, 29 | size_t numEvents, 30 | void * eventPaths, 31 | const FSEventStreamEventFlags eventFlags[], 32 | const FSEventStreamEventId eventIds[]); 33 | 34 | void sendError(const std::string &errorMsg); 35 | bool isWatching(); 36 | const std::filesystem::path &rootPath(); 37 | 38 | ~FSEventsService(); 39 | 40 | private: 41 | void dispatch(std::vector &&events); 42 | 43 | std::filesystem::path mPath; 44 | RunLoop * mRunLoop; 45 | std::shared_ptr mFilter; 46 | }; 47 | 48 | } // namespace pfw 49 | 50 | #endif /* PFW_FS_EVENTS_SERVICE_H */ -------------------------------------------------------------------------------- /test/testutil/test_helper.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_TEST_HELPER_H 2 | #define PFW_TEST_HELPER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #if defined(_WIN32) 10 | #include 11 | #include 12 | #endif 13 | #include 14 | #include 15 | #include 16 | 17 | class DummyDirectory { 18 | std::string path; 19 | 20 | public: 21 | static bool createDirectory(const std::string &dir) 22 | { 23 | int nError = 0; 24 | #if defined(_WIN32) 25 | nError = _mkdir(dir.c_str()); 26 | #else 27 | nError = mkdir(dir.c_str(), 0733); 28 | #endif 29 | return nError == 0 || errno == EEXIST; 30 | } 31 | 32 | DummyDirectory(const std::string &path) : path(path) { 33 | createDirectory(path); 34 | } 35 | 36 | void rename(const std::string &newPath) { 37 | std::rename(path.c_str(), newPath.c_str()); 38 | path = newPath; 39 | } 40 | 41 | ~DummyDirectory() { std::remove(path.c_str()); } 42 | }; 43 | 44 | class DummyFile { 45 | std::string path; 46 | 47 | public: 48 | DummyFile(const std::string &path) : path(path) { 49 | std::ofstream(path.c_str()); 50 | } 51 | 52 | void rename(const std::string &newPath) { 53 | std::rename(path.c_str(), newPath.c_str()); 54 | path = newPath; 55 | } 56 | 57 | void modify(const std::string &payload) { 58 | std::ofstream ofs(path.c_str()); 59 | ofs << payload; 60 | } 61 | 62 | ~DummyFile() { std::remove(path.c_str()); } 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /conan/conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake, tools 2 | from os import path 3 | 4 | class PanoptesConan(ConanFile): 5 | name = "panoptes" 6 | version = "1.0.4" 7 | license = "MIT" 8 | url = "https://github.com/neXenio/panoptes.git" 9 | author = "Mathias Eggert " 10 | settings = "os", "compiler", "build_type", "arch" 11 | options = {"shared": [True, False]} 12 | default_options = "shared=False" 13 | description = "cross-platform filewatcher library for C++17 using std::filesystem and native interfaces" 14 | generators = "cmake" 15 | 16 | def source(self): 17 | g = tools.Git(folder="source") 18 | g.clone(self.url, branch="master") 19 | g.checkout("v" + self.version) 20 | 21 | def build(self): 22 | cmake = CMake(self) 23 | cmake.configure(source_folder=path.join(self.source_folder, "source")) 24 | cmake.build() 25 | cmake.install() 26 | 27 | def package(self): 28 | build_dir = path.join(self.source_folder, "source") 29 | self.copy(pattern="*", dst="include", src=path.join(build_dir, "include")) 30 | self.copy(pattern="*.lib", dst="lib", src=path.join(build_dir, "lib"), keep_path=False) 31 | self.copy(pattern="*.a", dst="lib", src=path.join(build_dir, "lib"), keep_path=False) 32 | self.copy(pattern="*.so", dst="lib", src=path.join(build_dir, "lib"), keep_path=False) 33 | self.copy(pattern="*.dylib", dst="lib", src=path.join(build_dir, "lib"), keep_path=False) 34 | self.copy(pattern="*.dll", dst="bin", src=path.join(build_dir, "bin"), keep_path=False) 35 | self.copy(pattern="LICENSE", dst='licenses', src=build_dir, ignore_case=True, keep_path=False) 36 | 37 | def package_info(self): 38 | self.cpp_info.libs = ["PanoptesFW"] -------------------------------------------------------------------------------- /include/pfw/Listener.h: -------------------------------------------------------------------------------- 1 | #ifndef PFW_LISTENER_H 2 | #define PFW_LISTENER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace pfw { 11 | 12 | // see https://stackoverflow.com/questions/24691777 13 | template