git.lucas.co / cce-browser
web browser (Servo)
git clone https://git.lucas.co/cce-browser.git

commit8cce9f734d73fba3c92817bb373937c160e54ef7
parenta71280ef1f
authorLucas Galante <[email protected]>
date2026-09-01 10:29
fix: a forwarded open focuses the browser window

Single-instance made link clicks look broken: the tab opened instantly,
but in a window parked wherever the browser last was — off-camera and
unfocused — where a fresh launch used to be focused and panned to by the
compositor. On OpenExternal the app now sends focus-window over the
control socket (focus pans the camera and raises). xdg-activation is not
the route: the compositor deliberately answers it with an attention
notification, not focus.

Co-Authored-By: Claude Fable 5 <[email protected]>

 CLAUDE.md   |  9 +++++++--
 src/main.rs | 10 ++++++++++
 2 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 639acdf..ee7c849 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -74,8 +74,13 @@ with `parse_startup_arg` — it *is* a launch argument, so the URL bar's
 domain-guess parsing stays wrong for it — and a forwarded relative file path is
 canonicalized on the *sending* side, whose cwd it is relative to. Beyond
 tidiness this guards the profile dir: two engines must not share the plaintext
-cookie jar. There is deliberately no `--new-window` yet; raising the existing
-window on forward is also still open.
+cookie jar. There is deliberately no `--new-window` yet. On every forwarded
+open the app asks the compositor to `focus-window cce-browser` over the
+control socket — focus pans the camera to the window, which is what makes a
+forwarded link *visible*; without it the tab opens in a window parked
+off-camera and the click looks inert (that shipped for half a day). The
+compositor's xdg-activation is not the route: it deliberately answers with an
+attention notification, not focus.
 
 The other half of click-to-tab latency is inside WebKit: creating a webview
 and spawning its WebProcess is ~200ms, so the WPE host keeps a hidden **spare
diff --git a/src/main.rs b/src/main.rs
index ef2a3a1..384e1e8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1034,6 +1034,16 @@ impl Application for BrowserApp {
                     }
                     None => self.new_tab(),
                 }
+                // Bring the window to the user: focus + camera pan + raise
+                // over the control socket. A fresh launch used to get this
+                // from the compositor for free; without it the tab opens in
+                // a window parked somewhere off-camera and the click looks
+                // like it did nothing. (xdg-activation is not the route: the
+                // compositor deliberately answers it with an attention
+                // notification, not focus.)
+                std::thread::spawn(|| {
+                    let _ = cce_ui::ipc::send_command("cce", "focus-window cce-browser");
+                });
                 *needs_rebuild = true;
             }
         }