makcu 0.1.1__tar.gz → 0.1.2__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.2/MANIFEST.in +7 -0
- {makcu-0.1.1 → makcu-0.1.2}/PKG-INFO +7 -12
- {makcu-0.1.1 → makcu-0.1.2}/README.md +7 -12
- {makcu-0.1.1 → makcu-0.1.2}/makcu/__init__.py +2 -2
- {makcu-0.1.1 → makcu-0.1.2}/makcu/__main__.py +24 -9
- makcu-0.1.2/makcu/conftest.py +22 -0
- {makcu-0.1.1 → makcu-0.1.2}/makcu/connection.py +43 -49
- {makcu-0.1.1 → makcu-0.1.2}/makcu/controller.py +4 -37
- {makcu-0.1.1 → makcu-0.1.2}/makcu/mouse.py +0 -10
- makcu-0.1.2/makcu/test_suite.py +98 -0
- {makcu-0.1.1 → makcu-0.1.2}/makcu.egg-info/PKG-INFO +7 -12
- makcu-0.1.2/makcu.egg-info/SOURCES.txt +18 -0
- {makcu-0.1.1 → makcu-0.1.2}/pyproject.toml +6 -2
- makcu-0.1.2/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.2}/LICENSE +0 -0
- {makcu-0.1.1 → makcu-0.1.2}/makcu/enums.py +0 -0
- {makcu-0.1.1 → makcu-0.1.2}/makcu/errors.py +0 -0
- {makcu-0.1.1 → makcu-0.1.2}/makcu.egg-info/dependency_links.txt +0 -0
- {makcu-0.1.1 → makcu-0.1.2}/makcu.egg-info/top_level.txt +0 -0
- {makcu-0.1.1 → makcu-0.1.2}/setup.cfg +0 -0
makcu-0.1.2/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.2
|
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://github.com/SleepyTotem/makcu-py-lib)
|
@@ -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://github.com/SleepyTotem/makcu-py-lib)
|
@@ -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():
|
@@ -44,14 +43,30 @@ def test_port(port):
|
|
44
43
|
|
45
44
|
def run_tests():
|
46
45
|
print("🧪 Running Pytest Suite...")
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
46
|
+
|
47
|
+
package_dir = Path(__file__).resolve().parent
|
48
|
+
test_file = package_dir / "test_suite.py"
|
49
|
+
|
50
|
+
sys.exit(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"
|
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.returncode != 0:
|
67
|
+
print("❌ Some tests failed.")
|
68
|
+
else:
|
69
|
+
print("✅ All tests passed.")
|
55
70
|
|
56
71
|
def main():
|
57
72
|
args = sys.argv[1:]
|
@@ -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.")
|
@@ -224,54 +215,57 @@ class SerialTransport:
|
|
224
215
|
|
225
216
|
def _listen(self, debug=False):
|
226
217
|
self._log("Started listener thread")
|
227
|
-
last_value = None
|
228
218
|
button_states = {i: False for i in self.button_map}
|
229
219
|
self._last_mask = 0
|
220
|
+
self._last_callback_time = {bit: 0 for bit in self.button_map}
|
230
221
|
|
231
222
|
while self._is_connected and not self._stop_event.is_set():
|
232
223
|
if self._pause_listener:
|
233
224
|
time.sleep(0.001)
|
234
225
|
continue
|
226
|
+
|
235
227
|
try:
|
236
228
|
byte = self.serial.read(1)
|
237
229
|
if byte:
|
238
230
|
value = byte[0]
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
231
|
+
|
232
|
+
if value != self._last_mask:
|
233
|
+
byte_str = str(byte)
|
234
|
+
|
235
|
+
if "b'\\x00" in byte_str:
|
236
|
+
for bit in self.button_map:
|
237
|
+
button_states[bit] = False
|
238
|
+
|
239
|
+
elif "b'\\x" in byte_str:
|
240
|
+
for bit, name in self.button_map.items():
|
241
|
+
is_pressed = bool(value & (1 << bit))
|
242
|
+
if is_pressed != button_states[bit]:
|
243
|
+
button_states[bit] = is_pressed
|
244
|
+
|
252
245
|
for bit, name in self.button_map.items():
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
246
|
+
self._button_states[name] = button_states[bit]
|
247
|
+
|
248
|
+
if self._button_callback:
|
249
|
+
for bit, name in self.button_map.items():
|
250
|
+
previous = bool(self._last_mask & (1 << bit))
|
251
|
+
current = bool(value & (1 << bit))
|
252
|
+
if previous != current:
|
258
253
|
button_enum = self._button_enum_map.get(bit)
|
259
254
|
if button_enum:
|
260
255
|
self._button_callback(button_enum, current)
|
261
|
-
self._last_callback_time[bit] = now
|
262
256
|
|
263
|
-
|
257
|
+
self._last_mask = value
|
264
258
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
259
|
+
if debug:
|
260
|
+
pressed = [name for bit, name in self.button_map.items() if button_states[bit]]
|
261
|
+
button_str = ", ".join(pressed) if pressed else "No buttons pressed"
|
262
|
+
self._log(f"Byte: {value} (0x{value:02X}) -> {button_str}")
|
269
263
|
|
270
|
-
last_value = value
|
271
264
|
except serial.SerialException as e:
|
272
265
|
if "ClearCommError failed" not in str(e):
|
273
266
|
self._log(f"Serial error during listening: {e}")
|
274
267
|
break
|
268
|
+
|
275
269
|
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):
|
@@ -92,10 +92,6 @@ class MakcuController:
|
|
92
92
|
self._check_connection()
|
93
93
|
return self.transport.get_button_mask()
|
94
94
|
|
95
|
-
def is_locked(self, target: str) -> bool:
|
96
|
-
self._check_connection()
|
97
|
-
return self.mouse.is_locked(target)
|
98
|
-
|
99
95
|
def is_button_locked(self, button: MouseButton) -> bool:
|
100
96
|
self._check_connection()
|
101
97
|
return self.mouse.is_button_locked(button)
|
@@ -146,21 +142,9 @@ class MakcuController:
|
|
146
142
|
def get_all_lock_states(self) -> dict:
|
147
143
|
self._check_connection()
|
148
144
|
return self.mouse.get_all_lock_states()
|
149
|
-
|
150
|
-
def set_callback_debounce_time(self, ms: int):
|
151
|
-
self._check_connection()
|
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
145
|
|
161
146
|
def stop_capturing_clicks(self, button: str) -> int:
|
162
147
|
self._check_connection()
|
163
|
-
self.transport._capture_active[button] = False
|
164
148
|
return self.mouse.stop_capturing_clicks(button)
|
165
149
|
|
166
150
|
def press(self, button: MouseButton):
|
@@ -170,28 +154,11 @@ class MakcuController:
|
|
170
154
|
def release(self, button: MouseButton):
|
171
155
|
self._check_connection()
|
172
156
|
self.mouse.release(button)
|
173
|
-
|
174
|
-
def set_port(self, port: str):
|
175
|
-
import makcu.connection
|
176
|
-
makcu.connection.fallback_com_port = port
|
177
|
-
|
157
|
+
|
178
158
|
def get_button_states(self) -> dict:
|
179
159
|
self._check_connection()
|
180
160
|
return self.transport.get_button_states()
|
181
161
|
|
182
162
|
def is_button_pressed(self, button: MouseButton) -> bool:
|
183
163
|
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
|
-
}
|
164
|
+
return self.transport.get_button_states().get(button.name.lower(), False)
|
@@ -117,16 +117,6 @@ class Mouse:
|
|
117
117
|
"""
|
118
118
|
return self.transport.read_captured_clicks(button)
|
119
119
|
|
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
120
|
def get_all_lock_states(self) -> dict:
|
131
121
|
return {
|
132
122
|
target: self.is_locked(target)
|
@@ -0,0 +1,98 @@
|
|
1
|
+
import pytest
|
2
|
+
from makcu import MouseButton
|
3
|
+
|
4
|
+
def test_is_button_pressed(makcu):
|
5
|
+
assert makcu.is_button_pressed(MouseButton.LEFT) in [True, False]
|
6
|
+
|
7
|
+
def test_press_and_release(makcu):
|
8
|
+
makcu.press(MouseButton.LEFT)
|
9
|
+
makcu.release(MouseButton.LEFT)
|
10
|
+
|
11
|
+
def test_firmware_version(makcu):
|
12
|
+
print("Getting firmware version...")
|
13
|
+
version = makcu.mouse.get_firmware_version()
|
14
|
+
print(f"Firmware version: {version}")
|
15
|
+
assert version and len(version.strip()) > 0
|
16
|
+
|
17
|
+
def test_middle_click(makcu):
|
18
|
+
makcu.mouse.press(MouseButton.MIDDLE)
|
19
|
+
makcu.mouse.release(MouseButton.MIDDLE)
|
20
|
+
|
21
|
+
def test_device_info(makcu):
|
22
|
+
print("Fetching device info...")
|
23
|
+
info = makcu.mouse.get_device_info()
|
24
|
+
print(f"Device Info: {info}")
|
25
|
+
assert info.get("port")
|
26
|
+
assert info.get("isConnected") is True
|
27
|
+
|
28
|
+
def test_port_connection(makcu):
|
29
|
+
assert makcu.is_connected()
|
30
|
+
|
31
|
+
@pytest.mark.skip(reason="Capture test disabled until firmware supports tracking clicks from software input")
|
32
|
+
def test_capture_right_clicks(makcu):
|
33
|
+
makcu.mouse.lock_right(True)
|
34
|
+
assert makcu.mouse.is_button_locked(MouseButton.RIGHT)
|
35
|
+
|
36
|
+
makcu.mouse.begin_capture("RIGHT")
|
37
|
+
makcu.mouse.press(MouseButton.RIGHT)
|
38
|
+
makcu.mouse.release(MouseButton.RIGHT)
|
39
|
+
makcu.mouse.press(MouseButton.RIGHT)
|
40
|
+
makcu.mouse.release(MouseButton.RIGHT)
|
41
|
+
|
42
|
+
makcu.mouse.lock_right(False)
|
43
|
+
assert not makcu.mouse.is_button_locked(MouseButton.RIGHT)
|
44
|
+
|
45
|
+
count = makcu.mouse.stop_capturing_clicks("RIGHT")
|
46
|
+
assert count >= 2, f"Expected >=2 captured clicks, got {count}"
|
47
|
+
|
48
|
+
def test_button_mask(makcu):
|
49
|
+
print("Getting button mask...")
|
50
|
+
mask = makcu.get_button_mask()
|
51
|
+
print(f"Mask value: {mask}")
|
52
|
+
assert isinstance(mask, int)
|
53
|
+
|
54
|
+
def test_get_button_states(makcu):
|
55
|
+
states = makcu.get_button_states()
|
56
|
+
assert isinstance(states, dict)
|
57
|
+
for key in ['left', 'right', 'middle', 'mouse4', 'mouse5']:
|
58
|
+
assert key in states
|
59
|
+
|
60
|
+
def test_lock_state(makcu):
|
61
|
+
print("Locking LEFT button...")
|
62
|
+
makcu.lock_left(True)
|
63
|
+
|
64
|
+
print("Querying lock state while LEFT is locked...")
|
65
|
+
assert makcu.is_button_locked(MouseButton.LEFT)
|
66
|
+
|
67
|
+
print("Querying all lock states...")
|
68
|
+
all_states = makcu.get_all_lock_states()
|
69
|
+
print(f"All lock states: {all_states}")
|
70
|
+
|
71
|
+
assert all_states["LEFT"] is True
|
72
|
+
assert isinstance(all_states["RIGHT"], bool)
|
73
|
+
|
74
|
+
print("Unlocking LEFT button...")
|
75
|
+
makcu.lock_left(False)
|
76
|
+
|
77
|
+
print("Rechecking LEFT lock state after unlock...")
|
78
|
+
assert not makcu.is_button_locked(MouseButton.LEFT)
|
79
|
+
|
80
|
+
def test_makcu_behavior(makcu):
|
81
|
+
makcu.move(25, 25)
|
82
|
+
makcu.click(MouseButton.LEFT)
|
83
|
+
makcu.scroll(-2)
|
84
|
+
|
85
|
+
def test_reset_all(makcu):
|
86
|
+
makcu.mouse.lock_left(False)
|
87
|
+
makcu.mouse.lock_right(False)
|
88
|
+
makcu.mouse.lock_middle(False)
|
89
|
+
makcu.mouse.lock_side1(False)
|
90
|
+
makcu.mouse.lock_side2(False)
|
91
|
+
makcu.mouse.lock_x(False)
|
92
|
+
makcu.mouse.lock_y(False)
|
93
|
+
|
94
|
+
states = makcu.mouse.get_all_lock_states()
|
95
|
+
assert all(state is False for state in states.values() if state is not None), \
|
96
|
+
f"Expected all unlocked, got: {states}"
|
97
|
+
|
98
|
+
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.2
|
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://github.com/SleepyTotem/makcu-py-lib)
|
@@ -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.2"
|
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.2/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
|