diff --git a/gatelock/gatelock/_fitcheck.py b/gatelock/gatelock/_fitcheck.py index e5b2196..ed40a19 100644 --- a/gatelock/gatelock/_fitcheck.py +++ b/gatelock/gatelock/_fitcheck.py @@ -109,6 +109,13 @@ def _measure( """Render the screen once and read its height back.""" root = tk.Tk() try: + # A managed window has its geometry rewritten wholesale by a real + # window manager -- confirmed under i3, which tiled this to the pane + # size instead of the requested width/height, silently measuring the + # wrong resolution. Production lock windows are overrideredirect for + # exactly this reason; the throwaway root here must match so the + # measurement is honest regardless of what WM is running the check. + root.overrideredirect(boolean=True) root.geometry(f"{width}x{height}+0+0") root.update_idletasks() surface = ScrollableSurface(root, config or LockConfig(), center_when_fits=True) diff --git a/gatelock/gatelock/tests/test_scrollable.py b/gatelock/gatelock/tests/test_scrollable.py index 6964207..cd17439 100644 --- a/gatelock/gatelock/tests/test_scrollable.py +++ b/gatelock/gatelock/tests/test_scrollable.py @@ -35,6 +35,12 @@ _TALL = 60 def surface() -> tk.Misc: """A 400x200 viewport holding 20 stacked entries -- deliberately taller.""" root = tk.Tk() + # A managed window has its geometry rewritten wholesale by a real window + # manager (i3 confirmed: a plain geometry() request came back tiled to + # the pane size instead of 400x200, so nothing ever overflowed and every + # scroll assertion in this file failed against a real WM session) -- + # exactly the reason production lock windows are overrideredirect too. + root.overrideredirect(boolean=True) root.geometry(f"400x{_VIEWPORT_H}+0+0") # No window manager under Xvfb, so the toplevel must claim X input focus # before any synthetic key event is delivered anywhere. @@ -203,6 +209,9 @@ def test_a_surface_without_a_scrollbar_still_scrolls() -> None: """``show_scrollbar=False`` removes the affordance, not the mechanism.""" root = tk.Tk() try: + # See the `surface` fixture above for why this must be + # overrideredirect under a real WM. + root.overrideredirect(boolean=True) root.geometry(f"400x{_VIEWPORT_H}+0+0") root.focus_force() root.update_idletasks() diff --git a/gatelock/pyproject.toml b/gatelock/pyproject.toml index 1ceade6..93608e9 100644 --- a/gatelock/pyproject.toml +++ b/gatelock/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "gatelock" -version = "0.7.0" +version = "0.7.1" description = "Shared per-output lock-window + HMAC log-integrity backend for blocking-overlay apps" requires-python = ">=3.10" dependencies = [] # pure stdlib — tkinter is bundled with Python