-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathfiltering.py
More file actions
97 lines (74 loc) · 3.48 KB
/
filtering.py
File metadata and controls
97 lines (74 loc) · 3.48 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
import logging
from collections import defaultdict
from typing import DefaultDict, Dict, NoReturn, Set
import libevdev
from src.constants import FILTERED_KEYS
def _get_filtered_keys_set() -> Set[libevdev.EventCode]:
"""
Converts the list of key names to libevdev codes.
If FILTERED_KEYS is None or empty, returns None (filter all keys).
"""
if not FILTERED_KEYS:
return None
filtered_codes = set()
for key_name in FILTERED_KEYS:
try:
# Converts "KEY_A" to libevdev.EV_KEY.KEY_A
key_code = getattr(libevdev.EV_KEY, key_name)
filtered_codes.add(key_code)
except AttributeError:
logging.warning(f"Key name '{key_name}' not found in libevdev.EV_KEY. Skipping...")
return filtered_codes
# Converts the key list once at initialization
_FILTERED_KEYS_CODES = _get_filtered_keys_set()
def filter_chattering(evdev: libevdev.Device, threshold: int) -> NoReturn:
# grab the device - now only we see the events it emits
evdev.grab()
# create a copy of the device that we can write to - this will emit the filtered events to anyone who listens
ui_dev = evdev.create_uinput_device()
# Log filtering mode
if _FILTERED_KEYS_CODES is None:
logging.info("Filtering mode: ALL keys (original behavior)")
else:
logging.info(f"Filtering mode: SPECIFIC keys only ({len(_FILTERED_KEYS_CODES)} keys configured)")
if logging.getLogger().isEnabledFor(logging.DEBUG):
key_names = [name for name in FILTERED_KEYS]
logging.debug(f"Filtered keys: {', '.join(sorted(key_names))}")
logging.info("Listening to input events...")
while True:
# since the descriptor is blocking, this blocks until there are events available
for e in evdev.events():
if _from_keystroke(e, threshold):
ui_dev.send_events([e, libevdev.InputEvent(libevdev.EV_SYN.SYN_REPORT, 0)])
def _from_keystroke(event: libevdev.InputEvent, threshold: int) -> bool:
# no need to relay those - we are going to emit our own
if event.matches(libevdev.EV_SYN) or event.matches(libevdev.EV_MSC):
return False
# some events we don't want to filter, like EV_LED for toggling NumLock and the like, and also key hold events
if not event.matches(libevdev.EV_KEY) or event.value > 1:
logging.debug(f'FORWARDING {event.code}')
return True
if _FILTERED_KEYS_CODES is not None and event.code not in _FILTERED_KEYS_CODES:
logging.debug(f'FORWARDING {event.code} (not in filtered keys list)')
return True
# the values are 0 for up, 1 for down and 2 for hold
if event.value == 0:
if _key_pressed[event.code]:
logging.debug(f'FORWARDING {event.code} up')
_last_key_up[event.code] = event.sec * 1E6 + event.usec
_key_pressed[event.code] = False
return True
else:
logging.info(f'FILTERING {event.code} up: key not pressed beforehand')
return False
prev = _last_key_up.get(event.code)
now = event.sec * 1E6 + event.usec
if prev is None or now - prev > threshold * 1E3:
logging.debug(f'FORWARDING {event.code} down')
_key_pressed[event.code] = True
return True
logging.info(
f'FILTERED {event.code} down: last key up event happened {(now - prev) / 1E3} ms ago')
return False
_last_key_up: Dict[libevdev.EventCode, int] = {}
_key_pressed: DefaultDict[libevdev.EventCode, bool] = defaultdict(bool)