makcu 0.1.1__tar.gz → 0.1.3__tar.gz
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-0.1.3/MANIFEST.in +7 -0
- {makcu-0.1.1 → makcu-0.1.3}/PKG-INFO +7 -12
- {makcu-0.1.1 → makcu-0.1.3}/README.md +7 -12
- {makcu-0.1.1 → makcu-0.1.3}/makcu/__init__.py +2 -2
- {makcu-0.1.1 → makcu-0.1.3}/makcu/__main__.py +26 -9
- makcu-0.1.3/makcu/conftest.py +22 -0
- {makcu-0.1.1 → makcu-0.1.3}/makcu/connection.py +47 -53
- {makcu-0.1.1 → makcu-0.1.3}/makcu/controller.py +36 -57
- {makcu-0.1.1 → makcu-0.1.3}/makcu/mouse.py +14 -32
- makcu-0.1.3/makcu/test_suite.py +100 -0
- {makcu-0.1.1 → makcu-0.1.3}/makcu.egg-info/PKG-INFO +7 -12
- makcu-0.1.3/makcu.egg-info/SOURCES.txt +18 -0
- {makcu-0.1.1 → makcu-0.1.3}/pyproject.toml +6 -2
- makcu-0.1.3/pytest.ini +4 -0
- makcu-0.1.1/makcu/utils.py +0 -7
- makcu-0.1.1/makcu.egg-info/SOURCES.txt +0 -29
- makcu-0.1.1/tests/test_button_mask.py +0 -7
- makcu-0.1.1/tests/test_capture_right_clicks.py +0 -21
- makcu-0.1.1/tests/test_controller_behavior.py +0 -7
- makcu-0.1.1/tests/test_device_info.py +0 -8
- makcu-0.1.1/tests/test_firmware_version.py +0 -7
- makcu-0.1.1/tests/test_get_button_states.py +0 -16
- makcu-0.1.1/tests/test_get_raw_mask.py +0 -14
- makcu-0.1.1/tests/test_is_button_pressed.py +0 -13
- makcu-0.1.1/tests/test_lock_state.py +0 -22
- makcu-0.1.1/tests/test_middle_click.py +0 -6
- makcu-0.1.1/tests/test_port_connection.py +0 -4
- makcu-0.1.1/tests/test_press_and_release.py +0 -14
- makcu-0.1.1/tests/test_reset_all.py +0 -20
- makcu-0.1.1/tests/test_set_fallback_port.py +0 -3
- {makcu-0.1.1 → makcu-0.1.3}/LICENSE +0 -0
- {makcu-0.1.1 → makcu-0.1.3}/makcu/enums.py +0 -0
- {makcu-0.1.1 → makcu-0.1.3}/makcu/errors.py +0 -0
- {makcu-0.1.1 → makcu-0.1.3}/makcu.egg-info/dependency_links.txt +0 -0
- {makcu-0.1.1 → makcu-0.1.3}/makcu.egg-info/top_level.txt +0 -0
- {makcu-0.1.1 → makcu-0.1.3}/setup.cfg +0 -0
makcu-0.1.3/MANIFEST.in
ADDED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: makcu
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.3
|
4
4
|
Summary: Python library to interact with Makcu devices.
|
5
5
|
Author: SleepyTotem
|
6
6
|
License: GPL
|
@@ -65,7 +65,7 @@ python -m makcu --runtest
|
|
65
65
|
```python
|
66
66
|
from makcu import create_controller, MouseButton
|
67
67
|
|
68
|
-
makcu = create_controller()
|
68
|
+
makcu = create_controller("COM1") # Fallback port
|
69
69
|
makcu.click(MouseButton.LEFT)
|
70
70
|
makcu.move(100, 50)
|
71
71
|
makcu.scroll(-1)
|
@@ -85,7 +85,7 @@ makcu = create_controller(debug=True, send_init=True)
|
|
85
85
|
#### Set fallback port manually
|
86
86
|
|
87
87
|
```python
|
88
|
-
makcu
|
88
|
+
makcu = create_controller("COM4") # Optional fallback com port
|
89
89
|
```
|
90
90
|
|
91
91
|
---
|
@@ -189,17 +189,9 @@ def on_button_event(button, pressed):
|
|
189
189
|
makcu.set_button_callback(on_button_event)
|
190
190
|
```
|
191
191
|
|
192
|
-
### Configure Debounce
|
193
|
-
|
194
|
-
```python
|
195
|
-
makcu.set_callback_debounce_time(100) # milliseconds
|
196
|
-
```
|
197
|
-
|
198
192
|
---
|
199
193
|
|
200
|
-
## Click Capturing
|
201
|
-
|
202
|
-
### ❌ **Currently broken (Pending Firmware Update)**
|
194
|
+
## ❌ Click Capturing (Pending Firmware Update)
|
203
195
|
|
204
196
|
Click capturing will allow you to detect and count click events in software.
|
205
197
|
|
@@ -248,6 +240,8 @@ if makcu.is_button_pressed(MouseButton.RIGHT):
|
|
248
240
|
### Send raw serial commands
|
249
241
|
|
250
242
|
```python
|
243
|
+
from makcu import create_controller
|
244
|
+
makcu = create_controller()
|
251
245
|
response = makcu.transport.send_command("km.version()", expect_response=True)
|
252
246
|
print(response)
|
253
247
|
```
|
@@ -313,3 +307,4 @@ Please open an issue on the project repository and I will get to it asap
|
|
313
307
|
## 🌐 Links
|
314
308
|
|
315
309
|
- 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
|
310
|
+
- 🔗 [PyPI Homepage](https://pypi.org/project/makcu/)
|
@@ -53,7 +53,7 @@ python -m makcu --runtest
|
|
53
53
|
```python
|
54
54
|
from makcu import create_controller, MouseButton
|
55
55
|
|
56
|
-
makcu = create_controller()
|
56
|
+
makcu = create_controller("COM1") # Fallback port
|
57
57
|
makcu.click(MouseButton.LEFT)
|
58
58
|
makcu.move(100, 50)
|
59
59
|
makcu.scroll(-1)
|
@@ -73,7 +73,7 @@ makcu = create_controller(debug=True, send_init=True)
|
|
73
73
|
#### Set fallback port manually
|
74
74
|
|
75
75
|
```python
|
76
|
-
makcu
|
76
|
+
makcu = create_controller("COM4") # Optional fallback com port
|
77
77
|
```
|
78
78
|
|
79
79
|
---
|
@@ -177,17 +177,9 @@ def on_button_event(button, pressed):
|
|
177
177
|
makcu.set_button_callback(on_button_event)
|
178
178
|
```
|
179
179
|
|
180
|
-
### Configure Debounce
|
181
|
-
|
182
|
-
```python
|
183
|
-
makcu.set_callback_debounce_time(100) # milliseconds
|
184
|
-
```
|
185
|
-
|
186
180
|
---
|
187
181
|
|
188
|
-
## Click Capturing
|
189
|
-
|
190
|
-
### ❌ **Currently broken (Pending Firmware Update)**
|
182
|
+
## ❌ Click Capturing (Pending Firmware Update)
|
191
183
|
|
192
184
|
Click capturing will allow you to detect and count click events in software.
|
193
185
|
|
@@ -236,6 +228,8 @@ if makcu.is_button_pressed(MouseButton.RIGHT):
|
|
236
228
|
### Send raw serial commands
|
237
229
|
|
238
230
|
```python
|
231
|
+
from makcu import create_controller
|
232
|
+
makcu = create_controller()
|
239
233
|
response = makcu.transport.send_command("km.version()", expect_response=True)
|
240
234
|
print(response)
|
241
235
|
```
|
@@ -300,4 +294,5 @@ Please open an issue on the project repository and I will get to it asap
|
|
300
294
|
|
301
295
|
## 🌐 Links
|
302
296
|
|
303
|
-
- 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
|
297
|
+
- 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
|
298
|
+
- 🔗 [PyPI Homepage](https://pypi.org/project/makcu/)
|
@@ -2,8 +2,8 @@ from .controller import MakcuController
|
|
2
2
|
from .enums import MouseButton
|
3
3
|
from .errors import MakcuError, MakcuConnectionError
|
4
4
|
|
5
|
-
def create_controller(debug=False, send_init=True):
|
6
|
-
makcu = MakcuController(debug=debug, send_init=send_init)
|
5
|
+
def create_controller(fallback_com_port="", debug=False, send_init=True):
|
6
|
+
makcu = MakcuController(fallback_com_port, debug=debug, send_init=send_init)
|
7
7
|
makcu.connect()
|
8
8
|
return makcu
|
9
9
|
|
@@ -1,9 +1,8 @@
|
|
1
|
-
# makcu/__main__.py
|
2
|
-
|
3
1
|
import sys
|
4
|
-
import subprocess
|
5
2
|
import webbrowser
|
6
3
|
import os
|
4
|
+
from pathlib import Path
|
5
|
+
import pytest
|
7
6
|
from makcu import create_controller, MakcuConnectionError
|
8
7
|
|
9
8
|
def debug_console():
|
@@ -34,7 +33,7 @@ def debug_console():
|
|
34
33
|
def test_port(port):
|
35
34
|
try:
|
36
35
|
print(f"Trying to connect to {port} (without init command)...")
|
37
|
-
controller = create_controller(send_init=False)
|
36
|
+
controller = create_controller(fallback_com_port=port, send_init=False)
|
38
37
|
print(f"✅ Successfully connected to {port}")
|
39
38
|
controller.disconnect()
|
40
39
|
except MakcuConnectionError as e:
|
@@ -44,15 +43,33 @@ def test_port(port):
|
|
44
43
|
|
45
44
|
def run_tests():
|
46
45
|
print("🧪 Running Pytest Suite...")
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
|
47
|
+
package_dir = Path(__file__).resolve().parent
|
48
|
+
test_file = package_dir / "test_suite.py"
|
49
|
+
|
50
|
+
result = pytest.main([
|
51
|
+
str(test_file),
|
52
|
+
"--rootdir", str(package_dir),
|
53
|
+
"-v", "--tb=short",
|
54
|
+
"--capture=tee-sys",
|
55
|
+
"--html=latest_pytest.html",
|
56
|
+
"--self-contained-html"
|
50
57
|
])
|
51
58
|
|
52
59
|
report_path = os.path.abspath("latest_pytest.html")
|
53
|
-
|
54
|
-
|
60
|
+
if os.path.exists(report_path):
|
61
|
+
print(f"📄 Opening test report: {report_path}")
|
62
|
+
webbrowser.open(f"file://{report_path}")
|
63
|
+
else:
|
64
|
+
print("❌ Report not found. Something went wrong.")
|
65
|
+
|
66
|
+
if result != 0:
|
67
|
+
print("❌ Some tests failed.")
|
68
|
+
else:
|
69
|
+
print("✅ All tests passed.")
|
55
70
|
|
71
|
+
sys.exit(result)
|
72
|
+
|
56
73
|
def main():
|
57
74
|
args = sys.argv[1:]
|
58
75
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import pytest
|
2
|
+
from makcu import create_controller
|
3
|
+
import time
|
4
|
+
|
5
|
+
@pytest.fixture(scope="session")
|
6
|
+
def makcu():
|
7
|
+
ctrl = create_controller()
|
8
|
+
yield ctrl
|
9
|
+
ctrl.disconnect()
|
10
|
+
time.sleep(0.2)
|
11
|
+
|
12
|
+
@pytest.fixture(autouse=True)
|
13
|
+
def ensure_clean_exit(makcu):
|
14
|
+
yield
|
15
|
+
makcu.mouse.lock_left(False)
|
16
|
+
makcu.mouse.lock_right(False)
|
17
|
+
makcu.mouse.lock_middle(False)
|
18
|
+
makcu.mouse.lock_side1(False)
|
19
|
+
makcu.mouse.lock_side2(False)
|
20
|
+
makcu.mouse.lock_x(False)
|
21
|
+
makcu.mouse.lock_y(False)
|
22
|
+
makcu.enable_button_monitoring(False)
|
@@ -6,7 +6,6 @@ from .errors import MakcuConnectionError, MakcuTimeoutError
|
|
6
6
|
from .enums import MouseButton
|
7
7
|
|
8
8
|
class SerialTransport:
|
9
|
-
global fallback_com_port
|
10
9
|
baud_change_command = bytearray([0xDE, 0xAD, 0x05, 0x00, 0xA5, 0x00, 0x09, 0x3D, 0x00])
|
11
10
|
|
12
11
|
button_map = {
|
@@ -17,7 +16,8 @@ class SerialTransport:
|
|
17
16
|
4: 'mouse5'
|
18
17
|
}
|
19
18
|
|
20
|
-
def __init__(self, debug=False, send_init=True):
|
19
|
+
def __init__(self, fallback, debug=False, send_init=True):
|
20
|
+
self._fallback_com_port = fallback
|
21
21
|
self._log_messages = []
|
22
22
|
self.debug = debug
|
23
23
|
self.send_init = send_init
|
@@ -28,7 +28,6 @@ class SerialTransport:
|
|
28
28
|
self._stop_event = threading.Event()
|
29
29
|
self._listener_thread = None
|
30
30
|
self._button_states = {btn: False for btn in self.button_map.values()}
|
31
|
-
self._debounce_time_ms = 50
|
32
31
|
self._last_callback_time = {bit: 0 for bit in self.button_map}
|
33
32
|
self._pause_listener = False
|
34
33
|
|
@@ -48,6 +47,7 @@ class SerialTransport:
|
|
48
47
|
self.serial = None
|
49
48
|
self._current_baud = None
|
50
49
|
|
50
|
+
|
51
51
|
def receive_response(self, max_bytes=1024, max_lines=3, sent_command: str = "") -> str:
|
52
52
|
lines = []
|
53
53
|
try:
|
@@ -69,9 +69,6 @@ class SerialTransport:
|
|
69
69
|
lines.remove(command_clean)
|
70
70
|
return "\n".join(lines)
|
71
71
|
|
72
|
-
def set_debounce_time(self, ms: int):
|
73
|
-
self._debounce_time_ms = ms
|
74
|
-
|
75
72
|
def set_button_callback(self, callback):
|
76
73
|
self._button_callback = callback
|
77
74
|
|
@@ -84,22 +81,20 @@ class SerialTransport:
|
|
84
81
|
print(entry, flush=True)
|
85
82
|
|
86
83
|
def find_com_port(self):
|
87
|
-
global fallback_com_port
|
88
84
|
self._log("Searching for CH343 device...")
|
89
85
|
|
90
86
|
for port in list_ports.comports():
|
91
|
-
if "
|
87
|
+
if "VID:PID=1A86:55D3" in port.hwid.upper():
|
92
88
|
self._log(f"Device found: {port.device}")
|
93
89
|
return port.device
|
94
90
|
|
95
|
-
if
|
96
|
-
self._log(f"
|
97
|
-
return
|
91
|
+
if self._fallback_com_port:
|
92
|
+
self._log(f"Device not found. Falling back to specified port: {self._fallback_com_port}")
|
93
|
+
return self._fallback_com_port
|
98
94
|
else:
|
99
|
-
self._log("Fallback port
|
95
|
+
self._log("Fallback port not specified or invalid.")
|
100
96
|
return None
|
101
97
|
|
102
|
-
|
103
98
|
def _open_serial_port(self, port, baud_rate):
|
104
99
|
try:
|
105
100
|
self._log(f"Trying to open {port} at {baud_rate} baud.")
|
@@ -113,18 +108,14 @@ class SerialTransport:
|
|
113
108
|
self._log("Sending baud rate switch command to 4M.")
|
114
109
|
self.serial.write(self.baud_change_command)
|
115
110
|
self.serial.flush()
|
116
|
-
|
117
|
-
self.serial.
|
118
|
-
|
119
|
-
self.
|
120
|
-
|
121
|
-
self._current_baud = 4000000
|
122
|
-
self._log("Switched to 4M baud successfully.")
|
123
|
-
return True
|
124
|
-
else:
|
125
|
-
self._log("Failed to reopen port at 4M baud.")
|
111
|
+
time.sleep(0.05)
|
112
|
+
self.serial.baudrate = 4000000
|
113
|
+
self._current_baud = 4000000
|
114
|
+
self._log("Switched to 4M baud successfully.")
|
115
|
+
return True
|
126
116
|
return False
|
127
117
|
|
118
|
+
|
128
119
|
def connect(self):
|
129
120
|
if self._is_connected:
|
130
121
|
self._log("Already connected.")
|
@@ -162,7 +153,6 @@ class SerialTransport:
|
|
162
153
|
return self._is_connected
|
163
154
|
|
164
155
|
def send_command(self, command, expect_response=False):
|
165
|
-
time.sleep(0.06)
|
166
156
|
if not self._is_connected or not self.serial or not self.serial.is_open:
|
167
157
|
raise MakcuConnectionError("Serial connection not open.")
|
168
158
|
with self._lock:
|
@@ -179,15 +169,13 @@ class SerialTransport:
|
|
179
169
|
finally:
|
180
170
|
self._pause_listener = False
|
181
171
|
|
172
|
+
|
182
173
|
def get_button_states(self):
|
183
174
|
return dict(self._button_states)
|
184
175
|
|
185
176
|
def get_button_mask(self) -> int:
|
186
|
-
|
187
|
-
|
188
|
-
if self._button_states.get(name, False):
|
189
|
-
mask |= (1 << i)
|
190
|
-
return mask
|
177
|
+
return self._last_mask
|
178
|
+
|
191
179
|
|
192
180
|
def enable_button_monitoring(self, enable: bool = True):
|
193
181
|
self.send_command("km.buttons(1)" if enable else "km.buttons(0)")
|
@@ -224,54 +212,60 @@ class SerialTransport:
|
|
224
212
|
|
225
213
|
def _listen(self, debug=False):
|
226
214
|
self._log("Started listener thread")
|
227
|
-
last_value = None
|
228
215
|
button_states = {i: False for i in self.button_map}
|
229
216
|
self._last_mask = 0
|
217
|
+
self._last_callback_time = {bit: 0 for bit in self.button_map}
|
230
218
|
|
231
219
|
while self._is_connected and not self._stop_event.is_set():
|
232
220
|
if self._pause_listener:
|
233
221
|
time.sleep(0.001)
|
234
222
|
continue
|
223
|
+
|
235
224
|
try:
|
236
225
|
byte = self.serial.read(1)
|
237
|
-
if byte:
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
mask |= (1 << bit)
|
226
|
+
if not byte:
|
227
|
+
continue
|
228
|
+
|
229
|
+
value = byte[0]
|
230
|
+
byte_str = str(byte)
|
231
|
+
|
232
|
+
if not byte_str.startswith("b'\\x"):
|
233
|
+
continue
|
246
234
|
|
247
|
-
|
248
|
-
|
235
|
+
if value != self._last_mask:
|
236
|
+
if byte_str.startswith("b'\\x00"):
|
237
|
+
for bit, name in self.button_map.items():
|
238
|
+
button_states[bit] = False
|
239
|
+
self._button_states[name] = False
|
240
|
+
if debug:
|
241
|
+
print(f"{name} -> False")
|
242
|
+
else:
|
243
|
+
for bit, name in self.button_map.items():
|
244
|
+
is_pressed = bool(value & (1 << bit))
|
245
|
+
button_states[bit] = is_pressed
|
246
|
+
self._button_states[name] = is_pressed
|
247
|
+
if debug:
|
248
|
+
print(f"{name} -> {is_pressed}")
|
249
249
|
|
250
|
-
|
251
|
-
if self._button_callback and mask != self._last_mask:
|
250
|
+
if self._button_callback:
|
252
251
|
for bit, name in self.button_map.items():
|
253
252
|
previous = bool(self._last_mask & (1 << bit))
|
254
|
-
current = bool(
|
253
|
+
current = bool(value & (1 << bit))
|
255
254
|
if previous != current:
|
256
|
-
|
257
|
-
if
|
258
|
-
|
259
|
-
if button_enum:
|
260
|
-
self._button_callback(button_enum, current)
|
261
|
-
self._last_callback_time[bit] = now
|
255
|
+
button_enum = self._button_enum_map.get(bit)
|
256
|
+
if button_enum:
|
257
|
+
self._button_callback(button_enum, current)
|
262
258
|
|
263
|
-
self._last_mask =
|
259
|
+
self._last_mask = value
|
264
260
|
|
265
261
|
if debug:
|
266
262
|
pressed = [name for bit, name in self.button_map.items() if button_states[bit]]
|
267
263
|
button_str = ", ".join(pressed) if pressed else "No buttons pressed"
|
268
264
|
self._log(f"Byte: {value} (0x{value:02X}) -> {button_str}")
|
269
265
|
|
270
|
-
last_value = value
|
271
266
|
except serial.SerialException as e:
|
272
267
|
if "ClearCommError failed" not in str(e):
|
273
268
|
self._log(f"Serial error during listening: {e}")
|
274
269
|
break
|
275
|
-
time.sleep(0.0001)
|
276
270
|
|
277
271
|
self._log("Listener thread exiting")
|
@@ -6,8 +6,8 @@ from .errors import MakcuConnectionError
|
|
6
6
|
from .enums import MouseButton
|
7
7
|
|
8
8
|
class MakcuController:
|
9
|
-
def __init__(self, debug=False, send_init=True):
|
10
|
-
self.transport = SerialTransport(debug=debug, send_init=send_init)
|
9
|
+
def __init__(self, fallback_com_port, debug=False, send_init=True):
|
10
|
+
self.transport = SerialTransport(fallback_com_port, debug=debug, send_init=send_init)
|
11
11
|
self.mouse = Mouse(self.transport)
|
12
12
|
|
13
13
|
def connect(self):
|
@@ -25,8 +25,8 @@ class MakcuController:
|
|
25
25
|
|
26
26
|
def click(self, button: MouseButton):
|
27
27
|
self._check_connection()
|
28
|
-
self.
|
29
|
-
self.
|
28
|
+
self._send_button_command(button, 1)
|
29
|
+
self._send_button_command(button, 0)
|
30
30
|
|
31
31
|
def move(self, dx: int, dy: int):
|
32
32
|
self._check_connection()
|
@@ -72,6 +72,14 @@ class MakcuController:
|
|
72
72
|
self._check_connection()
|
73
73
|
self.mouse.lock_side2(lock)
|
74
74
|
|
75
|
+
def lock_x(self, lock: bool):
|
76
|
+
self._check_connection()
|
77
|
+
self.mouse.lock_x(lock)
|
78
|
+
|
79
|
+
def lock_y(self, lock: bool):
|
80
|
+
self._check_connection()
|
81
|
+
self.mouse.lock_y(lock)
|
82
|
+
|
75
83
|
def spoof_serial(self, serial: str):
|
76
84
|
self._check_connection()
|
77
85
|
self.mouse.spoof_serial(serial)
|
@@ -92,21 +100,22 @@ class MakcuController:
|
|
92
100
|
self._check_connection()
|
93
101
|
return self.transport.get_button_mask()
|
94
102
|
|
95
|
-
def is_locked(self, target: str) -> bool:
|
96
|
-
self._check_connection()
|
97
|
-
return self.mouse.is_locked(target)
|
98
|
-
|
99
103
|
def is_button_locked(self, button: MouseButton) -> bool:
|
100
104
|
self._check_connection()
|
101
105
|
return self.mouse.is_button_locked(button)
|
102
106
|
|
103
|
-
def capture(self, button: MouseButton):
|
104
|
-
|
105
|
-
|
107
|
+
#def capture(self, button: MouseButton):
|
108
|
+
# self._check_connection()
|
109
|
+
# self.mouse.begin_capture(button.name)
|
106
110
|
|
107
|
-
|
108
|
-
|
109
|
-
|
111
|
+
|
112
|
+
#def stop_capturing_clicks(self, button: str) -> int:
|
113
|
+
# self._check_connection()
|
114
|
+
# return self.mouse.stop_capturing_clicks(button)
|
115
|
+
|
116
|
+
#def get_captured_clicks(self, button: MouseButton) -> int:
|
117
|
+
# self._check_connection()
|
118
|
+
# return self.mouse.stop_capturing_clicks(button.name)
|
110
119
|
|
111
120
|
|
112
121
|
def click_human_like(self, button: MouseButton, count: int = 1,
|
@@ -114,15 +123,15 @@ class MakcuController:
|
|
114
123
|
self._check_connection()
|
115
124
|
|
116
125
|
timing_profiles = {
|
117
|
-
"normal":
|
118
|
-
"fast":
|
119
|
-
"slow":
|
126
|
+
"normal": (60, 120, 100, 180),
|
127
|
+
"fast": (30, 60, 50, 100),
|
128
|
+
"slow": (100, 180, 150, 300),
|
120
129
|
}
|
121
130
|
|
122
131
|
if profile not in timing_profiles:
|
123
132
|
raise ValueError(f"Invalid profile: {profile}. Choose from {list(timing_profiles.keys())}")
|
124
133
|
|
125
|
-
|
134
|
+
min_down, max_down, min_wait, max_wait = timing_profiles[profile]
|
126
135
|
|
127
136
|
for _ in range(count):
|
128
137
|
if jitter > 0:
|
@@ -130,10 +139,10 @@ class MakcuController:
|
|
130
139
|
dy = random.randint(-jitter, jitter)
|
131
140
|
self.mouse.move(dx, dy)
|
132
141
|
|
133
|
-
self.
|
134
|
-
time.sleep(random.uniform(
|
142
|
+
self.press(button)
|
143
|
+
time.sleep(random.uniform(min_down, max_down) / 1000.0)
|
135
144
|
self.mouse.release(button)
|
136
|
-
time.sleep(random.uniform(
|
145
|
+
time.sleep(random.uniform(min_wait, max_wait) / 1000.0)
|
137
146
|
|
138
147
|
def enable_button_monitoring(self, enable: bool = True):
|
139
148
|
self._check_connection()
|
@@ -147,51 +156,21 @@ class MakcuController:
|
|
147
156
|
self._check_connection()
|
148
157
|
return self.mouse.get_all_lock_states()
|
149
158
|
|
150
|
-
def
|
151
|
-
self.
|
152
|
-
self.transport.set_debounce_time(ms)
|
153
|
-
|
154
|
-
def start_capturing_clicks(self, button: str):
|
155
|
-
self._check_connection()
|
156
|
-
self.transport._capture_counts[button] = 0
|
157
|
-
self.transport._capture_active[button] = True
|
158
|
-
self.transport._capture_last_state[button] = None
|
159
|
-
self.mouse.begin_capture(button)
|
160
|
-
|
161
|
-
def stop_capturing_clicks(self, button: str) -> int:
|
162
|
-
self._check_connection()
|
163
|
-
self.transport._capture_active[button] = False
|
164
|
-
return self.mouse.stop_capturing_clicks(button)
|
159
|
+
def _send_button_command(self, button: MouseButton, state: int):
|
160
|
+
self.mouse._send_button_command(button, state)
|
165
161
|
|
166
162
|
def press(self, button: MouseButton):
|
167
163
|
self._check_connection()
|
168
|
-
self.
|
164
|
+
self._send_button_command(button, 1)
|
169
165
|
|
170
166
|
def release(self, button: MouseButton):
|
171
167
|
self._check_connection()
|
172
|
-
self.
|
173
|
-
|
174
|
-
def set_port(self, port: str):
|
175
|
-
import makcu.connection
|
176
|
-
makcu.connection.fallback_com_port = port
|
177
|
-
|
168
|
+
self._send_button_command(button, 0)
|
169
|
+
|
178
170
|
def get_button_states(self) -> dict:
|
179
171
|
self._check_connection()
|
180
172
|
return self.transport.get_button_states()
|
181
173
|
|
182
174
|
def is_button_pressed(self, button: MouseButton) -> bool:
|
183
175
|
self._check_connection()
|
184
|
-
return self.transport.get_button_states().get(button.name.lower(), False)
|
185
|
-
|
186
|
-
def get_raw_mask(self) -> int:
|
187
|
-
self._check_connection()
|
188
|
-
return self.transport.get_button_mask()
|
189
|
-
|
190
|
-
def get_virtual_bit_mapping(self) -> dict:
|
191
|
-
return {
|
192
|
-
"LEFT": 0,
|
193
|
-
"RIGHT": 1,
|
194
|
-
"MIDDLE": 2,
|
195
|
-
"MOUSE4": 3,
|
196
|
-
"MOUSE5": 4
|
197
|
-
}
|
176
|
+
return self.transport.get_button_states().get(button.name.lower(), False)
|
@@ -6,27 +6,17 @@ class Mouse:
|
|
6
6
|
def __init__(self, transport):
|
7
7
|
self.transport = transport
|
8
8
|
|
9
|
-
def
|
10
|
-
|
11
|
-
MouseButton.LEFT: "
|
12
|
-
MouseButton.RIGHT: "
|
13
|
-
MouseButton.MIDDLE: "
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
raise MakcuCommandError(f"Unsupported button
|
19
|
-
|
20
|
-
def release(self, button: MouseButton):
|
21
|
-
cmd = {
|
22
|
-
MouseButton.LEFT: "km.left(0)",
|
23
|
-
MouseButton.RIGHT: "km.right(0)",
|
24
|
-
MouseButton.MIDDLE: "km.middle(0)"
|
25
|
-
}.get(button)
|
26
|
-
if cmd:
|
27
|
-
self.transport.send_command(cmd)
|
28
|
-
else:
|
29
|
-
raise MakcuCommandError(f"Unsupported button for release(): {button}")
|
9
|
+
def _send_button_command(self, button: MouseButton, state: int):
|
10
|
+
command_map = {
|
11
|
+
MouseButton.LEFT: "left",
|
12
|
+
MouseButton.RIGHT: "right",
|
13
|
+
MouseButton.MIDDLE: "middle",
|
14
|
+
MouseButton.MOUSE4: "ms1",
|
15
|
+
MouseButton.MOUSE5: "ms2",
|
16
|
+
}
|
17
|
+
if button not in command_map:
|
18
|
+
raise MakcuCommandError(f"Unsupported button: {button}")
|
19
|
+
self.transport.send_command(f"km.{command_map[button]}({state})")
|
30
20
|
|
31
21
|
def move(self, x: int, y: int):
|
32
22
|
self.transport.send_command(f"km.move({x},{y})")
|
@@ -40,7 +30,9 @@ class Mouse:
|
|
40
30
|
def scroll(self, delta: int):
|
41
31
|
self.transport.send_command(f"km.wheel({delta})")
|
42
32
|
|
43
|
-
def lock_left(self, lock: bool):
|
33
|
+
def lock_left(self, lock: bool):
|
34
|
+
self.transport.send_command(f"km.lock_ml({int(lock)})")
|
35
|
+
print(f"km.lock_ml({int(lock)})")
|
44
36
|
def lock_middle(self, lock: bool): self.transport.send_command(f"km.lock_mm({int(lock)})")
|
45
37
|
def lock_right(self, lock: bool): self.transport.send_command(f"km.lock_mr({int(lock)})")
|
46
38
|
def lock_side1(self, lock: bool): self.transport.send_command(f"km.lock_ms1({int(lock)})")
|
@@ -117,16 +109,6 @@ class Mouse:
|
|
117
109
|
"""
|
118
110
|
return self.transport.read_captured_clicks(button)
|
119
111
|
|
120
|
-
def get_captured_clicks_enum(self, button: MouseButton) -> int:
|
121
|
-
mapping = {
|
122
|
-
MouseButton.LEFT: "LEFT",
|
123
|
-
MouseButton.RIGHT: "RIGHT",
|
124
|
-
MouseButton.MIDDLE: "MIDDLE",
|
125
|
-
MouseButton.MOUSE4: "MOUSE4",
|
126
|
-
MouseButton.MOUSE5: "MOUSE5",
|
127
|
-
}
|
128
|
-
return self.stop_capturing_clicks(mapping[button])
|
129
|
-
|
130
112
|
def get_all_lock_states(self) -> dict:
|
131
113
|
return {
|
132
114
|
target: self.is_locked(target)
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import pytest, time
|
2
|
+
from makcu import MouseButton
|
3
|
+
|
4
|
+
def test_press_and_release(makcu):
|
5
|
+
makcu.press(MouseButton.LEFT)
|
6
|
+
makcu.release(MouseButton.LEFT)
|
7
|
+
|
8
|
+
def test_firmware_version(makcu):
|
9
|
+
version = makcu.mouse.get_firmware_version()
|
10
|
+
assert version and len(version.strip()) > 0
|
11
|
+
|
12
|
+
def test_middle_click(makcu):
|
13
|
+
makcu.press(MouseButton.MIDDLE)
|
14
|
+
makcu.release(MouseButton.MIDDLE)
|
15
|
+
|
16
|
+
def test_device_info(makcu):
|
17
|
+
print("Fetching device info...")
|
18
|
+
info = makcu.mouse.get_device_info()
|
19
|
+
print(f"Device Info: {info}")
|
20
|
+
assert info.get("port")
|
21
|
+
assert info.get("isConnected") is True
|
22
|
+
|
23
|
+
def test_port_connection(makcu):
|
24
|
+
assert makcu.is_connected()
|
25
|
+
|
26
|
+
@pytest.mark.skip(reason="Capture test disabled until firmware supports tracking clicks from software input")
|
27
|
+
def test_capture_right_clicks(makcu):
|
28
|
+
makcu.mouse.lock_right(True)
|
29
|
+
assert makcu.mouse.is_button_locked(MouseButton.RIGHT)
|
30
|
+
|
31
|
+
makcu.mouse.begin_capture("RIGHT")
|
32
|
+
makcu.press(MouseButton.RIGHT)
|
33
|
+
makcu.mouse.release(MouseButton.RIGHT)
|
34
|
+
makcu.press(MouseButton.RIGHT)
|
35
|
+
makcu.mouse.release(MouseButton.RIGHT)
|
36
|
+
|
37
|
+
makcu.mouse.lock_right(False)
|
38
|
+
assert not makcu.mouse.is_button_locked(MouseButton.RIGHT)
|
39
|
+
|
40
|
+
count = makcu.mouse.stop_capturing_clicks("RIGHT")
|
41
|
+
assert count >= 2, f"Expected >=2 captured clicks, got {count}"
|
42
|
+
|
43
|
+
def test_button_mask(makcu):
|
44
|
+
print("Getting button mask...")
|
45
|
+
mask = makcu.get_button_mask()
|
46
|
+
print(f"Mask value: {mask}")
|
47
|
+
assert isinstance(mask, int)
|
48
|
+
|
49
|
+
def test_get_button_states(makcu):
|
50
|
+
states = makcu.get_button_states()
|
51
|
+
assert isinstance(states, dict)
|
52
|
+
for key in ['left', 'right', 'middle', 'mouse4', 'mouse5']:
|
53
|
+
assert key in states
|
54
|
+
|
55
|
+
def test_lock_state(makcu):
|
56
|
+
print("Locking LEFT button...")
|
57
|
+
makcu.lock_left(True)
|
58
|
+
|
59
|
+
time.sleep(0.1)
|
60
|
+
|
61
|
+
print("Querying lock state while LEFT is locked...")
|
62
|
+
assert makcu.is_button_locked(MouseButton.LEFT)
|
63
|
+
|
64
|
+
print("Querying all lock states...")
|
65
|
+
all_states = makcu.get_all_lock_states()
|
66
|
+
print(f"All lock states: {all_states}")
|
67
|
+
|
68
|
+
assert all_states["LEFT"] is True
|
69
|
+
assert isinstance(all_states["RIGHT"], bool)
|
70
|
+
|
71
|
+
makcu.press(MouseButton.LEFT)
|
72
|
+
makcu.release(MouseButton.LEFT)
|
73
|
+
|
74
|
+
time.sleep(0.1)
|
75
|
+
|
76
|
+
print("Unlocking LEFT button...")
|
77
|
+
makcu.lock_left(False)
|
78
|
+
|
79
|
+
print("Rechecking LEFT lock state after unlock...")
|
80
|
+
assert not makcu.is_button_locked(MouseButton.LEFT)
|
81
|
+
|
82
|
+
def test_makcu_behavior(makcu):
|
83
|
+
makcu.move(25, 25)
|
84
|
+
makcu.click(MouseButton.LEFT)
|
85
|
+
makcu.scroll(-2)
|
86
|
+
|
87
|
+
def test_reset_all(makcu):
|
88
|
+
makcu.mouse.lock_left(False)
|
89
|
+
makcu.mouse.lock_right(False)
|
90
|
+
makcu.mouse.lock_middle(False)
|
91
|
+
makcu.mouse.lock_side1(False)
|
92
|
+
makcu.mouse.lock_side2(False)
|
93
|
+
makcu.mouse.lock_x(False)
|
94
|
+
makcu.mouse.lock_y(False)
|
95
|
+
|
96
|
+
states = makcu.mouse.get_all_lock_states()
|
97
|
+
assert all(state is False for state in states.values() if state is not None), \
|
98
|
+
f"Expected all unlocked, got: {states}"
|
99
|
+
|
100
|
+
makcu.enable_button_monitoring(False)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: makcu
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.3
|
4
4
|
Summary: Python library to interact with Makcu devices.
|
5
5
|
Author: SleepyTotem
|
6
6
|
License: GPL
|
@@ -65,7 +65,7 @@ python -m makcu --runtest
|
|
65
65
|
```python
|
66
66
|
from makcu import create_controller, MouseButton
|
67
67
|
|
68
|
-
makcu = create_controller()
|
68
|
+
makcu = create_controller("COM1") # Fallback port
|
69
69
|
makcu.click(MouseButton.LEFT)
|
70
70
|
makcu.move(100, 50)
|
71
71
|
makcu.scroll(-1)
|
@@ -85,7 +85,7 @@ makcu = create_controller(debug=True, send_init=True)
|
|
85
85
|
#### Set fallback port manually
|
86
86
|
|
87
87
|
```python
|
88
|
-
makcu
|
88
|
+
makcu = create_controller("COM4") # Optional fallback com port
|
89
89
|
```
|
90
90
|
|
91
91
|
---
|
@@ -189,17 +189,9 @@ def on_button_event(button, pressed):
|
|
189
189
|
makcu.set_button_callback(on_button_event)
|
190
190
|
```
|
191
191
|
|
192
|
-
### Configure Debounce
|
193
|
-
|
194
|
-
```python
|
195
|
-
makcu.set_callback_debounce_time(100) # milliseconds
|
196
|
-
```
|
197
|
-
|
198
192
|
---
|
199
193
|
|
200
|
-
## Click Capturing
|
201
|
-
|
202
|
-
### ❌ **Currently broken (Pending Firmware Update)**
|
194
|
+
## ❌ Click Capturing (Pending Firmware Update)
|
203
195
|
|
204
196
|
Click capturing will allow you to detect and count click events in software.
|
205
197
|
|
@@ -248,6 +240,8 @@ if makcu.is_button_pressed(MouseButton.RIGHT):
|
|
248
240
|
### Send raw serial commands
|
249
241
|
|
250
242
|
```python
|
243
|
+
from makcu import create_controller
|
244
|
+
makcu = create_controller()
|
251
245
|
response = makcu.transport.send_command("km.version()", expect_response=True)
|
252
246
|
print(response)
|
253
247
|
```
|
@@ -313,3 +307,4 @@ Please open an issue on the project repository and I will get to it asap
|
|
313
307
|
## 🌐 Links
|
314
308
|
|
315
309
|
- 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
|
310
|
+
- 🔗 [PyPI Homepage](https://pypi.org/project/makcu/)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
LICENSE
|
2
|
+
MANIFEST.in
|
3
|
+
README.md
|
4
|
+
pyproject.toml
|
5
|
+
pytest.ini
|
6
|
+
makcu/__init__.py
|
7
|
+
makcu/__main__.py
|
8
|
+
makcu/conftest.py
|
9
|
+
makcu/connection.py
|
10
|
+
makcu/controller.py
|
11
|
+
makcu/enums.py
|
12
|
+
makcu/errors.py
|
13
|
+
makcu/mouse.py
|
14
|
+
makcu/test_suite.py
|
15
|
+
makcu.egg-info/PKG-INFO
|
16
|
+
makcu.egg-info/SOURCES.txt
|
17
|
+
makcu.egg-info/dependency_links.txt
|
18
|
+
makcu.egg-info/top_level.txt
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "makcu"
|
3
|
-
version = "0.1.
|
3
|
+
version = "0.1.3"
|
4
4
|
description = "Python library to interact with Makcu devices."
|
5
5
|
readme = "README.md"
|
6
6
|
requires-python = ">=3.7"
|
@@ -14,7 +14,11 @@ license = { text = "GPL" }
|
|
14
14
|
|
15
15
|
[tool.setuptools]
|
16
16
|
packages = ["makcu"]
|
17
|
+
include-package-data = true
|
17
18
|
|
18
19
|
[build-system]
|
19
20
|
requires = ["setuptools>=61.0"]
|
20
|
-
build-backend = "setuptools.build_meta"
|
21
|
+
build-backend = "setuptools.build_meta"
|
22
|
+
|
23
|
+
[tool.setuptools.data-files]
|
24
|
+
"." = ["tests/*.py"]
|
makcu-0.1.3/pytest.ini
ADDED
makcu-0.1.1/makcu/utils.py
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
LICENSE
|
2
|
-
README.md
|
3
|
-
pyproject.toml
|
4
|
-
makcu/__init__.py
|
5
|
-
makcu/__main__.py
|
6
|
-
makcu/connection.py
|
7
|
-
makcu/controller.py
|
8
|
-
makcu/enums.py
|
9
|
-
makcu/errors.py
|
10
|
-
makcu/mouse.py
|
11
|
-
makcu/utils.py
|
12
|
-
makcu.egg-info/PKG-INFO
|
13
|
-
makcu.egg-info/SOURCES.txt
|
14
|
-
makcu.egg-info/dependency_links.txt
|
15
|
-
makcu.egg-info/top_level.txt
|
16
|
-
tests/test_button_mask.py
|
17
|
-
tests/test_capture_right_clicks.py
|
18
|
-
tests/test_controller_behavior.py
|
19
|
-
tests/test_device_info.py
|
20
|
-
tests/test_firmware_version.py
|
21
|
-
tests/test_get_button_states.py
|
22
|
-
tests/test_get_raw_mask.py
|
23
|
-
tests/test_is_button_pressed.py
|
24
|
-
tests/test_lock_state.py
|
25
|
-
tests/test_middle_click.py
|
26
|
-
tests/test_port_connection.py
|
27
|
-
tests/test_press_and_release.py
|
28
|
-
tests/test_reset_all.py
|
29
|
-
tests/test_set_fallback_port.py
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# test_capture_right_clicks.py
|
2
|
-
import pytest
|
3
|
-
from makcu import MouseButton
|
4
|
-
|
5
|
-
@pytest.mark.skip(reason="Capture test disabled until firmware supports tracking clicks from software input")
|
6
|
-
def test_capture_right_clicks(makcu):
|
7
|
-
makcu.mouse.lock_right(True)
|
8
|
-
assert makcu.mouse.is_button_locked(MouseButton.RIGHT)
|
9
|
-
|
10
|
-
makcu.mouse.begin_capture("RIGHT")
|
11
|
-
|
12
|
-
makcu.mouse.press(MouseButton.RIGHT)
|
13
|
-
makcu.mouse.release(MouseButton.RIGHT)
|
14
|
-
makcu.mouse.press(MouseButton.RIGHT)
|
15
|
-
makcu.mouse.release(MouseButton.RIGHT)
|
16
|
-
|
17
|
-
makcu.mouse.lock_right(False)
|
18
|
-
assert not makcu.mouse.is_button_locked(MouseButton.RIGHT)
|
19
|
-
|
20
|
-
count = makcu.mouse.stop_capturing_clicks("RIGHT")
|
21
|
-
assert count >= 2, f"Expected >=2 captured clicks, got {count}"
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# test_get_button_states.py
|
2
|
-
|
3
|
-
import pytest
|
4
|
-
from makcu import create_controller
|
5
|
-
|
6
|
-
@pytest.fixture(scope="module")
|
7
|
-
def makcu():
|
8
|
-
ctrl = create_controller()
|
9
|
-
yield ctrl
|
10
|
-
ctrl.disconnect()
|
11
|
-
|
12
|
-
def test_get_button_states(makcu):
|
13
|
-
states = makcu.get_button_states()
|
14
|
-
assert isinstance(states, dict)
|
15
|
-
for key in ['left', 'right', 'middle', 'mouse4', 'mouse5']:
|
16
|
-
assert key in states
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# test_get_raw_mask.py
|
2
|
-
|
3
|
-
import pytest
|
4
|
-
from makcu import create_controller
|
5
|
-
|
6
|
-
@pytest.fixture(scope="module")
|
7
|
-
def makcu():
|
8
|
-
ctrl = create_controller()
|
9
|
-
yield ctrl
|
10
|
-
ctrl.disconnect()
|
11
|
-
|
12
|
-
def test_get_raw_mask(makcu):
|
13
|
-
mask = makcu.get_raw_mask()
|
14
|
-
assert isinstance(mask, int)
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# test_is_button_pressed.py
|
2
|
-
|
3
|
-
import pytest
|
4
|
-
from makcu import create_controller, MouseButton
|
5
|
-
|
6
|
-
@pytest.fixture(scope="module")
|
7
|
-
def makcu():
|
8
|
-
ctrl = create_controller()
|
9
|
-
yield ctrl
|
10
|
-
ctrl.disconnect()
|
11
|
-
|
12
|
-
def test_is_button_pressed(makcu):
|
13
|
-
assert makcu.is_button_pressed(MouseButton.LEFT) in [True, False]
|
@@ -1,22 +0,0 @@
|
|
1
|
-
# tests/test_lock_state.py
|
2
|
-
from makcu import MouseButton
|
3
|
-
|
4
|
-
def test_lock_state(makcu):
|
5
|
-
print("Locking LEFT button...")
|
6
|
-
makcu.lock_left(True)
|
7
|
-
|
8
|
-
print("Querying lock state while LEFT is locked...")
|
9
|
-
assert makcu.is_button_locked(MouseButton.LEFT)
|
10
|
-
|
11
|
-
print("Querying all lock states...")
|
12
|
-
all_states = makcu.get_all_lock_states()
|
13
|
-
print(f"All lock states: {all_states}")
|
14
|
-
|
15
|
-
assert all_states["LEFT"] is True
|
16
|
-
assert isinstance(all_states["RIGHT"], bool)
|
17
|
-
|
18
|
-
print("Unlocking LEFT button...")
|
19
|
-
makcu.lock_left(False)
|
20
|
-
|
21
|
-
print("Rechecking LEFT lock state after unlock...")
|
22
|
-
assert not makcu.is_button_locked(MouseButton.LEFT)
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# test_press_and_release.py
|
2
|
-
|
3
|
-
import pytest
|
4
|
-
from makcu import create_controller, MouseButton
|
5
|
-
|
6
|
-
@pytest.fixture(scope="module")
|
7
|
-
def makcu():
|
8
|
-
ctrl = create_controller()
|
9
|
-
yield ctrl
|
10
|
-
ctrl.disconnect()
|
11
|
-
|
12
|
-
def test_press_and_release(makcu):
|
13
|
-
makcu.press(MouseButton.LEFT)
|
14
|
-
makcu.release(MouseButton.LEFT)
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# test_reset_all.py
|
2
|
-
|
3
|
-
def test_reset_all(makcu):
|
4
|
-
# Unlock all buttons
|
5
|
-
makcu.mouse.lock_left(False)
|
6
|
-
makcu.mouse.lock_right(False)
|
7
|
-
makcu.mouse.lock_middle(False)
|
8
|
-
makcu.mouse.lock_side1(False)
|
9
|
-
makcu.mouse.lock_side2(False)
|
10
|
-
makcu.mouse.lock_x(False)
|
11
|
-
makcu.mouse.lock_y(False)
|
12
|
-
|
13
|
-
# Check lock states to confirm everything is clean
|
14
|
-
states = makcu.mouse.get_all_lock_states()
|
15
|
-
|
16
|
-
assert all(state is False for state in states.values() if state is not None), \
|
17
|
-
f"Expected all unlocked, got: {states}"
|
18
|
-
|
19
|
-
# Optionally disable monitoring
|
20
|
-
makcu.enable_button_monitoring(False)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|