makcu 2.1.1__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/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