makcu 0.1.2__py3-none-any.whl → 0.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/__main__.py CHANGED
@@ -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
- sys.exit(pytest.main([
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.returncode != 0:
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
 
makcu/connection.py CHANGED
@@ -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
- mask = 0
178
- for i, name in self.button_map.items():
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
- value = byte[0]
231
-
232
- if value != self._last_mask:
233
- byte_str = str(byte)
226
+ if not byte:
227
+ continue
234
228
 
235
- if "b'\\x00" in byte_str:
236
- for bit in self.button_map:
237
- button_states[bit] = False
229
+ value = byte[0]
230
+ byte_str = str(byte)
238
231
 
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
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
- self._button_states[name] = button_states[bit]
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
- 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:
253
- button_enum = self._button_enum_map.get(bit)
254
- if button_enum:
255
- self._button_callback(button_enum, current)
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
- self._last_mask = value
259
+ self._last_mask = value
258
260
 
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}")
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")
makcu/controller.py CHANGED
@@ -25,8 +25,8 @@ class MakcuController:
25
25
 
26
26
  def click(self, button: MouseButton):
27
27
  self._check_connection()
28
- self.mouse.press(button)
29
- self.mouse.release(button)
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
- self._check_connection()
101
- self.mouse.begin_capture(button.name)
107
+ #def capture(self, button: MouseButton):
108
+ # self._check_connection()
109
+ # self.mouse.begin_capture(button.name)
102
110
 
103
- def get_captured_clicks(self, button: MouseButton) -> int:
104
- self._check_connection()
105
- return self.mouse.stop_capturing_clicks(button.name)
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": {"min_down": 60, "max_down": 120, "min_wait": 100, "max_wait": 180},
114
- "fast": {"min_down": 30, "max_down": 60, "min_wait": 50, "max_wait": 100},
115
- "slow": {"min_down": 100, "max_down": 180, "min_wait": 150, "max_wait": 300},
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
- t = timing_profiles[profile]
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.mouse.press(button)
130
- time.sleep(random.uniform(t["min_down"], t["max_down"]) / 1000.0)
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(t["min_wait"], t["max_wait"]) / 1000.0)
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 stop_capturing_clicks(self, button: str) -> int:
147
- self._check_connection()
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.mouse.press(button)
164
+ self._send_button_command(button, 1)
153
165
 
154
166
  def release(self, button: MouseButton):
155
167
  self._check_connection()
156
- self.mouse.release(button)
168
+ self._send_button_command(button, 0)
157
169
 
158
170
  def get_button_states(self) -> dict:
159
171
  self._check_connection()
makcu/mouse.py CHANGED
@@ -6,27 +6,17 @@ class Mouse:
6
6
  def __init__(self, transport):
7
7
  self.transport = transport
8
8
 
9
- def press(self, button: MouseButton):
10
- cmd = {
11
- MouseButton.LEFT: "km.left(1)",
12
- MouseButton.RIGHT: "km.right(1)",
13
- MouseButton.MIDDLE: "km.middle(1)"
14
- }.get(button)
15
- if cmd:
16
- self.transport.send_command(cmd)
17
- else:
18
- raise MakcuCommandError(f"Unsupported button for press(): {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): self.transport.send_command(f"km.lock_ml({int(lock)})")
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)})")
makcu/test_suite.py CHANGED
@@ -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.mouse.press(MouseButton.MIDDLE)
19
- makcu.mouse.release(MouseButton.MIDDLE)
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.mouse.press(MouseButton.RIGHT)
32
+ makcu.press(MouseButton.RIGHT)
38
33
  makcu.mouse.release(MouseButton.RIGHT)
39
- makcu.mouse.press(MouseButton.RIGHT)
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.2
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
- - 🔗 [PyPi Homepage](https://github.com/SleepyTotem/makcu-py-lib)
310
+ - 🔗 [PyPI Homepage](https://pypi.org/project/makcu/)
@@ -0,0 +1,14 @@
1
+ makcu/__init__.py,sha256=hCP6COi14T4C0V35crnbBEzJPa9hnwGb-gDPoxs_H6E,459
2
+ makcu/__main__.py,sha256=wjRtr7V6qd54w43lHmXQldlVffKMW27nkhKa4E5B9t8,2830
3
+ makcu/conftest.py,sha256=TQibb01_1OfzDrDU5u3IDlrfehXyr7E7jx3g0VySZmU,560
4
+ makcu/connection.py,sha256=QObUZ-iNH-LaC7hamgXDH5p2wH_VfJ6OEAv5qJ1RwVE,10208
5
+ makcu/controller.py,sha256=u2BeScKqQI19nSXQzRZqNL9rIj7_AyGDRcawdHvQtxM,5776
6
+ makcu/enums.py,sha256=VmvCLmpghVHuTAkvCGMfA14MgWTtFVMfsGQQNnJ58Ts,126
7
+ makcu/errors.py,sha256=4CkQ4gKa7GL5-BO3yOAJMMsy3QlUDDL42S1P1clqV4A,562
8
+ makcu/mouse.py,sha256=vvJ88r9tOLaGT6evHHx_K45wwTa_bxc9c0S6wj7gX6o,4686
9
+ makcu/test_suite.py,sha256=kmsLRv00mWLu3cUW5iAWL3QAhdqOL-rUwAWn6Rs1_ME,3104
10
+ makcu-0.1.3.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
11
+ makcu-0.1.3.dist-info/METADATA,sha256=hIYmeM8Qd9IWNCEfE_tbZ4DHqe-ttU9uvE8247ih2w0,5588
12
+ makcu-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ makcu-0.1.3.dist-info/top_level.txt,sha256=IRO1UVb5LK_ovjau0g4oObyXQqy00tVEE-yF5lPgw1w,6
14
+ makcu-0.1.3.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- makcu/__init__.py,sha256=hCP6COi14T4C0V35crnbBEzJPa9hnwGb-gDPoxs_H6E,459
2
- makcu/__main__.py,sha256=0fC-GdjCjC-ZCwE0Zwq-EJAuD5wEOo78Di7j9UAvcy0,2790
3
- makcu/conftest.py,sha256=TQibb01_1OfzDrDU5u3IDlrfehXyr7E7jx3g0VySZmU,560
4
- makcu/connection.py,sha256=AL1W-5ql7Wf54O_lDH8Tf36G73Ny1MUpx05TYXQt8zk,10268
5
- makcu/controller.py,sha256=AgulithR4NFHib-Q3mwmSlqhrodOmNFtojAnBgs3pMQ,5518
6
- makcu/enums.py,sha256=VmvCLmpghVHuTAkvCGMfA14MgWTtFVMfsGQQNnJ58Ts,126
7
- makcu/errors.py,sha256=4CkQ4gKa7GL5-BO3yOAJMMsy3QlUDDL42S1P1clqV4A,562
8
- makcu/mouse.py,sha256=rjrSE9PwO0e3CCtZ9oYqoxd17gVFNpeXlyBe5aV-QFc,4912
9
- makcu/test_suite.py,sha256=zko1SaUC-zOnEy7PH22DzJXXELVoV4JVHwagIdCTW5M,3196
10
- makcu-0.1.2.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
11
- makcu-0.1.2.dist-info/METADATA,sha256=lCvjpEpQ0GnQ7bbUWiB3JWqJoNqZiVxtp9fTj_k2P88,5600
12
- makcu-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- makcu-0.1.2.dist-info/top_level.txt,sha256=IRO1UVb5LK_ovjau0g4oObyXQqy00tVEE-yF5lPgw1w,6
14
- makcu-0.1.2.dist-info/RECORD,,
File without changes