// gKeyMapper daemon - watches the mappings file for changes (inotify). #ifndef GKEYMAPPER_DAEMON_CONFIG_WATCH_H #define GKEYMAPPER_DAEMON_CONFIG_WATCH_H #include namespace gkeymapper::daemon { // Watches mappings.conf so a save in the GUI is picked up without a signal. // // The *directory* is watched, not the file: that also catches an atomic save // (write a temporary file, rename it over the config) and the config appearing // after it was deleted. // // poll_events() collects whatever happened since the last call and take_change() // reports it once. Because the daemon evaluates this once per loop iteration // (at least every read timeout, i.e. 500 ms), all events a single save produces // collapse into one reload, and the file is complete by the time it is read // (that is what the atomic save in shared/mapping.cpp guarantees). // // The daemon still accepts SIGHUP/SIGUSR1 as a fallback when inotify is // unavailable. class ConfigWatcher { public: ConfigWatcher() = default; ~ConfigWatcher(); ConfigWatcher(const ConfigWatcher&) = delete; ConfigWatcher& operator=(const ConfigWatcher&) = delete; // Watches the directory holding `path` (creating it if it does not exist // yet). Returns false if inotify cannot be set up; the daemon then relies // on signals only. bool init(const std::string& path); bool is_watching() const { return fd_ >= 0; } // Consumes queued inotify events. Non-blocking and cheap; call it once per // event-loop iteration. void poll_events(); // True if the mappings file changed since the last call; clears the mark so // every change is reported exactly once. bool take_change(); private: int fd_ = -1; std::string directory_; std::string name_; bool pending_ = false; }; } // namespace gkeymapper::daemon #endif // GKEYMAPPER_DAEMON_CONFIG_WATCH_H