From 434e2bc1d88fdf73d17e856cccae779a29df50eb Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Fri, 30 Oct 2026 16:50:07 -0700 Subject: [PATCH] test(nix): the ring of wallpaper is read outside the frame A port whose test window has a color of its own put it in the ring, and a warm ring beside a cool window read as mixed. Both ports draw the border outside the frame, so what is inside it is not read. The fake leaves room for the ring and follows focus-workspace. BEHAVIOUR.md says what kfw's next has fixed or found since: the maximize, the killed daemon, and the top edge. --- docs/dev/BEHAVIOUR.md | 27 ++++++++++--------- .../komorebi_vm/core/scenarios/wallpaper.py | 10 +++---- nix/tests/vm/lib/tests/fake_wallpaper.py | 9 ++++--- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/docs/dev/BEHAVIOUR.md b/docs/dev/BEHAVIOUR.md index 6c6343ee..42a19ff7 100644 --- a/docs/dev/BEHAVIOUR.md +++ b/docs/dev/BEHAVIOUR.md @@ -261,24 +261,27 @@ counts when it needs them and asks only that the monitor it left shows a workspa Windows runs on `next` (komorebi `6d862a11`), with the decision of what to do with each: - **A window the user maximizes** (`maximizing.user`, the title bar button). kfde takes it as the - workspace's maximized window, as for `toggle-maximize`; kfw sets `maximized_window` only from - `toggle-maximize`, nothing turns a user's maximize into it, so every later check of the scenario - fails on Windows. **Decided: a known difference for now**, neither port changes; the Windows runner - records it as a known failure. + workspace's maximized window, as for `toggle-maximize`. kfw's `next` (aa353a71) does too now, and + the first checks pass. What differs is what the layout commands do while it is maximized: kfde + ignores them all, and kfw, with a window the user maximized, still ignores `move`, `cycle-move`, + `promote` and `unstack`, but `stack left/right` stacks the other windows (two containers become + one) and `toggle-float` floats the window and ends the maximize. **Which is the reference is a + decision, with the maintainer**; the scenario stays as it is and the Windows runner records the + rest as known failures. - **A window moved to another display by something other than a drag** (`hotplug.user_move`, `SetWindowPos` on Windows). kfde follows the window to the other monitor; kfw relocates a window only when a drag ends (`MoveResizeEnd`) and a move by any other means raises no such event, so it is not taken there within 45 s. **Decided: a known difference for now**, recorded as a known failure on Windows. - **A killed daemon** (`lifecycle_hidden.crash`). On kfw, after `kill -9` during a monocle the windows - it cloaked stay cloaked and unmanaged, at their old frames, and the next daemon manages only the - windows that were in view: a clean `stop` uncloaks them, a kill does not, and a starting daemon - does not look for cloaked windows. On KDE it passes (the next start sweeps the parking desktop). - Looks like a kfw bug, with the kfw session's user. -- **Dragging the top edge of the lower tile up** (`resize.edge_cases`, `win3:top:0,-72`). On kfw - nothing moves, while the same shared edge dragged from the other tile (`win2:bottom:0,72`) moves - by what the pointer did; the other seven drags pass. Looks like a kfw bug, with the kfw session's - user. (On KDE all of them pass.) + it cloaked stayed cloaked and unmanaged. Fixed on kfw's `next` (dc9b2e95, uncloaks what a killed + daemon left): it passes 17/0 there. On KDE it passes (the next start sweeps the parking desktop). +- **Dragging the top edge of the lower tile up** (`resize.edge_cases`, `win3:top:0,-72`). Not a kfw + bug: the scenario pressed 4 px outside the frame, and a Windows window has no invisible border on + its top, so the press hit the desktop. `Platform.resize_grab` gives each port its own press point + (KDE outside the frame, Windows 2 px inside the top row); it passes 18/0 on both ports. On kfw + the scenario is still occasionally flaky (a frame that lags its tile): it now waits for the frames + to match the layout before each press. ## What the scenarios had to allow for diff --git a/nix/tests/vm/lib/komorebi_vm/core/scenarios/wallpaper.py b/nix/tests/vm/lib/komorebi_vm/core/scenarios/wallpaper.py index 3ebc9702..76e09834 100644 --- a/nix/tests/vm/lib/komorebi_vm/core/scenarios/wallpaper.py +++ b/nix/tests/vm/lib/komorebi_vm/core/scenarios/wallpaper.py @@ -58,11 +58,11 @@ def hue_of(image: Image.Image) -> str: def ring_hue(frame: Rect, image: Image.Image, reach: int = 12) -> str: - """The hue of the ring around a window: the colors within `reach` pixels of the left edge of - its frame, on either side of it, at its middle. The window's own color is neutral, so what has - a hue is the border; where the ports draw it, outside the frame or on its edge, differs.""" + """The hue of the ring around a window: the colors within `reach` pixels outside the left edge + of its frame, at its middle. Both ports draw the border outside the frame. What is inside it is + the window's own, whatever its color, and is not read.""" seen = set() - for x in range(max(0, frame.x - reach), min(image.size[0], frame.x + reach)): + for x in range(max(0, frame.x - reach), min(image.size[0], frame.x)): red, _, blue = pixels.pixel(image, x, frame.y + frame.h // 2) seen.add(_hue(red, blue)) seen.discard("neither") @@ -104,7 +104,7 @@ def run(platform: Platform, checks: Checks) -> None: image = platform.screenshot(name) f = window.frame y = f.y + f.h // 2 - row = [pixels.pixel(image, x, y) for x in range(max(0, f.x - 12), f.x + 12, 2)] + row = [pixels.pixel(image, x, y) for x in range(max(0, f.x - 12), f.x + 4, 2)] print(f" {client.title} {f}: ring {ring_hue(f, image)}, wanted {hue}; pixels {row}") return False diff --git a/nix/tests/vm/lib/tests/fake_wallpaper.py b/nix/tests/vm/lib/tests/fake_wallpaper.py index ca640c54..a99b6981 100644 --- a/nix/tests/vm/lib/tests/fake_wallpaper.py +++ b/nix/tests/vm/lib/tests/fake_wallpaper.py @@ -11,7 +11,7 @@ from tests.fake_platform import FakePlatform HUES = {"warm": (230, 40, 40), "cool": (40, 60, 230), "other": (60, 230, 60)} -# the ring is the 4 pixels of the wallpaper's hue that the window leaves at its left +# the ring is the wallpaper's hue outside the window, which is the color of what is inside it class FakeWallpaper(FakePlatform): def __init__(self, *args, **kwargs) -> None: # noqa: ANN002, ANN003 super().__init__(*args, **kwargs) @@ -20,6 +20,8 @@ class FakeWallpaper(FakePlatform): self.workspace_wallpapers: dict[int, str] = {} self.workspace = 0 self.puts_back = True + # room outside the first window for its ring + self.workspace_padding = 10 def put_file(self, path: str, data: bytes) -> None: assert data.startswith(b"\x89PNG"), "a picture" @@ -46,7 +48,8 @@ class FakeWallpaper(FakePlatform): def komorebic(self, *args: str) -> str: if args[0] == "focus-workspace": - self.workspace = int(args[1]) + self.workspace = self.current = int(args[1]) + self.focus = min(self.focus, max(0, len(self.containers) - 1)) if self.puts_back or self.shown != self.wallpaper(): self._show() return "" @@ -60,5 +63,5 @@ class FakeWallpaper(FakePlatform): window = self.os_window(client) if window is not None and window.focused: f = window.frame - draw.rectangle((f.x + 4, f.y, f.x + f.w, f.y + f.h), fill=(48, 48, 48)) + draw.rectangle((f.x, f.y, f.x + f.w, f.y + f.h), fill=(48, 48, 48)) return image