-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_grab_exposure.py
More file actions
143 lines (110 loc) · 6.08 KB
/
Copy pathtest_grab_exposure.py
File metadata and controls
143 lines (110 loc) · 6.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#!/usr/bin/env python3
"""S1: куда уходят посторонние нажатия, пока горячая клавиша удерживается.
Это тот самый эксперимент из плана, только автоматический. Вопрос стоит так:
пассивный грэб клавиши при срабатывании переходит в активный грэб клавиатуры, и
теоретически ВСЕ нажатия на время удержания достаются программе, а не окну, в
котором человек печатает. Если это так — приложение в момент диктовки видит чужой
ввод, и об этом надо честно написать в README.
Проверка: открываем своё окно с полем ввода, забираем фокус, синтезируем через
XTEST последовательность «зажать горячую клавишу → нажать букву → отпустить» и
смотрим на два независимых факта:
1. дошла ли буква до поля ввода (значит, чужой ввод идёт мимо нас);
2. увидел ли её наш перехватчик.
Буква синтезируется в собственное окно, поэтому в чужие программы ничего не попадёт.
"""
from __future__ import annotations
import sys
import threading
import time
from pathlib import Path
import gi
from Xlib import XK, X, display
from Xlib.ext import xtest
gi.require_version("Gtk", "3.0")
from gi.repository import GLib, Gtk # noqa: E402
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from golos.hotkey import HotkeyListener # noqa: E402
HOTKEY_KEYCODE = 105 # правый Ctrl — то, что стоит по умолчанию
PROBE_KEYSYM = XK.string_to_keysym("a")
class Probe:
"""Считает, что увидел перехватчик, и что дошло до поля ввода."""
def __init__(self):
self.presses = 0
self.releases = 0
self.foreign_seen_by_us = 0
self.entry_text = ""
def install_spy(listener: HotkeyListener, probe: Probe) -> None:
"""Временно снимает фильтр S1, чтобы УВИДЕТЬ, что вообще прилетает.
В боевом коде чужие события отбрасываются первой строкой обработчика и никуда
не попадают. Здесь нам нужно ровно обратное: посчитать, приходят ли они вообще.
Считаем только количество и только в этом тесте — сами keycode не сохраняем.
"""
original = listener._handle
def spy(event): # noqa: ANN001
if event.type == X.KeyPress and event.detail != listener.keycode:
probe.foreign_seen_by_us += 1
original(event)
listener._handle = spy
def main() -> int:
probe = Probe()
window = Gtk.Window(title="Golos: проверка S1")
entry = Gtk.Entry()
window.add(entry)
window.set_keep_above(True)
window.show_all()
entry.grab_focus()
listener = HotkeyListener(
HOTKEY_KEYCODE,
on_press=lambda: setattr(probe, "presses", probe.presses + 1),
on_release=lambda _held: setattr(probe, "releases", probe.releases + 1),
)
install_spy(listener, probe)
listener.start()
fake = display.Display()
probe_keycode = fake.keysym_to_keycode(PROBE_KEYSYM)
def scenario() -> None:
time.sleep(1.0) # дать окну реально получить фокус у оконного менеджера
print("удерживаю горячую клавишу и «печатаю» букву…")
xtest.fake_input(fake, X.KeyPress, HOTKEY_KEYCODE)
fake.sync()
time.sleep(0.4)
for _ in range(3):
xtest.fake_input(fake, X.KeyPress, probe_keycode)
fake.sync()
time.sleep(0.05)
xtest.fake_input(fake, X.KeyRelease, probe_keycode)
fake.sync()
time.sleep(0.15)
xtest.fake_input(fake, X.KeyRelease, HOTKEY_KEYCODE)
fake.sync()
time.sleep(0.5)
GLib.idle_add(finish)
def finish() -> bool:
probe.entry_text = entry.get_text()
Gtk.main_quit()
return False
threading.Thread(target=scenario, daemon=True).start()
Gtk.main()
listener.stop()
window.destroy()
reached_window = len(probe.entry_text)
print()
print(f"перехватчик: нажатий {probe.presses}, отпусканий {probe.releases}")
print(f"букв дошло до поля ввода: {reached_window} из 3")
print(f"чужих нажатий увидел перехватчик: {probe.foreign_seen_by_us}")
print()
if reached_window == 3 and probe.foreign_seen_by_us == 0:
print("ВЫВОД: чужой ввод идёт мимо программы даже во время удержания.")
elif probe.foreign_seen_by_us > 0:
print("ВЫВОД: во время удержания чужие нажатия ПРИХОДЯТ программе.")
print(" В боевом коде они отбрасываются, но написать об этом в README надо.")
else:
print("ВЫВОД: буквы не дошли ни до поля, ни до нас — окно не получило фокус,")
print(" результат недостоверен, повторить.")
print()
print(f"автоповтор: удержание дало ровно {probe.presses} нажатие и "
f"{probe.releases} отпускание" if probe.presses == probe.releases == 1
else f"ВНИМАНИЕ: ожидалось 1/1, получено {probe.presses}/{probe.releases}")
return 0
if __name__ == "__main__":
sys.exit(main())