makcu 2.1.2__py3-none-any.whl → 2.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- makcu/__init__.py +378 -60
- makcu/__main__.py +387 -387
- makcu/conftest.py +33 -33
- makcu/connection.py +459 -459
- makcu/controller.py +388 -376
- makcu/enums.py +7 -7
- makcu/errors.py +13 -13
- makcu/makcu.pyi +10 -10
- makcu/mouse.py +249 -249
- makcu/test_suite.py +144 -144
- makcu-2.1.3.dist-info/METADATA +32 -0
- makcu-2.1.3.dist-info/RECORD +15 -0
- makcu-2.1.2.dist-info/METADATA +0 -1141
- makcu-2.1.2.dist-info/RECORD +0 -16
- makcu-2.1.2.dist-info/licenses/LICENSE +0 -674
- {makcu-2.1.2.dist-info → makcu-2.1.3.dist-info}/WHEEL +0 -0
- {makcu-2.1.2.dist-info → makcu-2.1.3.dist-info}/top_level.txt +0 -0
makcu/enums.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
|
3
|
-
class MouseButton(Enum):
|
4
|
-
LEFT = 0
|
5
|
-
RIGHT = 1
|
6
|
-
MIDDLE = 2
|
7
|
-
MOUSE4 = 3
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
class MouseButton(Enum):
|
4
|
+
LEFT = 0
|
5
|
+
RIGHT = 1
|
6
|
+
MIDDLE = 2
|
7
|
+
MOUSE4 = 3
|
8
8
|
MOUSE5 = 4
|
makcu/errors.py
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
class MakcuError(Exception):
|
2
|
-
pass
|
3
|
-
|
4
|
-
class MakcuConnectionError(MakcuError):
|
5
|
-
pass
|
6
|
-
|
7
|
-
class MakcuCommandError(MakcuError):
|
8
|
-
pass
|
9
|
-
|
10
|
-
class MakcuTimeoutError(MakcuError):
|
11
|
-
pass
|
12
|
-
|
13
|
-
class MakcuResponseError(MakcuError):
|
1
|
+
class MakcuError(Exception):
|
2
|
+
pass
|
3
|
+
|
4
|
+
class MakcuConnectionError(MakcuError):
|
5
|
+
pass
|
6
|
+
|
7
|
+
class MakcuCommandError(MakcuError):
|
8
|
+
pass
|
9
|
+
|
10
|
+
class MakcuTimeoutError(MakcuError):
|
11
|
+
pass
|
12
|
+
|
13
|
+
class MakcuResponseError(MakcuError):
|
14
14
|
pass
|
makcu/makcu.pyi
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
from typing import List
|
2
|
-
from .controller import MakcuController
|
3
|
-
|
4
|
-
__version__: str
|
5
|
-
__all__: List[str]
|
6
|
-
|
7
|
-
def create_controller(
|
8
|
-
fallback_com_port: str = "",
|
9
|
-
debug: bool = False,
|
10
|
-
send_init: bool = True
|
1
|
+
from typing import List
|
2
|
+
from .controller import MakcuController
|
3
|
+
|
4
|
+
__version__: str
|
5
|
+
__all__: List[str]
|
6
|
+
|
7
|
+
def create_controller(
|
8
|
+
fallback_com_port: str = "",
|
9
|
+
debug: bool = False,
|
10
|
+
send_init: bool = True
|
11
11
|
) -> MakcuController: ...
|
makcu/mouse.py
CHANGED
@@ -1,250 +1,250 @@
|
|
1
|
-
from typing import Dict, Union
|
2
|
-
from .enums import MouseButton
|
3
|
-
from .connection import SerialTransport
|
4
|
-
from .errors import MakcuCommandError
|
5
|
-
from serial.tools import list_ports
|
6
|
-
|
7
|
-
class AxisButton:
|
8
|
-
def __init__(self, name: str) -> None:
|
9
|
-
self.name = name
|
10
|
-
|
11
|
-
class Mouse:
|
12
|
-
|
13
|
-
|
14
|
-
_BUTTON_COMMANDS = {
|
15
|
-
MouseButton.LEFT: "left",
|
16
|
-
MouseButton.RIGHT: "right",
|
17
|
-
MouseButton.MIDDLE: "middle",
|
18
|
-
MouseButton.MOUSE4: "ms1",
|
19
|
-
MouseButton.MOUSE5: "ms2",
|
20
|
-
}
|
21
|
-
|
22
|
-
|
23
|
-
_PRESS_COMMANDS = {}
|
24
|
-
_RELEASE_COMMANDS = {}
|
25
|
-
_LOCK_COMMANDS = {}
|
26
|
-
_UNLOCK_COMMANDS = {}
|
27
|
-
_LOCK_QUERY_COMMANDS = {}
|
28
|
-
|
29
|
-
def __init__(self, transport: SerialTransport) -> None:
|
30
|
-
self.transport = transport
|
31
|
-
self._lock_states_cache: int = 0
|
32
|
-
self._cache_valid = False
|
33
|
-
|
34
|
-
|
35
|
-
self._init_command_cache()
|
36
|
-
|
37
|
-
def _init_command_cache(self) -> None:
|
38
|
-
|
39
|
-
for button, cmd in self._BUTTON_COMMANDS.items():
|
40
|
-
self._PRESS_COMMANDS[button] = f"km.{cmd}(1)"
|
41
|
-
self._RELEASE_COMMANDS[button] = f"km.{cmd}(0)"
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
lock_targets = [
|
46
|
-
("LEFT", "ml", 0),
|
47
|
-
("RIGHT", "mr", 1),
|
48
|
-
("MIDDLE", "mm", 2),
|
49
|
-
("MOUSE4", "ms1", 3),
|
50
|
-
("MOUSE5", "ms2", 4),
|
51
|
-
("X", "mx", 5),
|
52
|
-
("Y", "my", 6),
|
53
|
-
]
|
54
|
-
|
55
|
-
for name, cmd, bit in lock_targets:
|
56
|
-
self._LOCK_COMMANDS[name] = (f"km.lock_{cmd}(1)", bit)
|
57
|
-
self._UNLOCK_COMMANDS[name] = (f"km.lock_{cmd}(0)", bit)
|
58
|
-
self._LOCK_QUERY_COMMANDS[name] = (f"km.lock_{cmd}()", bit)
|
59
|
-
|
60
|
-
def _send_button_command(self, button: MouseButton, state: int) -> None:
|
61
|
-
if button not in self._BUTTON_COMMANDS:
|
62
|
-
raise MakcuCommandError(f"Unsupported button: {button}")
|
63
|
-
|
64
|
-
|
65
|
-
cmd = self._PRESS_COMMANDS[button] if state else self._RELEASE_COMMANDS[button]
|
66
|
-
self.transport.send_command(cmd)
|
67
|
-
|
68
|
-
def press(self, button: MouseButton) -> None:
|
69
|
-
self.transport.send_command(self._PRESS_COMMANDS[button])
|
70
|
-
|
71
|
-
def release(self, button: MouseButton) -> None:
|
72
|
-
self.transport.send_command(self._RELEASE_COMMANDS[button])
|
73
|
-
|
74
|
-
def move(self, x: int, y: int) -> None:
|
75
|
-
self.transport.send_command(f"km.move({x},{y})")
|
76
|
-
|
77
|
-
def click(self, button: MouseButton) -> None:
|
78
|
-
if button not in self._BUTTON_COMMANDS:
|
79
|
-
raise MakcuCommandError(f"Unsupported button: {button}")
|
80
|
-
|
81
|
-
|
82
|
-
press_cmd = self._PRESS_COMMANDS[button]
|
83
|
-
release_cmd = self._RELEASE_COMMANDS[button]
|
84
|
-
|
85
|
-
|
86
|
-
transport = self.transport
|
87
|
-
transport.send_command(press_cmd)
|
88
|
-
transport.send_command(release_cmd)
|
89
|
-
|
90
|
-
def move_smooth(self, x: int, y: int, segments: int) -> None:
|
91
|
-
self.transport.send_command(f"km.move({x},{y},{segments})")
|
92
|
-
|
93
|
-
def move_bezier(self, x: int, y: int, segments: int, ctrl_x: int, ctrl_y: int) -> None:
|
94
|
-
self.transport.send_command(f"km.move({x},{y},{segments},{ctrl_x},{ctrl_y})")
|
95
|
-
|
96
|
-
def scroll(self, delta: int) -> None:
|
97
|
-
self.transport.send_command(f"km.wheel({delta})")
|
98
|
-
|
99
|
-
|
100
|
-
def _set_lock(self, name: str, lock: bool) -> None:
|
101
|
-
if lock:
|
102
|
-
cmd, bit = self._LOCK_COMMANDS[name]
|
103
|
-
else:
|
104
|
-
cmd, bit = self._UNLOCK_COMMANDS[name]
|
105
|
-
|
106
|
-
self.transport.send_command(cmd)
|
107
|
-
|
108
|
-
|
109
|
-
if lock:
|
110
|
-
self._lock_states_cache |= (1 << bit)
|
111
|
-
else:
|
112
|
-
self._lock_states_cache &= ~(1 << bit)
|
113
|
-
self._cache_valid = True
|
114
|
-
|
115
|
-
def lock_left(self, lock: bool) -> None:
|
116
|
-
self._set_lock("LEFT", lock)
|
117
|
-
|
118
|
-
def lock_middle(self, lock: bool) -> None:
|
119
|
-
self._set_lock("MIDDLE", lock)
|
120
|
-
|
121
|
-
def lock_right(self, lock: bool) -> None:
|
122
|
-
self._set_lock("RIGHT", lock)
|
123
|
-
|
124
|
-
def lock_side1(self, lock: bool) -> None:
|
125
|
-
self._set_lock("MOUSE4", lock)
|
126
|
-
|
127
|
-
def lock_side2(self, lock: bool) -> None:
|
128
|
-
self._set_lock("MOUSE5", lock)
|
129
|
-
|
130
|
-
def lock_x(self, lock: bool) -> None:
|
131
|
-
self._set_lock("X", lock)
|
132
|
-
|
133
|
-
def lock_y(self, lock: bool) -> None:
|
134
|
-
self._set_lock("Y", lock)
|
135
|
-
|
136
|
-
def spoof_serial(self, serial: str) -> None:
|
137
|
-
self.transport.send_command(f"km.serial('{serial}')")
|
138
|
-
|
139
|
-
def reset_serial(self) -> None:
|
140
|
-
self.transport.send_command("km.serial(0)")
|
141
|
-
|
142
|
-
def get_device_info(self) -> Dict[str, Union[str, bool]]:
|
143
|
-
port_name = self.transport.port
|
144
|
-
is_connected = self.transport.is_connected()
|
145
|
-
|
146
|
-
if not is_connected or not port_name:
|
147
|
-
return {
|
148
|
-
"port": port_name or "Unknown",
|
149
|
-
"description": "Disconnected",
|
150
|
-
"vid": "Unknown",
|
151
|
-
"pid": "Unknown",
|
152
|
-
"isConnected": False
|
153
|
-
}
|
154
|
-
|
155
|
-
info = {
|
156
|
-
"port": port_name,
|
157
|
-
"description": "Connected Device",
|
158
|
-
"vid": "Unknown",
|
159
|
-
"pid": "Unknown",
|
160
|
-
"isConnected": True
|
161
|
-
}
|
162
|
-
|
163
|
-
try:
|
164
|
-
for port in list_ports.comports():
|
165
|
-
if port.device == port_name:
|
166
|
-
info["description"] = port.description or "Connected Device"
|
167
|
-
if port.vid is not None:
|
168
|
-
info["vid"] = f"0x{port.vid:04x}"
|
169
|
-
if port.pid is not None:
|
170
|
-
info["pid"] = f"0x{port.pid:04x}"
|
171
|
-
break
|
172
|
-
except Exception:
|
173
|
-
pass
|
174
|
-
|
175
|
-
return info
|
176
|
-
|
177
|
-
def get_firmware_version(self) -> str:
|
178
|
-
response = self.transport.send_command("km.version()", expect_response=True, timeout=0.1)
|
179
|
-
return response or ""
|
180
|
-
|
181
|
-
def _invalidate_cache(self) -> None:
|
182
|
-
self._cache_valid = False
|
183
|
-
|
184
|
-
def get_all_lock_states(self) -> Dict[str, bool]:
|
185
|
-
|
186
|
-
if self._cache_valid:
|
187
|
-
return {
|
188
|
-
"X": bool(self._lock_states_cache & (1 << 5)),
|
189
|
-
"Y": bool(self._lock_states_cache & (1 << 6)),
|
190
|
-
"LEFT": bool(self._lock_states_cache & (1 << 0)),
|
191
|
-
"RIGHT": bool(self._lock_states_cache & (1 << 1)),
|
192
|
-
"MIDDLE": bool(self._lock_states_cache & (1 << 2)),
|
193
|
-
"MOUSE4": bool(self._lock_states_cache & (1 << 3)),
|
194
|
-
"MOUSE5": bool(self._lock_states_cache & (1 << 4)),
|
195
|
-
}
|
196
|
-
|
197
|
-
|
198
|
-
states = {}
|
199
|
-
targets = ["X", "Y", "LEFT", "RIGHT", "MIDDLE", "MOUSE4", "MOUSE5"]
|
200
|
-
|
201
|
-
for target in targets:
|
202
|
-
cmd, bit = self._LOCK_QUERY_COMMANDS[target]
|
203
|
-
try:
|
204
|
-
result = self.transport.send_command(cmd, expect_response=True, timeout=0.05)
|
205
|
-
if result and result.strip() in ['0', '1']:
|
206
|
-
is_locked = result.strip() == '1'
|
207
|
-
states[target] = is_locked
|
208
|
-
|
209
|
-
|
210
|
-
if is_locked:
|
211
|
-
self._lock_states_cache |= (1 << bit)
|
212
|
-
else:
|
213
|
-
self._lock_states_cache &= ~(1 << bit)
|
214
|
-
else:
|
215
|
-
states[target] = False
|
216
|
-
except Exception:
|
217
|
-
states[target] = False
|
218
|
-
|
219
|
-
self._cache_valid = True
|
220
|
-
return states
|
221
|
-
|
222
|
-
def is_locked(self, button: Union[MouseButton, AxisButton]) -> bool:
|
223
|
-
try:
|
224
|
-
target_name = button.name if hasattr(button, 'name') else str(button)
|
225
|
-
|
226
|
-
|
227
|
-
if self._cache_valid and target_name in self._LOCK_QUERY_COMMANDS:
|
228
|
-
_, bit = self._LOCK_QUERY_COMMANDS[target_name]
|
229
|
-
return bool(self._lock_states_cache & (1 << bit))
|
230
|
-
|
231
|
-
|
232
|
-
cmd, bit = self._LOCK_QUERY_COMMANDS[target_name]
|
233
|
-
result = self.transport.send_command(cmd, expect_response=True, timeout=0.05)
|
234
|
-
|
235
|
-
if not result:
|
236
|
-
return False
|
237
|
-
|
238
|
-
result = result.strip()
|
239
|
-
is_locked = result == '1'
|
240
|
-
|
241
|
-
|
242
|
-
if is_locked:
|
243
|
-
self._lock_states_cache |= (1 << bit)
|
244
|
-
else:
|
245
|
-
self._lock_states_cache &= ~(1 << bit)
|
246
|
-
|
247
|
-
return is_locked
|
248
|
-
|
249
|
-
except Exception:
|
1
|
+
from typing import Dict, Union
|
2
|
+
from .enums import MouseButton
|
3
|
+
from .connection import SerialTransport
|
4
|
+
from .errors import MakcuCommandError
|
5
|
+
from serial.tools import list_ports
|
6
|
+
|
7
|
+
class AxisButton:
|
8
|
+
def __init__(self, name: str) -> None:
|
9
|
+
self.name = name
|
10
|
+
|
11
|
+
class Mouse:
|
12
|
+
|
13
|
+
|
14
|
+
_BUTTON_COMMANDS = {
|
15
|
+
MouseButton.LEFT: "left",
|
16
|
+
MouseButton.RIGHT: "right",
|
17
|
+
MouseButton.MIDDLE: "middle",
|
18
|
+
MouseButton.MOUSE4: "ms1",
|
19
|
+
MouseButton.MOUSE5: "ms2",
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
_PRESS_COMMANDS = {}
|
24
|
+
_RELEASE_COMMANDS = {}
|
25
|
+
_LOCK_COMMANDS = {}
|
26
|
+
_UNLOCK_COMMANDS = {}
|
27
|
+
_LOCK_QUERY_COMMANDS = {}
|
28
|
+
|
29
|
+
def __init__(self, transport: SerialTransport) -> None:
|
30
|
+
self.transport = transport
|
31
|
+
self._lock_states_cache: int = 0
|
32
|
+
self._cache_valid = False
|
33
|
+
|
34
|
+
|
35
|
+
self._init_command_cache()
|
36
|
+
|
37
|
+
def _init_command_cache(self) -> None:
|
38
|
+
|
39
|
+
for button, cmd in self._BUTTON_COMMANDS.items():
|
40
|
+
self._PRESS_COMMANDS[button] = f"km.{cmd}(1)"
|
41
|
+
self._RELEASE_COMMANDS[button] = f"km.{cmd}(0)"
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
lock_targets = [
|
46
|
+
("LEFT", "ml", 0),
|
47
|
+
("RIGHT", "mr", 1),
|
48
|
+
("MIDDLE", "mm", 2),
|
49
|
+
("MOUSE4", "ms1", 3),
|
50
|
+
("MOUSE5", "ms2", 4),
|
51
|
+
("X", "mx", 5),
|
52
|
+
("Y", "my", 6),
|
53
|
+
]
|
54
|
+
|
55
|
+
for name, cmd, bit in lock_targets:
|
56
|
+
self._LOCK_COMMANDS[name] = (f"km.lock_{cmd}(1)", bit)
|
57
|
+
self._UNLOCK_COMMANDS[name] = (f"km.lock_{cmd}(0)", bit)
|
58
|
+
self._LOCK_QUERY_COMMANDS[name] = (f"km.lock_{cmd}()", bit)
|
59
|
+
|
60
|
+
def _send_button_command(self, button: MouseButton, state: int) -> None:
|
61
|
+
if button not in self._BUTTON_COMMANDS:
|
62
|
+
raise MakcuCommandError(f"Unsupported button: {button}")
|
63
|
+
|
64
|
+
|
65
|
+
cmd = self._PRESS_COMMANDS[button] if state else self._RELEASE_COMMANDS[button]
|
66
|
+
self.transport.send_command(cmd)
|
67
|
+
|
68
|
+
def press(self, button: MouseButton) -> None:
|
69
|
+
self.transport.send_command(self._PRESS_COMMANDS[button])
|
70
|
+
|
71
|
+
def release(self, button: MouseButton) -> None:
|
72
|
+
self.transport.send_command(self._RELEASE_COMMANDS[button])
|
73
|
+
|
74
|
+
def move(self, x: int, y: int) -> None:
|
75
|
+
self.transport.send_command(f"km.move({x},{y})")
|
76
|
+
|
77
|
+
def click(self, button: MouseButton) -> None:
|
78
|
+
if button not in self._BUTTON_COMMANDS:
|
79
|
+
raise MakcuCommandError(f"Unsupported button: {button}")
|
80
|
+
|
81
|
+
|
82
|
+
press_cmd = self._PRESS_COMMANDS[button]
|
83
|
+
release_cmd = self._RELEASE_COMMANDS[button]
|
84
|
+
|
85
|
+
|
86
|
+
transport = self.transport
|
87
|
+
transport.send_command(press_cmd)
|
88
|
+
transport.send_command(release_cmd)
|
89
|
+
|
90
|
+
def move_smooth(self, x: int, y: int, segments: int) -> None:
|
91
|
+
self.transport.send_command(f"km.move({x},{y},{segments})")
|
92
|
+
|
93
|
+
def move_bezier(self, x: int, y: int, segments: int, ctrl_x: int, ctrl_y: int) -> None:
|
94
|
+
self.transport.send_command(f"km.move({x},{y},{segments},{ctrl_x},{ctrl_y})")
|
95
|
+
|
96
|
+
def scroll(self, delta: int) -> None:
|
97
|
+
self.transport.send_command(f"km.wheel({delta})")
|
98
|
+
|
99
|
+
|
100
|
+
def _set_lock(self, name: str, lock: bool) -> None:
|
101
|
+
if lock:
|
102
|
+
cmd, bit = self._LOCK_COMMANDS[name]
|
103
|
+
else:
|
104
|
+
cmd, bit = self._UNLOCK_COMMANDS[name]
|
105
|
+
|
106
|
+
self.transport.send_command(cmd)
|
107
|
+
|
108
|
+
|
109
|
+
if lock:
|
110
|
+
self._lock_states_cache |= (1 << bit)
|
111
|
+
else:
|
112
|
+
self._lock_states_cache &= ~(1 << bit)
|
113
|
+
self._cache_valid = True
|
114
|
+
|
115
|
+
def lock_left(self, lock: bool) -> None:
|
116
|
+
self._set_lock("LEFT", lock)
|
117
|
+
|
118
|
+
def lock_middle(self, lock: bool) -> None:
|
119
|
+
self._set_lock("MIDDLE", lock)
|
120
|
+
|
121
|
+
def lock_right(self, lock: bool) -> None:
|
122
|
+
self._set_lock("RIGHT", lock)
|
123
|
+
|
124
|
+
def lock_side1(self, lock: bool) -> None:
|
125
|
+
self._set_lock("MOUSE4", lock)
|
126
|
+
|
127
|
+
def lock_side2(self, lock: bool) -> None:
|
128
|
+
self._set_lock("MOUSE5", lock)
|
129
|
+
|
130
|
+
def lock_x(self, lock: bool) -> None:
|
131
|
+
self._set_lock("X", lock)
|
132
|
+
|
133
|
+
def lock_y(self, lock: bool) -> None:
|
134
|
+
self._set_lock("Y", lock)
|
135
|
+
|
136
|
+
def spoof_serial(self, serial: str) -> None:
|
137
|
+
self.transport.send_command(f"km.serial('{serial}')")
|
138
|
+
|
139
|
+
def reset_serial(self) -> None:
|
140
|
+
self.transport.send_command("km.serial(0)")
|
141
|
+
|
142
|
+
def get_device_info(self) -> Dict[str, Union[str, bool]]:
|
143
|
+
port_name = self.transport.port
|
144
|
+
is_connected = self.transport.is_connected()
|
145
|
+
|
146
|
+
if not is_connected or not port_name:
|
147
|
+
return {
|
148
|
+
"port": port_name or "Unknown",
|
149
|
+
"description": "Disconnected",
|
150
|
+
"vid": "Unknown",
|
151
|
+
"pid": "Unknown",
|
152
|
+
"isConnected": False
|
153
|
+
}
|
154
|
+
|
155
|
+
info = {
|
156
|
+
"port": port_name,
|
157
|
+
"description": "Connected Device",
|
158
|
+
"vid": "Unknown",
|
159
|
+
"pid": "Unknown",
|
160
|
+
"isConnected": True
|
161
|
+
}
|
162
|
+
|
163
|
+
try:
|
164
|
+
for port in list_ports.comports():
|
165
|
+
if port.device == port_name:
|
166
|
+
info["description"] = port.description or "Connected Device"
|
167
|
+
if port.vid is not None:
|
168
|
+
info["vid"] = f"0x{port.vid:04x}"
|
169
|
+
if port.pid is not None:
|
170
|
+
info["pid"] = f"0x{port.pid:04x}"
|
171
|
+
break
|
172
|
+
except Exception:
|
173
|
+
pass
|
174
|
+
|
175
|
+
return info
|
176
|
+
|
177
|
+
def get_firmware_version(self) -> str:
|
178
|
+
response = self.transport.send_command("km.version()", expect_response=True, timeout=0.1)
|
179
|
+
return response or ""
|
180
|
+
|
181
|
+
def _invalidate_cache(self) -> None:
|
182
|
+
self._cache_valid = False
|
183
|
+
|
184
|
+
def get_all_lock_states(self) -> Dict[str, bool]:
|
185
|
+
|
186
|
+
if self._cache_valid:
|
187
|
+
return {
|
188
|
+
"X": bool(self._lock_states_cache & (1 << 5)),
|
189
|
+
"Y": bool(self._lock_states_cache & (1 << 6)),
|
190
|
+
"LEFT": bool(self._lock_states_cache & (1 << 0)),
|
191
|
+
"RIGHT": bool(self._lock_states_cache & (1 << 1)),
|
192
|
+
"MIDDLE": bool(self._lock_states_cache & (1 << 2)),
|
193
|
+
"MOUSE4": bool(self._lock_states_cache & (1 << 3)),
|
194
|
+
"MOUSE5": bool(self._lock_states_cache & (1 << 4)),
|
195
|
+
}
|
196
|
+
|
197
|
+
|
198
|
+
states = {}
|
199
|
+
targets = ["X", "Y", "LEFT", "RIGHT", "MIDDLE", "MOUSE4", "MOUSE5"]
|
200
|
+
|
201
|
+
for target in targets:
|
202
|
+
cmd, bit = self._LOCK_QUERY_COMMANDS[target]
|
203
|
+
try:
|
204
|
+
result = self.transport.send_command(cmd, expect_response=True, timeout=0.05)
|
205
|
+
if result and result.strip() in ['0', '1']:
|
206
|
+
is_locked = result.strip() == '1'
|
207
|
+
states[target] = is_locked
|
208
|
+
|
209
|
+
|
210
|
+
if is_locked:
|
211
|
+
self._lock_states_cache |= (1 << bit)
|
212
|
+
else:
|
213
|
+
self._lock_states_cache &= ~(1 << bit)
|
214
|
+
else:
|
215
|
+
states[target] = False
|
216
|
+
except Exception:
|
217
|
+
states[target] = False
|
218
|
+
|
219
|
+
self._cache_valid = True
|
220
|
+
return states
|
221
|
+
|
222
|
+
def is_locked(self, button: Union[MouseButton, AxisButton]) -> bool:
|
223
|
+
try:
|
224
|
+
target_name = button.name if hasattr(button, 'name') else str(button)
|
225
|
+
|
226
|
+
|
227
|
+
if self._cache_valid and target_name in self._LOCK_QUERY_COMMANDS:
|
228
|
+
_, bit = self._LOCK_QUERY_COMMANDS[target_name]
|
229
|
+
return bool(self._lock_states_cache & (1 << bit))
|
230
|
+
|
231
|
+
|
232
|
+
cmd, bit = self._LOCK_QUERY_COMMANDS[target_name]
|
233
|
+
result = self.transport.send_command(cmd, expect_response=True, timeout=0.05)
|
234
|
+
|
235
|
+
if not result:
|
236
|
+
return False
|
237
|
+
|
238
|
+
result = result.strip()
|
239
|
+
is_locked = result == '1'
|
240
|
+
|
241
|
+
|
242
|
+
if is_locked:
|
243
|
+
self._lock_states_cache |= (1 << bit)
|
244
|
+
else:
|
245
|
+
self._lock_states_cache &= ~(1 << bit)
|
246
|
+
|
247
|
+
return is_locked
|
248
|
+
|
249
|
+
except Exception:
|
250
250
|
return False
|