diff options
Diffstat (limited to 'packages/kernel/src')
| -rw-r--r-- | packages/kernel/src/file_watcher.cpp | 17 | ||||
| -rw-r--r-- | packages/kernel/src/file_watcher.hpp | 10 | ||||
| -rw-r--r-- | packages/kernel/src/ui_substrate.cpp | 23 |
3 files changed, 42 insertions, 8 deletions
diff --git a/packages/kernel/src/file_watcher.cpp b/packages/kernel/src/file_watcher.cpp index ae292c2..c747fe4 100644 --- a/packages/kernel/src/file_watcher.cpp +++ b/packages/kernel/src/file_watcher.cpp @@ -109,6 +109,19 @@ auto FileWatcher::add(const std::string& path, std::function<void()> on_change, return FileWatch(this, token); } +auto FileWatcher::add_dir(const std::string& dir, std::function<void()> on_change, ExtensionId who) + -> FileWatch { + if (!ensure_started()) { + return FileWatch{}; + } + std::string d = dir.empty() ? std::string(".") : dir; + const Token token = ++next_token_; + // Empty basename => match ANY file change in `d`. + entries_.emplace(token, Entry{std::move(d), std::string{}, std::move(on_change), who}); + arm_dir(entries_.at(token).dir); + return FileWatch(this, token); +} + void FileWatcher::remove_watch(Token token) noexcept { auto it = entries_.find(token); if (it == entries_.end()) { @@ -164,7 +177,9 @@ void FileWatcher::on_readable() { std::vector<Token> to_fire; for (const auto& [token, e] : entries_) { for (const auto& [dir, name] : changed) { - if (e.dir == dir && e.basename == name) { + // A file-specific entry matches its basename; a directory entry + // (empty basename) matches ANY file change in its dir. + if (e.dir == dir && (e.basename.empty() || e.basename == name)) { to_fire.push_back(token); break; } diff --git a/packages/kernel/src/file_watcher.hpp b/packages/kernel/src/file_watcher.hpp index fbd6d86..00806db 100644 --- a/packages/kernel/src/file_watcher.hpp +++ b/packages/kernel/src/file_watcher.hpp @@ -55,13 +55,21 @@ public: [[nodiscard]] auto add(const std::string& path, std::function<void()> on_change, ExtensionId who) -> FileWatch; + // Watch the DIRECTORY `dir` for a change to ANY file within it (not a single + // basename). Used by the ui-substrate's asset hot-reload: a document and its + // `<link>`ed RCSS/assets live in the same dir, and editing ANY of them must + // reload — without parsing the document's link set. Same coalescing / error + // isolation / RAII as add(). `dir` should be an absolute directory path. + [[nodiscard]] auto add_dir(const std::string& dir, std::function<void()> on_change, + ExtensionId who) -> FileWatch; + // detail::WatchRegistry — stop the watch with this token (FileWatch dtor). void remove_watch(Token token) noexcept override; private: struct Entry { std::string dir; // watched directory (absolute) - std::string basename; // file within `dir` to match + std::string basename; // file within `dir` to match; EMPTY = any file std::function<void()> on_change; ExtensionId who{}; }; diff --git a/packages/kernel/src/ui_substrate.cpp b/packages/kernel/src/ui_substrate.cpp index 8b7440e..1f289bd 100644 --- a/packages/kernel/src/ui_substrate.cpp +++ b/packages/kernel/src/ui_substrate.cpp @@ -809,14 +809,22 @@ Rml::ElementDocument* Substrate::Impl::load_document_first(Surface& s) { return nullptr; } // Dev-only: register an asset hot-reload watch on the kernel's SHARED - // file watcher (the same machinery Host::watch_file uses). The callback - // flags this surface for reload, applied (coalesced) at the next - // tick_all on the GL context. Only this DECISION is UNBOX_DEV-gated; the - // watcher infra itself is always available. + // file watcher (the same machinery Host::watch_file uses). We watch the + // document's whole DIRECTORY (add_dir), NOT just the .rml basename: + // RmlUi resolves `<link href="x.rcss">` and image srcs relative to the + // document dir, and the dock keeps its dock.rcss BESIDE dock.rml — so a + // change to ANY file in that dir (the .rml OR a linked .rcss/asset) must + // reload. (Watching only the .rml basename was the regression: editing + // dock.rcss fired no reload.) The callback flags this surface for reload, + // applied (coalesced) at the next tick_all on the GL context. Only this + // DECISION is UNBOX_DEV-gated; the watcher infra itself is always + // available (Host::watch_file is ungated). if (hot_reload_enabled() && watcher != nullptr) { Surface* sp = &s; - s.asset_watch = watcher->add( - s.resolved_path, + const std::string dir = + std::filesystem::path(s.resolved_path).parent_path().string(); + s.asset_watch = watcher->add_dir( + dir, [this, sp] { if (std::find(pending_reloads.begin(), pending_reloads.end(), sp) == pending_reloads.end()) { @@ -824,6 +832,9 @@ Rml::ElementDocument* Substrate::Impl::load_document_first(Surface& s) { } }, s.who); + wlr_log(WLR_INFO, + "ui-substrate: dev hot-reload ON (inotify watching asset dir '%s')", + dir.c_str()); } } else { doc = s.context->LoadDocumentFromMemory(s.rml_inline); |
