Skip to content

Commit 6f7f909

Browse files
Merge remote-tracking branch 'upstream/main'
2 parents 7ad62ba + 41e3d5f commit 6f7f909

File tree

10 files changed

+124
-236
lines changed

10 files changed

+124
-236
lines changed

main-ui/controller/key_watcher_controller.py

Lines changed: 90 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11

22

3-
from dataclasses import dataclass
3+
from collections import deque
4+
import errno
45
import os
56
import struct
6-
import select
77
import threading
88
import time
99
from typing import OrderedDict
1010

11-
from controller.controller_inputs import ControllerInput
1211
from controller.controller_interface import ControllerInterface
1312
from controller.key_state import KeyState
13+
from controller.key_watcher_controller_dataclasses import KeyEvent
1414
from utils.logger import PyUiLogger
1515

1616
# Constants for Linux input
@@ -20,19 +20,6 @@
2020
KEY_RELEASE = 0
2121
KEY_REPEAT = 2
2222

23-
24-
@dataclass
25-
class InputResult:
26-
controller_input: ControllerInput
27-
key_state: KeyState
28-
29-
@dataclass(frozen=True)
30-
class KeyEvent:
31-
event_type: int
32-
code: int
33-
value: int
34-
35-
3623
class KeyWatcherController(ControllerInterface):
3724

3825
def __init__(self, event_path, key_mappings):
@@ -43,9 +30,10 @@ def __init__(self, event_path, key_mappings):
4330
self.event_path = event_path
4431
self.key_mappings = key_mappings
4532
self.held_controller_inputs = OrderedDict()
46-
33+
self.input_queue = deque()
34+
4735
try:
48-
self.fd = os.open(self.event_path, os.O_RDONLY | os.O_NONBLOCK)
36+
self.fd = os.open(self.event_path, os.O_RDONLY)
4937
except OSError as e:
5038
PyUiLogger.get_logger().warning(f"Could not open {self.event_path}: {e}")
5139
self.fd = None
@@ -84,39 +72,81 @@ def cache_last_event(self):
8472

8573
def restore_cached_event(self):
8674
self.last_held_input = self.cached_input
75+
76+
def read_event(self, fd):
77+
"""Read exactly one input_event from fd (blocking)."""
78+
buf = b''
79+
while len(buf) < EVENT_SIZE:
80+
try:
81+
chunk = os.read(fd, EVENT_SIZE - len(buf))
82+
except BlockingIOError:
83+
continue
84+
if not chunk:
85+
PyUiLogger.get_logger().debug("EOF or no data read from fd")
86+
return None
87+
buf += chunk
88+
89+
event = struct.unpack(EVENT_FORMAT, buf)
90+
return event
91+
8792

8893

8994
def poll_keyboard(self):
90-
while(True):
95+
logger = PyUiLogger.get_logger()
96+
if self.fd is None:
97+
logger.error("File descriptor is None, cannot poll keyboard.")
98+
return
99+
100+
while True:
91101
now = time.time()
92102
try:
93-
rlist, _, _ = select.select([self.fd], [], [], 100)
94-
if rlist:
95-
data = os.read(self.fd, EVENT_SIZE)
96103

