Scope Esc cancel listener to active recording

This commit is contained in:
Thales Maciel 2026-02-26 16:28:49 -03:00
parent e5d709a393
commit 64c8c26bce
6 changed files with 105 additions and 7 deletions

View file

@ -42,6 +42,9 @@ class X11Adapter:
self.indicator = None
self.status_icon = None
self.menu = None
self._cancel_listener_lock = threading.Lock()
self._cancel_listener_stop_event: threading.Event | None = None
self._cancel_listener_callback: Callable[[], None] | None = None
if AppIndicator3 is not None:
self.indicator = AppIndicator3.Indicator.new(
"aman",
@ -72,9 +75,35 @@ class X11Adapter:
def start_cancel_listener(self, callback: Callable[[], None]) -> None:
mods, keysym = self._parse_hotkey("Escape")
thread = threading.Thread(target=self._listen, args=(mods, keysym, callback), daemon=True)
with self._cancel_listener_lock:
if self._cancel_listener_stop_event is not None:
self._cancel_listener_callback = callback
return
stop_event = threading.Event()
self._cancel_listener_stop_event = stop_event
self._cancel_listener_callback = callback
thread = threading.Thread(
target=self._listen,
args=(mods, keysym, self._dispatch_cancel_listener, stop_event),
daemon=True,
)
thread.start()
def stop_cancel_listener(self) -> None:
with self._cancel_listener_lock:
stop_event = self._cancel_listener_stop_event
self._cancel_listener_stop_event = None
self._cancel_listener_callback = None
if stop_event is not None:
stop_event.set()
def _dispatch_cancel_listener(self) -> None:
with self._cancel_listener_lock:
callback = self._cancel_listener_callback
if callback is not None:
callback()
def inject_text(
self,
text: str,
@ -126,7 +155,14 @@ class X11Adapter:
finally:
self.request_quit()
def _listen(self, mods: int, keysym: int, callback: Callable[[], None]) -> None:
def _listen(
self,
mods: int,
keysym: int,
callback: Callable[[], None],
stop_event: threading.Event | None = None,
) -> None:
local_stop = stop_event or threading.Event()
disp = None
root = None
keycode = None
@ -134,14 +170,18 @@ class X11Adapter:
disp = display.Display()
root = disp.screen().root
keycode = self._grab_hotkey(disp, root, mods, keysym)
while True:
while not local_stop.is_set():
if disp.pending_events() == 0:
time.sleep(0.05)
continue
ev = disp.next_event()
if ev.type == X.KeyPress and ev.detail == keycode:
state = ev.state & ~(X.LockMask | X.Mod2Mask)
if state == mods:
callback()
except Exception as exc:
logging.error("hotkey listener stopped: %s", exc)
if not local_stop.is_set():
logging.error("hotkey listener stopped: %s", exc)
finally:
if root is not None and keycode is not None and disp is not None:
try:
@ -149,6 +189,11 @@ class X11Adapter:
disp.sync()
except Exception:
pass
if disp is not None:
try:
disp.close()
except Exception:
pass
def _parse_hotkey(self, hotkey: str):
mods = 0