mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 06:00:09 +02:00
Input relay: release keys the desktop has down that no keyboard holds
After a gaze calibration, Meta stayed down in KWin: typing opened the overview, and clicks on the desktop did other things. A key can be left down there when its device vanishes with it held (release_held only let go of it on the relay's own devices; the Z3's keyboard dropped out twice right then) or a release goes astray. The relay now remembers which keys it told ft-screens went down, and once a second releases any no device holds (EVIOCGKEY), and the key combinations forget a Meta or modifier no device holds. A relay that starts releases the modifiers on the desktop, for keys an earlier one left down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
da3e3d66a7
commit
2ae10a3b65
1 file changed
+38
-1
+38
-1
@@ -790,13 +790,16 @@ def main():
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
screens_down = set() # keys the desktop was told went down and not yet up (see reconcile_desktop_keys)
|
||||||
|
|
||||||
def to_screens(code, value):
|
def to_screens(code, value):
|
||||||
"""A key for the desktop screens (ft-screens decides whether it types)."""
|
"""A key for the desktop screens (ft-screens decides whether it types)."""
|
||||||
if value in (0, 1) and code < BTN_MISC:
|
if value in (0, 1) and code < BTN_MISC:
|
||||||
try:
|
try:
|
||||||
screens_sock.sendto(f"key {code} {value}".encode(), SCREENS)
|
screens_sock.sendto(f"key {code} {value}".encode(), SCREENS)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass # ft-screens not running
|
return # ft-screens not running
|
||||||
|
(screens_down.add if value else screens_down.discard)(code)
|
||||||
nodes = {} # fd -> Node
|
nodes = {} # fd -> Node
|
||||||
# Nodes already probed (rejected or open): path -> inode. A device that disconnects and
|
# Nodes already probed (rejected or open): path -> inode. A device that disconnects and
|
||||||
# reconnects between two scans often gets the same event numbers back, so the path alone
|
# reconnects between two scans often gets the same event numbers back, so the path alone
|
||||||
@@ -878,6 +881,35 @@ def main():
|
|||||||
mouse.sync()
|
mouse.sync()
|
||||||
keyboard.sync()
|
keyboard.sync()
|
||||||
|
|
||||||
|
def physically_down():
|
||||||
|
"""The keys down on every device read here, as the kernel has them (EVIOCGKEY)."""
|
||||||
|
down = set()
|
||||||
|
for node in nodes.values():
|
||||||
|
buf = bytearray((KEY_MAX + 8) // 8)
|
||||||
|
try:
|
||||||
|
fcntl.ioctl(node.fd, EVIOCGKEY, buf)
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
down.update(i * 8 + bit for i, b in enumerate(buf) if b for bit in range(8) if b >> bit & 1)
|
||||||
|
return down
|
||||||
|
|
||||||
|
def reconcile_desktop_keys():
|
||||||
|
"""A key the desktop has down that no device holds comes up there, and the key
|
||||||
|
combinations forget a Meta or modifier no device holds. A key can be left down when
|
||||||
|
its device vanishes with it held (release_held only lets go of it here) or a release
|
||||||
|
goes astray: on 2026-10-01, after a calibration, Meta stayed down in KWin, so typing
|
||||||
|
opened the overview and clicks on the desktop did other things."""
|
||||||
|
if not screens_down and not held_meta and not held_modifiers:
|
||||||
|
return
|
||||||
|
down = physically_down()
|
||||||
|
for code in sorted(screens_down - down):
|
||||||
|
to_screens(code, 0)
|
||||||
|
log(f"key {code} released on the desktop: no keyboard holds it")
|
||||||
|
for code in [c for c in held_meta if c not in down]:
|
||||||
|
held_meta.discard(code)
|
||||||
|
meta_hidden.discard(code)
|
||||||
|
held_modifiers.intersection_update({MODIFIERS[c] for c in down if c in MODIFIERS})
|
||||||
|
|
||||||
def keys_down(node):
|
def keys_down(node):
|
||||||
buf = bytearray((KEY_MAX + 8) // 8)
|
buf = bytearray((KEY_MAX + 8) // 8)
|
||||||
try:
|
try:
|
||||||
@@ -1000,11 +1032,16 @@ def main():
|
|||||||
|
|
||||||
vr_bind(time.monotonic()) # a helper that's already running keeps its buttons in step
|
vr_bind(time.monotonic()) # a helper that's already running keeps its buttons in step
|
||||||
waiting = False # a keyboard's grab waits for its keys to come up
|
waiting = False # a keyboard's grab waits for its keys to come up
|
||||||
|
# A relay that went away with a key down left it down on the desktop, where this one
|
||||||
|
# never sent it: modifiers come up there now (a release of a key that isn't down is nothing).
|
||||||
|
for code in sorted(MODIFIERS):
|
||||||
|
to_screens(code, 0)
|
||||||
while True:
|
while True:
|
||||||
now = time.monotonic()
|
now = time.monotonic()
|
||||||
pointer = state["pointer"]
|
pointer = state["pointer"]
|
||||||
if now >= next_scan:
|
if now >= next_scan:
|
||||||
next_scan = now + 1.0
|
next_scan = now + 1.0
|
||||||
|
reconcile_desktop_keys()
|
||||||
current = {}
|
current = {}
|
||||||
for name in os.listdir("/dev/input"):
|
for name in os.listdir("/dev/input"):
|
||||||
if name.startswith("event"):
|
if name.startswith("event"):
|
||||||
|
|||||||
Reference in new issue
Block a user