makcu 0.1.2__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.2/makcu.egg-info → makcu-0.1.3}/PKG-INFO +2 -2
- {makcu-0.1.2 → makcu-0.1.3}/README.md +1 -1
- {makcu-0.1.2 → makcu-0.1.3}/makcu/__main__.py +6 -4
- {makcu-0.1.2 → makcu-0.1.3}/makcu/connection.py +35 -35
- {makcu-0.1.2 → makcu-0.1.3}/makcu/controller.py +33 -21
- {makcu-0.1.2 → makcu-0.1.3}/makcu/mouse.py +14 -22
- {makcu-0.1.2 → makcu-0.1.3}/makcu/test_suite.py +12 -10
- {makcu-0.1.2 → makcu-0.1.3/makcu.egg-info}/PKG-INFO +2 -2
- {makcu-0.1.2 → makcu-0.1.3}/pyproject.toml +1 -1
- {makcu-0.1.2 → makcu-0.1.3}/LICENSE +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/MANIFEST.in +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/makcu/__init__.py +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/makcu/conftest.py +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/makcu/enums.py +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/makcu/errors.py +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/makcu.egg-info/SOURCES.txt +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/makcu.egg-info/dependency_links.txt +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/makcu.egg-info/top_level.txt +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/pytest.ini +0 -0
- {makcu-0.1.2 → makcu-0.1.3}/setup.cfg +0 -0
@@ -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
|
@@ -307,4 +307,4 @@ Please open an issue on the project repository and I will get to it asap
|
|
307
307
|
## 🌐 Links
|
308
308
|
|
309
309
|
- 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
|
310
|
-
- 🔗 [
|
310
|
+
- 🔗 [PyPI Homepage](https://pypi.org/project/makcu/)
|
@@ -295,4 +295,4 @@ Please open an issue on the project repository and I will get to it asap
|
|
295
295
|
## 🌐 Links
|
296
296
|
|
297
297
|
- 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
|
298
|
-
- 🔗 [
|
298
|
+
- 🔗 [PyPI Homepage](https://pypi.org/project/makcu/)
|
@@ -33,7 +33,7 @@ def debug_console():
|
|
33
33
|
def test_port(port):
|
34
34
|
try:
|
35
35
|
print(f"Trying to connect to {port} (without init command)...")
|
36
|
-
controller = create_controller(send_init=False)
|
36
|
+
controller = create_controller(fallback_com_port=port, send_init=False)
|
37
37
|
print(f"✅ Successfully connected to {port}")
|
38
38
|
controller.disconnect()
|
39
39
|
except MakcuConnectionError as e:
|
@@ -47,14 +47,14 @@ def run_tests():
|
|
47
47
|
package_dir = Path(__file__).resolve().parent
|
48
48
|
test_file = package_dir / "test_suite.py"
|
49
49
|
|
50
|
-
|
50
|
+
result = pytest.main([
|
51
51
|
str(test_file),
|
52
52
|
"--rootdir", str(package_dir),
|
53
53
|
"-v", "--tb=short",
|
54
54
|
"--capture=tee-sys",
|
55
55
|
"--html=latest_pytest.html",
|
56
56
|
"--self-contained-html"
|
57
|
-
])
|
57
|
+
])
|
58
58
|
|
59
59
|
report_path = os.path.abspath("latest_pytest.html")
|
60
60
|
if os.path.exists(report_path):
|
@@ -63,11 +63,13 @@ def run_tests():
|
|
63
63
|
else:
|
64
64
|
print("❌ Report not found. Something went wrong.")
|
65
65
|
|
66
|
-
if result
|
66
|
+
if result != 0:
|
67
67
|
print("❌ Some tests failed.")
|
68
68
|
else:
|
69
69
|
print("✅ All tests passed.")
|
70
70
|
|
71
|
+
sys.exit(result)
|
72
|
+
|
71
73
|
def main():
|
72
74
|
args = sys.argv[1:]
|
73
75
|
|
@@ -153,7 +153,6 @@ class SerialTransport:
|
|
153
153
|
return self._is_connected
|
154
154
|
|
155
155
|
def send_command(self, command, expect_response=False):
|
156
|
-
time.sleep(0.06)
|
157
156
|
if not self._is_connected or not self.serial or not self.serial.is_open:
|
158
157
|
raise MakcuConnectionError("Serial connection not open.")
|
159
158
|
with self._lock:
|
@@ -170,15 +169,13 @@ class SerialTransport:
|
|
170
169
|
finally:
|
171
170
|
self._pause_listener = False
|
172
171
|
|
172
|
+
|
173
173
|
def get_button_states(self):
|
174
174
|
return dict(self._button_states)
|
175
175
|
|
176
176
|
def get_button_mask(self) -> int:
|
177
|
-
|
178
|
-
|
179
|
-
if self._button_states.get(name, False):
|
180
|
-
mask |= (1 << i)
|
181
|
-
return mask
|
177
|
+
return self._last_mask
|
178
|
+
|
182
179
|
|
183
180
|
def enable_button_monitoring(self, enable: bool = True):
|
184
181
|
self.send_command("km.buttons(1)" if enable else "km.buttons(0)")
|
@@ -226,46 +223,49 @@ class SerialTransport:
|
|
226
223
|
|
227
224
|
try:
|
228
225
|
byte = self.serial.read(1)
|
229
|
-
if byte:
|
230
|
-
|
231
|
-
|
232
|
-
if value != self._last_mask:
|
233
|
-
byte_str = str(byte)
|
226
|
+
if not byte:
|
227
|
+
continue
|
234
228
|
|
235
|
-
|
236
|
-
|
237
|
-
button_states[bit] = False
|
229
|
+
value = byte[0]
|
230
|
+
byte_str = str(byte)
|
238
231
|
|
239
|
-
|
240
|
-
|
241
|
-
is_pressed = bool(value & (1 << bit))
|
242
|
-
if is_pressed != button_states[bit]:
|
243
|
-
button_states[bit] = is_pressed
|
232
|
+
if not byte_str.startswith("b'\\x"):
|
233
|
+
continue
|
244
234
|
|
235
|
+
if value != self._last_mask:
|
236
|
+
if byte_str.startswith("b'\\x00"):
|
245
237
|
for bit, name in self.button_map.items():
|
246
|
-
|
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}")
|
247
249
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
250
|
+
if self._button_callback:
|
251
|
+
for bit, name in self.button_map.items():
|
252
|
+
previous = bool(self._last_mask & (1 << bit))
|
253
|
+
current = bool(value & (1 << bit))
|
254
|
+
if previous != current:
|
255
|
+
button_enum = self._button_enum_map.get(bit)
|
256
|
+
if button_enum:
|
257
|
+
self._button_callback(button_enum, current)
|
256
258
|
|
257
|
-
|
259
|
+
self._last_mask = value
|
258
260
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
261
|
+
if debug:
|
262
|
+
pressed = [name for bit, name in self.button_map.items() if button_states[bit]]
|
263
|
+
button_str = ", ".join(pressed) if pressed else "No buttons pressed"
|
264
|
+
self._log(f"Byte: {value} (0x{value:02X}) -> {button_str}")
|
263
265
|
|
264
266
|
except serial.SerialException as e:
|
265
267
|
if "ClearCommError failed" not in str(e):
|
266
268
|
self._log(f"Serial error during listening: {e}")
|
267
269
|
break
|
268
270
|
|
269
|
-
time.sleep(0.0001)
|
270
|
-
|
271
271
|
self._log("Listener thread exiting")
|
@@ -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)
|
@@ -96,13 +104,18 @@ class MakcuController:
|
|
96
104
|
self._check_connection()
|
97
105
|
return self.mouse.is_button_locked(button)
|
98
106
|
|
99
|
-
def capture(self, button: MouseButton):
|
100
|
-
|
101
|
-
|
107
|
+
#def capture(self, button: MouseButton):
|
108
|
+
# self._check_connection()
|
109
|
+
# self.mouse.begin_capture(button.name)
|
102
110
|
|
103
|
-
|
104
|
-
|
105
|
-
|
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)
|
106
119
|
|
107
120
|
|
108
121
|
def click_human_like(self, button: MouseButton, count: int = 1,
|
@@ -110,15 +123,15 @@ class MakcuController:
|
|
110
123
|
self._check_connection()
|
111
124
|
|
112
125
|
timing_profiles = {
|
113
|
-
"normal":
|
114
|
-
"fast":
|
115
|
-
"slow":
|
126
|
+
"normal": (60, 120, 100, 180),
|
127
|
+
"fast": (30, 60, 50, 100),
|
128
|
+
"slow": (100, 180, 150, 300),
|
116
129
|
}
|
117
130
|
|
118
131
|
if profile not in timing_profiles:
|
119
132
|
raise ValueError(f"Invalid profile: {profile}. Choose from {list(timing_profiles.keys())}")
|
120
133
|
|
121
|
-
|
134
|
+
min_down, max_down, min_wait, max_wait = timing_profiles[profile]
|
122
135
|
|
123
136
|
for _ in range(count):
|
124
137
|
if jitter > 0:
|
@@ -126,10 +139,10 @@ class MakcuController:
|
|
126
139
|
dy = random.randint(-jitter, jitter)
|
127
140
|
self.mouse.move(dx, dy)
|
128
141
|
|
129
|
-
self.
|
130
|
-
time.sleep(random.uniform(
|
142
|
+
self.press(button)
|
143
|
+
time.sleep(random.uniform(min_down, max_down) / 1000.0)
|
131
144
|
self.mouse.release(button)
|
132
|
-
time.sleep(random.uniform(
|
145
|
+
time.sleep(random.uniform(min_wait, max_wait) / 1000.0)
|
133
146
|
|
134
147
|
def enable_button_monitoring(self, enable: bool = True):
|
135
148
|
self._check_connection()
|
@@ -142,18 +155,17 @@ class MakcuController:
|
|
142
155
|
def get_all_lock_states(self) -> dict:
|
143
156
|
self._check_connection()
|
144
157
|
return self.mouse.get_all_lock_states()
|
145
|
-
|
146
|
-
def
|
147
|
-
self.
|
148
|
-
return self.mouse.stop_capturing_clicks(button)
|
158
|
+
|
159
|
+
def _send_button_command(self, button: MouseButton, state: int):
|
160
|
+
self.mouse._send_button_command(button, state)
|
149
161
|
|
150
162
|
def press(self, button: MouseButton):
|
151
163
|
self._check_connection()
|
152
|
-
self.
|
164
|
+
self._send_button_command(button, 1)
|
153
165
|
|
154
166
|
def release(self, button: MouseButton):
|
155
167
|
self._check_connection()
|
156
|
-
self.
|
168
|
+
self._send_button_command(button, 0)
|
157
169
|
|
158
170
|
def get_button_states(self) -> dict:
|
159
171
|
self._check_connection()
|
@@ -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)})")
|
@@ -1,22 +1,17 @@
|
|
1
|
-
import pytest
|
1
|
+
import pytest, time
|
2
2
|
from makcu import MouseButton
|
3
3
|
|
4
|
-
def test_is_button_pressed(makcu):
|
5
|
-
assert makcu.is_button_pressed(MouseButton.LEFT) in [True, False]
|
6
|
-
|
7
4
|
def test_press_and_release(makcu):
|
8
5
|
makcu.press(MouseButton.LEFT)
|
9
6
|
makcu.release(MouseButton.LEFT)
|
10
7
|
|
11
8
|
def test_firmware_version(makcu):
|
12
|
-
print("Getting firmware version...")
|
13
9
|
version = makcu.mouse.get_firmware_version()
|
14
|
-
print(f"Firmware version: {version}")
|
15
10
|
assert version and len(version.strip()) > 0
|
16
11
|
|
17
12
|
def test_middle_click(makcu):
|
18
|
-
makcu.
|
19
|
-
makcu.
|
13
|
+
makcu.press(MouseButton.MIDDLE)
|
14
|
+
makcu.release(MouseButton.MIDDLE)
|
20
15
|
|
21
16
|
def test_device_info(makcu):
|
22
17
|
print("Fetching device info...")
|
@@ -34,9 +29,9 @@ def test_capture_right_clicks(makcu):
|
|
34
29
|
assert makcu.mouse.is_button_locked(MouseButton.RIGHT)
|
35
30
|
|
36
31
|
makcu.mouse.begin_capture("RIGHT")
|
37
|
-
makcu.
|
32
|
+
makcu.press(MouseButton.RIGHT)
|
38
33
|
makcu.mouse.release(MouseButton.RIGHT)
|
39
|
-
makcu.
|
34
|
+
makcu.press(MouseButton.RIGHT)
|
40
35
|
makcu.mouse.release(MouseButton.RIGHT)
|
41
36
|
|
42
37
|
makcu.mouse.lock_right(False)
|
@@ -61,6 +56,8 @@ def test_lock_state(makcu):
|
|
61
56
|
print("Locking LEFT button...")
|
62
57
|
makcu.lock_left(True)
|
63
58
|
|
59
|
+
time.sleep(0.1)
|
60
|
+
|
64
61
|
print("Querying lock state while LEFT is locked...")
|
65
62
|
assert makcu.is_button_locked(MouseButton.LEFT)
|
66
63
|
|
@@ -71,6 +68,11 @@ def test_lock_state(makcu):
|
|
71
68
|
assert all_states["LEFT"] is True
|
72
69
|
assert isinstance(all_states["RIGHT"], bool)
|
73
70
|
|
71
|
+
makcu.press(MouseButton.LEFT)
|
72
|
+
makcu.release(MouseButton.LEFT)
|
73
|
+
|
74
|
+
time.sleep(0.1)
|
75
|
+
|
74
76
|
print("Unlocking LEFT button...")
|
75
77
|
makcu.lock_left(False)
|
76
78
|
|
@@ -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
|
@@ -307,4 +307,4 @@ Please open an issue on the project repository and I will get to it asap
|
|
307
307
|
## 🌐 Links
|
308
308
|
|
309
309
|
- 🔗 [Project Homepage](https://github.com/SleepyTotem/makcu-py-lib)
|
310
|
-
- 🔗 [
|
310
|
+
- 🔗 [PyPI Homepage](https://pypi.org/project/makcu/)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|