mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-11 22:14:00 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
gatelock pre-commit / pre-commit (push) Waiting to run
gatelock tests / test (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
A keyring prompt (gcr-prompter) that grabbed input before the leetcode lock armed left the lock visible but unclickable: every click went to the prompt hidden under it. Each failed grab_set_global also froze Tk ~1 s, so the lock barely repainted. - Raise known credential prompts above the lock after 2 s of a blocked grab; games and other grabbers stay covered. - Probe the grab on a side Xlib connection first so a blocked grab never stalls the Tk loop (worst gap 1.05 s -> 0.05 s). - Log the Tk grab error and stop blaming our own claim. - 1.5 s startup grace in wait_for_turn, and a weaker armed lock steps back (windowless, re-arms later) when a stronger one is live, so only one lock is ever on screen. - Real-X checks: verify_prompt_yield, verify_grab_responsive, verify_all_lockers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WzxdWZbpxQZj41WpbQPuxB
74 lines
2.4 KiB
Python
Executable File
74 lines
2.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# Copyright (c) 2026 Krzysztof Rudnicki
|
|
# pylint: disable=import-error
|
|
"""A second X client that holds the keyboard+pointer grab, like a credential prompt.
|
|
|
|
Started by ``verify_prompt_yield.py`` on a private display. Maps one window with
|
|
the given ``WM_CLASS`` (optionally inside an override-redirect frame, the way a
|
|
window manager reparents clients), takes ``XGrabKeyboard`` + ``XGrabPointer``,
|
|
prints ``READY``, then waits on stdin: ``release`` ungrabs and unmaps (prints
|
|
``RELEASED``), EOF exits.
|
|
|
|
Usage: ``prompt_grab_helper.py <wm_class> [--framed]``
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import time
|
|
|
|
from Xlib import X, display
|
|
|
|
OWNER_EVENTS = True
|
|
VIEWABLE_TRIES = 50
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
"""Grab, announce, wait for ``release``; returns a process exit status."""
|
|
wm_class = argv[0]
|
|
xdisplay = display.Display()
|
|
root = xdisplay.screen().root
|
|
depth = xdisplay.screen().root_depth
|
|
top = window = root.create_window(
|
|
10, 10, 400, 200, 0, depth, X.InputOutput, X.CopyFromParent
|
|
)
|
|
if "--framed" in argv:
|
|
top = root.create_window(
|
|
10, 10, 400, 200, 0, depth, X.InputOutput, X.CopyFromParent,
|
|
override_redirect=1,
|
|
) # fmt: skip
|
|
window.reparent(top, 0, 0)
|
|
window.set_wm_class(wm_class, wm_class.capitalize())
|
|
window.map()
|
|
top.map()
|
|
xdisplay.sync()
|
|
# A window manager maps asynchronously; grabbing a not-yet-viewable window
|
|
# fails with GrabNotViewable.
|
|
for _ in range(VIEWABLE_TRIES):
|
|
if window.get_attributes().map_state == X.IsViewable:
|
|
break
|
|
time.sleep(0.1)
|
|
modes = (X.GrabModeAsync, X.GrabModeAsync)
|
|
keyboard = window.grab_keyboard(OWNER_EVENTS, *modes, X.CurrentTime)
|
|
pointer = window.grab_pointer(
|
|
OWNER_EVENTS, X.ButtonPressMask, *modes, X.NONE, X.NONE, X.CurrentTime
|
|
)
|
|
if keyboard or pointer:
|
|
sys.stdout.write(f"grab failed keyboard={keyboard} pointer={pointer}\n")
|
|
return 2
|
|
sys.stdout.write("READY\n")
|
|
sys.stdout.flush()
|
|
for line in sys.stdin:
|
|
if line.strip() == "release":
|
|
xdisplay.ungrab_keyboard(X.CurrentTime)
|
|
xdisplay.ungrab_pointer(X.CurrentTime)
|
|
top.unmap()
|
|
xdisplay.sync()
|
|
sys.stdout.write("RELEASED\n")
|
|
sys.stdout.flush()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv[1:]))
|