From 6ed0e36960afc13007322bf34bd7349f38cc968b Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Sun, 1 Nov 2026 16:22:15 -0500 Subject: [PATCH] More problems deiscovered from the problem child. :) I think these fixes should improve reliability across the board for reading focused windows though. Needs testing. --- docs/testing/background-events.md | 15 ++++++++++++++- internal/platform/xorg/commands_native.c | 5 ++++- .../platform/xorg/frame_foreground_native_test.go | 2 ++ tests/x11_frame_fixture.py | 9 ++++++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/docs/testing/background-events.md b/docs/testing/background-events.md index f285c5c..5317b44 100644 --- a/docs/testing/background-events.md +++ b/docs/testing/background-events.md @@ -97,11 +97,19 @@ This covers frame/client discrepancies across applications; arbitrary incorrect AT-SPI geometry still requires diagnosis. Rejected activation diagnostics now include `x_frame_bounds` alongside client and advertised outer bounds. +i3 marks its decoration frames `override_redirect`; these frames must pass +the same tree and containment checks. Rejecting that flag discarded the real +frame even after frame lookup was added. A later Slint log showed the Run +dialog and application menu at (0,24), 1916×989, against a client at (2,24), +1912×987, with no recorded frame. This caused control events to be filtered, +while explicit suggestion-count announcements still passed. + ```sh python3 tests/native_test_session.py env CTHULHU_NATIVE_FRAME_FOREGROUND=1 CTHULHU_NATIVE_GRAB_FOREGROUND=1 go test ./internal/platform/xorg -run '^TestNative(ReparentedFrameForeground|InputOnlyGrabForeground)$' -count=1 -v ``` -The frame fixture covers missing, zero and stale extents, borders, moving, +The frame fixture covers i3 override-redirect frames (including the reported +Run dialog geometry), missing, zero and stale extents, borders, moving, resizing, switching to a sibling frame, shared containers and empty workspaces. On Slint, listen while switching between two windows from one application, another application's window, dialogs and menus. Move and resize them, switch @@ -109,3 +117,8 @@ away to an empty workspace and return, and check preferences again. Confirm speech and Braille follow the foreground control and background activity does not take focus. Keep the first rejected activation from a fresh debug log if a window remains silent; private X11 results do not prove affected-host acceptance. + +On Slint/I38, open the Run dialog, type a command, and use Left/Right, Home/End +and Control+Left/Right to review it. Check suggestion navigation as well as +counts. Open Alt+F1 and move through categories and applications, then close +it and verify speech returns to the previous window. diff --git a/internal/platform/xorg/commands_native.c b/internal/platform/xorg/commands_native.c index 2758ff7..9e64801 100644 --- a/internal/platform/xorg/commands_native.c +++ b/internal/platform/xorg/commands_native.c @@ -188,8 +188,11 @@ static void cthulhu_read_frame_bounds(Display *display, Window window, Foregroun XWindowAttributes attributes; if (!XGetWindowAttributes(display, parent, &attributes) || - attributes.class != InputOutput || attributes.override_redirect || + attributes.class != InputOutput || attributes.map_state != IsViewable) return; + // i3's decoration windows are override-redirect: that flag bypasses WM + // management, not foreground ownership. Validate the enclosing frame by + // its tree, managed children and bounds below instead. Atom stateAtom = XInternAtom(display, "WM_STATE", False); Atom type = None; int format = 0; unsigned long items = 0; unsigned char *data = cthulhu_window_property(display, parent, stateAtom, &type, &format, &items); diff --git a/internal/platform/xorg/frame_foreground_native_test.go b/internal/platform/xorg/frame_foreground_native_test.go index 044e7ea..460e31a 100644 --- a/internal/platform/xorg/frame_foreground_native_test.go +++ b/internal/platform/xorg/frame_foreground_native_test.go @@ -62,6 +62,8 @@ func TestNativeReparentedFrameForeground(t *testing.T) { {"resize", "client", [4]int{0, 0, 1000, 700}}, {"move", "client", [4]int{-30, 40, 1916, 994}}, {"border", "client", [4]int{0, 0, 1920, 998}}, + {"i3-frame", "client", [4]int{0, 0, 1916, 994}}, + {"i3-run-dialog", "client", [4]int{0, 24, 1916, 989}}, {"other", "other", [4]int{2000, 0, 500, 400}}, {"plain", "plain", [4]int{}}, {"shared", "shared", [4]int{}}, diff --git a/tests/x11_frame_fixture.py b/tests/x11_frame_fixture.py index 59cdafd..01f2b38 100644 --- a/tests/x11_frame_fixture.py +++ b/tests/x11_frame_fixture.py @@ -45,8 +45,9 @@ windows = dict(client=client, other=other, plain=plain, shared=shared, def configure(action): + frame.change_attributes(override_redirect=False) frame.configure(x=0, y=0, width=1916, height=994, border_width=0) - client.configure(width=1912, height=974) + client.configure(x=2, y=18, width=1912, height=974) client.delete_property(extentsAtom) selected = windows.get(action, client) if action == "zero-extents": @@ -60,6 +61,12 @@ def configure(action): frame.configure(x=-30, y=40) elif action == "border": frame.configure(border_width=2) + elif action == "i3-frame": + frame.change_attributes(override_redirect=True) + elif action == "i3-run-dialog": + frame.change_attributes(override_redirect=True) + frame.configure(y=24, width=1916, height=989) + client.configure(y=0, width=1912, height=987) root.change_property(activeAtom, Xatom.WINDOW, 32, [0 if action == "empty" else selected.id]) connection.sync()