From c4939d736ff55fa48cff31a26333f4922430c3d8 Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Sat, 13 Jun 2026 23:48:14 +0900 Subject: kernel: fix asset hot-reload regression (watch the whole asset dir, not the .rml basename) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The watch_file refactor (35e5d32) moved the substrate's UI-asset hot-reload onto the shared FileWatcher but armed a BASENAME watch on the document's .rml file only. The dock's styling lives in a separately-ed dock.rcss, so editing it (the common case) never matched the watch — asset hot-reload silently stopped working on the real seat (no "dev hot-reload ON" line, no reload on save), while config watching kept working. The ui_reload_surface() seam test passed because it bypassed the real inotify->reload path. Fix: FileWatcher::add_dir watches the document's whole DIRECTORY (so any .rml/.rcss in it triggers the surface reload); the substrate uses it and restores the "dev hot-reload ON (inotify watching asset dir '...')" log. Added an END-TO-END test mirroring the dock (a doc that s a separate .rcss, real inotify event, wl_event_loop pumped, assert the document actually reloaded) — fails on the buggy code, passes now; no more relying on the seam. Real-seat verified: editing dock.rcss now reloads the live dock (border-radius + background-color changes apply on save). kernel 59 cases/260 assertions green on build + build-asan, no new suppressions. Edits confined to packages/kernel/. --- packages/kernel/src/ui_substrate.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'packages/kernel/src/ui_substrate.cpp') 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 `` 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); -- cgit v1.2.3