97-
if len(data) == EVENT_SIZE:
98-
_, _, event_type, code, value = struct.unpack(EVENT_FORMAT, data)
99-
#PyUiLogger.get_logger().error(f"event_type: {event_type}, code: {code}, value: {value}")
100-
key_event = KeyEvent(event_type, code, value)
101-
if key_event in self.key_mappings:
102-
mapped_events = self.key_mappings[key_event]
103-
if(mapped_events is not None and len(mapped_events) > 0):
104-
for mapped_event in mapped_events:
105-
if mapped_event.key_state == KeyState.PRESS:
106-
with self.lock:
107-
self.held_controller_inputs[mapped_event.controller_input] = now
108-
self.key_change(mapped_event.controller_input,"PRESS")
109-
#PyUiLogger.get_logger().debug(f"Adding {mapped_event.controller_input} to held inputs")
110-
elif mapped_event.key_state == KeyState.RELEASE:
111-
with self.lock:
112-
self.held_controller_inputs.pop(mapped_event.controller_input, None)
113-
self.key_change(mapped_event.controller_input,"RELEASE")
114-
#PyUiLogger.get_logger().debug(f"Removing {mapped_event.controller_input} from held inputs")
115-
else:
116-
PyUiLogger.get_logger().exception(f"No mapping for event: {key_event}")
104+
try:
105+
data = os.read(self.fd, EVENT_SIZE)
106+
except OSError as e:
107+
if e.errno == errno.EINTR:
108+
continue
109+
110+
if e.errno in (errno.EBADF, errno.ENODEV, errno.EIO):
111+
logger.error(
112+
"Keyboard device became unavailable (errno=%d)",
113+
e.errno,
114+
)
115+
return
116+
117+
logger.exception("Unexpected OSError while reading input")
118+
return
119+
120+
if len(data) != EVENT_SIZE:
121+
logger.error("Short read: got %d bytes, expected %d", len(data), EVENT_SIZE)
122+
continue
123+
124+
tv_sec, tv_usec, event_type, code, value = struct.unpack(EVENT_FORMAT, data)
125+
126+
key_event = KeyEvent(event_type, code, value)
127+
128+
if key_event in self.key_mappings:
129+
mapped_events = self.key_mappings[key_event]
130+
if mapped_events:
131+
for mapped_event in mapped_events:
132+
if mapped_event.key_state == KeyState.PRESS:
133+
with self.lock:
134+
self.held_controller_inputs[mapped_event.controller_input] = now
135+
if mapped_event.controller_input not in self.input_queue:
136+
self.input_queue.append(mapped_event.controller_input)
137+
self.key_change(mapped_event.controller_input,"PRESS")
138+
elif mapped_event.key_state == KeyState.RELEASE:
139+
with self.lock:
140+
self.key_change(mapped_event.controller_input,"RELEASE")
141+
self.held_controller_inputs.pop(mapped_event.controller_input, None)
142+
else:
143+
logger.error("No mapping for event: %s", key_event)
144+
elif(key_event.event_type != 0 or key_event.code !=0 or key_event.value != 0):
145+
#logger.debug("Unmapped key event: %s", key_event)
146+
pass
117147

118148
except Exception as e:
119-
PyUiLogger.get_logger().error(f"Error reading input: {e}")
149+
logger.exception("Error processing input: %s", e)
120150

121151
def key_change(self, controller_input, direction):
122152
if(self.print_key_changes):
@@ -125,17 +155,27 @@ def key_change(self, controller_input, direction):
125155
def get_input(self, timeoutInMilliseconds):
126156
start_time = time.time()
127157
timeout = timeoutInMilliseconds / 1000.0
128-
129-
with self.lock:
130-
self.last_held_input = next(iter(self.held_controller_inputs), None)
131158

132-
while self.last_held_input is None and (time.time() - start_time) < timeout:
133-
time.sleep(0.05) # 1/20 of a second delay
159+
while (time.time() - start_time) < timeout:
134160
with self.lock:
135-
self.last_held_input = next(iter(self.held_controller_inputs), None)
161+
# First, check the event queue
162+
if self.input_queue:
163+
value = self.input_queue.popleft()
164+
self.last_held_input = value
165+
return value
166+
167+
# Fallback: return the first currently held key
168+
if self.held_controller_inputs:
169+
value = next(iter(self.held_controller_inputs))
170+
self.last_held_input = value
171+
return value
172+
173+
time.sleep(0.005)
136174

137-
return self.last_held_input
175+
self.last_held_input = None
176+
return None
138177

178+
139179
def clear_input_queue(self):
140180
pass
141181

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
from dataclasses import dataclass
3+
4+
from controller.controller_inputs import ControllerInput
5+
from controller.key_state import KeyState
6+
7+
8+
@dataclass
9+
class InputResult:
10+
controller_input: ControllerInput
11+
key_state: KeyState
12+
13+
@dataclass(frozen=True)
14+
class KeyEvent:
15+
event_type: int
16+
code: int
17+
value: int
18+
19+

main-ui/controller/key_watcher_controller_miyoo_mini.py

Lines changed: 0 additions & 177 deletions
This file was deleted.

0 commit comments

Comments
 (0)