makcu 2.2.0__py3-none-any.whl → 2.2.1__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/README.md CHANGED
@@ -1,405 +1,406 @@
1
- # 🖱️ Makcu Python Library v2.2.0
2
-
3
- [![PyPI Version](https://img.shields.io/pypi/v/makcu.svg)](https://pypi.org/project/makcu/)
4
- [![Python Support](https://img.shields.io/pypi/pyversions/makcu.svg)](https://pypi.org/project/makcu/)
5
- [![License](https://img.shields.io/badge/license-GPL-blue.svg)](LICENSE)
6
-
7
- Makcu Py Lib is a high-performance Python library for controlling Makcu devices — now with **async/await support**, **zero-delay command execution**, and **automatic reconnection**!
8
-
9
- ---
10
-
11
- ## 📦 Installation
12
-
13
- ### Recommended: PyPI
14
-
15
- ```bash
16
- pip install makcu
17
- ```
18
-
19
- ### From Source
20
-
21
- ```bash
22
- git clone https://github.com/SleepyTotem/makcu-py-lib
23
- cd makcu-py-lib
24
- pip install .
25
- ```
26
-
27
- ---
28
-
29
- ## 🧠 Quick Start
30
-
31
- ### Synchronous API (Classic)
32
-
33
- ```python
34
- from makcu import create_controller, MouseButton
35
-
36
- # Create and connect
37
- makcu = create_controller(debug=True, auto_reconnect=True)
38
-
39
- # Basic operations
40
- makcu.click(MouseButton.LEFT)
41
- makcu.move(100, 50)
42
- makcu.scroll(-1)
43
-
44
- # Human-like interaction
45
- makcu.click_human_like(MouseButton.LEFT, count=2, profile="gaming", jitter=3)
46
-
47
- # Clean disconnect
48
- makcu.disconnect()
49
- ```
50
-
51
- ### Asynchronous API (New!)
52
-
53
- ```python
54
- import asyncio
55
- from makcu import create_async_controller, MouseButton
56
-
57
- async def main():
58
- # Auto-connect with context manager
59
- async with await create_async_controller(debug=True) as makcu:
60
- # Parallel operations
61
- await asyncio.gather(
62
- makcu.move(100, 0),
63
- makcu.click(MouseButton.LEFT),
64
- makcu.scroll(-1)
65
- )
66
-
67
- # Human-like clicking
68
- await makcu.click_human_like(MouseButton.RIGHT, count=3)
69
-
70
- asyncio.run(main())
71
- ```
72
-
73
- ---
74
-
75
- ## 🎮 Core Features
76
-
77
- ### Mouse Control
78
-
79
- ```python
80
- # Button actions
81
- await makcu.click(MouseButton.LEFT)
82
- await makcu.double_click(MouseButton.RIGHT)
83
- await makcu.press(MouseButton.MIDDLE)
84
- await makcu.release(MouseButton.MIDDLE)
85
-
86
- # Movement
87
- await makcu.move(100, 50) # Relative movement
88
- await makcu.move_smooth(200, 100, segments=20) # Smooth interpolation
89
- await makcu.move_bezier(150, 150, segments=30, ctrl_x=75, ctrl_y=200) # Bezier curve
90
-
91
- # Scrolling
92
- await makcu.scroll(-5) # Scroll down
93
- await makcu.scroll(3) # Scroll up
94
-
95
- # Dragging
96
- await makcu.drag(0, 0, 300, 200, button=MouseButton.LEFT, duration=1.5)
97
- ```
98
-
99
- ### Button & Axis Locking
100
-
101
- ```python
102
- # New unified locking API
103
- await makcu.lock(MouseButton.LEFT) # Lock left button
104
- await makcu.unlock(MouseButton.RIGHT) # Unlock right button
105
- await makcu.lock("X") # Lock X-axis movement
106
- await makcu.unlock("Y") # Unlock Y-axis movement
107
-
108
- # Query lock states (no delays!)
109
- is_locked = await makcu.is_locked(MouseButton.LEFT)
110
- all_states = await makcu.get_all_lock_states()
111
- # Returns: {"LEFT": True, "RIGHT": False, "X": True, ...}
112
- ```
113
-
114
- ### Human-like Interactions
115
-
116
- ```python
117
- # Realistic clicking with timing variations
118
- await makcu.click_human_like(
119
- button=MouseButton.LEFT,
120
- count=5,
121
- profile="gaming", # "fast", "normal", "slow", "variable", "gaming"
122
- jitter=5 # Random mouse movement between clicks
123
- )
124
- ```
125
-
126
- ### Button Event Monitoring
127
-
128
- ```python
129
- # Real-time button monitoring
130
- def on_button_event(button: MouseButton, pressed: bool):
131
- print(f"{button.name} {'pressed' if pressed else 'released'}")
132
-
133
- makcu.set_button_callback(on_button_event)
134
- await makcu.enable_button_monitoring(True)
135
-
136
- # Check current button states
137
- states = makcu.get_button_states()
138
- if makcu.is_pressed(MouseButton.RIGHT):
139
- print("Right button is pressed")
140
- ```
141
-
142
- ### Connection Management
143
-
144
- ```python
145
- # Auto-reconnection on disconnect
146
- makcu = await create_async_controller(auto_reconnect=True)
147
-
148
- # Connection status callbacks
149
- @makcu.on_connection_change
150
- async def handle_connection(connected: bool):
151
- if connected:
152
- print("Device reconnected!")
153
- else:
154
- print("Device disconnected!")
155
-
156
- # Manual reconnection
157
- if not makcu.is_connected():
158
- await makcu.connect()
159
- ```
160
-
161
- ---
162
-
163
- ## 🔧 Advanced Features
164
-
165
- ### Batch Operations
166
-
167
- ```python
168
- # Execute multiple commands efficiently
169
- async def combo_action():
170
- await makcu.batch_execute([
171
- lambda: makcu.move(50, 0),
172
- lambda: makcu.click(MouseButton.LEFT),
173
- lambda: makcu.move(-50, 0),
174
- lambda: makcu.click(MouseButton.RIGHT)
175
- ])
176
- ```
177
-
178
- ### Device Information
179
-
180
- ```python
181
- # Get device details
182
- info = await makcu.get_device_info()
183
- # {'port': 'COM3', 'vid': '0x1a86', 'pid': '0x55d3', ...}
184
-
185
- # Firmware version
186
- version = await makcu.get_firmware_version()
187
- ```
188
-
189
- ### Serial Spoofing
190
-
191
- ```python
192
- # Spoof device serial
193
- await makcu.spoof_serial("CUSTOM123456")
194
-
195
- # Reset to default
196
- await makcu.reset_serial()
197
- ```
198
-
199
- ### Low-Level Access
200
-
201
- ```python
202
- # Send raw commands with tracked responses
203
- response = await makcu.transport.async_send_command(
204
- "km.version()",
205
- expect_response=True,
206
- timeout=0.1 # Optimized for gaming
207
- )
208
- ```
209
-
210
- ---
211
-
212
- ## 🧪 Command-Line Tools
213
-
214
- ```bash
215
- # Interactive debug console
216
- python -m makcu --debug
217
-
218
- # Test specific port
219
- python -m makcu --testPort COM3
220
-
221
- # Run automated tests
222
- python -m makcu --runtest
223
- ```
224
-
225
- ### Tool Descriptions
226
-
227
- - `--debug`: Launches an interactive console where you can type raw device commands and see live responses.
228
- - `--testPort COMx`: Attempts to connect to the given COM port and reports success or failure.
229
- - `--runtest`: Runs `test_suite.py` using `pytest` and opens a detailed HTML test report.
230
-
231
- ---
232
-
233
- ### Test Suite
234
-
235
- - File: `test_suite.py`
236
- - Run with: `python -m makcu --runtest`
237
- - Output: `latest_pytest.html`
238
-
239
- Includes tests for:
240
- - Port connection
241
- - Firmware version check
242
- - Mouse movement and button control
243
- - Button masking and locking
244
-
245
- ---
246
-
247
- ## Test Timings (v1.3 vs v1.4 vs v2.0)
248
-
249
- | Test Name | v1.3 | v1.4 | v2.0 | Improvement (v1.3 → v2.0) |
250
- |--------------------------|--------|-------|-------|----------------------------|
251
- | connect_to_port | ~100ms | ~55ms | **46ms** | ~2.2x faster |
252
- | press_and_release | ~18ms | ~9ms | **1ms** | ~18x faster |
253
- | firmware_version | ~20ms | ~9ms | **1ms** | ~20x faster |
254
- | middle_click | ~18ms | ~9ms | **1ms** | ~18x faster |
255
- | device_info | ~25ms | ~13ms | **6ms** | ~4.1x faster |
256
- | port_connection | ~20ms | ~9ms | **1ms** | ~20x faster |
257
- | button_mask | ~17ms | ~8ms | **1ms** | ~17x faster |
258
- | get_button_states | ~18ms | ~9ms | **1ms** | ~18x faster |
259
- | lock_state | ~33ms | ~10ms | **1ms** | ~33x faster |
260
- | makcu_behavior | ~20ms | ~10ms | **1ms** | ~20x faster |
261
- | batch_commands | ~350ms | ~90ms | **3ms** | ~117x faster |
262
- | rapid_moves | ~17ms | ~8ms | **2ms** | ~8.5x faster |
263
- | button_performance | ~18ms | ~9ms | **2ms** | ~9x faster |
264
- | mixed_operations | ~22ms | ~10ms | **2ms** | ~11x faster |
265
-
266
- Based on the measured test suite, v2.0 is on average **~17× faster** than v1.3 across all core operations.
267
-
268
-
269
- ### Gaming Performance Targets (v2.0)
270
-
271
- - **144Hz Gaming**: 7ms frame time — ✅ Easily met (avg 1–3ms per operation)
272
- - **240Hz Gaming**: 4.2ms frame time — ✅ Consistently met (most ops ≤ 2ms)
273
- - **360Hz Gaming**: 2.8ms frame time — ⚡ Achievable for atomic/single ops
274
-
275
- ---
276
-
277
- ## 🏎️ Performance Optimization Details
278
-
279
- ### Version History & Performance
280
-
281
- - **v1.3 and earlier**: Original implementation with sleep delays
282
- - **v1.4**: Initial optimizations, removed some sleep delays
283
- - **v2.0**: Complete rewrite with zero-delay architecture
284
-
285
- ### Key Optimizations in v2.0
286
-
287
- 1. **Pre-computed Commands**: All commands are pre-formatted at initialization
288
- 2. **Bitwise Operations**: Button states use single integer with bit manipulation
289
- 3. **Zero-Copy Buffers**: Pre-allocated buffers for parsing
290
- 4. **Reduced Timeouts**: Gaming-optimized timeouts (100ms default)
291
- 5. **Cache Everything**: Connection states, lock states, and device info cached
292
- 6. **Minimal Allocations**: Reuse objects and avoid string formatting
293
- 7. **Fast Serial Settings**: 1ms read timeout, 10ms write timeout
294
- 8. **Optimized Listener**: Batch processing with minimal overhead
295
-
296
- ### Tips for Maximum Performance
297
-
298
- ```python
299
- # Disable debug mode in production
300
- makcu = create_controller(debug=False)
301
-
302
- # Use cached connection checks
303
- if makcu.is_connected(): # Cached, no serial check
304
- makcu.click(MouseButton.LEFT)
305
-
306
- # Batch similar operations
307
- with makcu: # Context manager ensures connection
308
- for _ in range(10):
309
- makcu.move(10, 0) # No connection check per call
310
- ```
311
-
312
- ---
313
-
314
- ## 🔍 Debugging
315
-
316
- Enable debug mode for detailed logging:
317
-
318
- ```python
319
- makcu = await create_async_controller(debug=True)
320
-
321
- # View command flow (optimized timestamps)
322
- # [123.456] [INFO] Sent command #42: km.move(100,50)
323
- # [123.458] [DEBUG] Command #42 completed in 0.002s
324
- ```
325
-
326
- ---
327
-
328
- ## 🏗️ Migration from v1.x
329
-
330
- Most code works without changes! Key differences:
331
-
332
- ```python
333
- # v1.x (still works)
334
- makcu = create_controller()
335
- makcu.move(100, 100)
336
-
337
- # v2.0 (async)
338
- makcu = await create_async_controller()
339
- await makcu.move(100, 100)
340
-
341
- # v2.0 context manager (auto cleanup)
342
- async with await create_async_controller() as makcu:
343
- await makcu.click(MouseButton.LEFT)
344
- ```
345
-
346
- ---
347
-
348
- ## 📚 API Reference
349
-
350
- ### Enumerations
351
-
352
- ```python
353
- from makcu import MouseButton
354
-
355
- MouseButton.LEFT # Left mouse button
356
- MouseButton.RIGHT # Right mouse button
357
- MouseButton.MIDDLE # Middle mouse button
358
- MouseButton.MOUSE4 # Side button 1
359
- MouseButton.MOUSE5 # Side button 2
360
- ```
361
-
362
- ### Exception Handling
363
-
364
- ```python
365
- from makcu import MakcuError, MakcuConnectionError, MakcuTimeoutError
366
-
367
- try:
368
- makcu = await create_async_controller()
369
- except MakcuConnectionError as e:
370
- print(f"Connection failed: {e}")
371
- except MakcuTimeoutError as e:
372
- print(f"Command timed out: {e}")
373
- ```
374
-
375
- ---
376
-
377
- ## 🛠️ Technical Details
378
-
379
- - **Protocol**: CH343 USB serial at 4Mbps
380
- - **Command Format**: ASCII with optional ID tracking (`command#ID`)
381
- - **Response Format**: `>>> #ID:response` for tracked commands
382
- - **Threading**: High-priority listener thread with async bridge
383
- - **Auto-Discovery**: VID:PID=1A86:55D3 detection
384
- - **Buffer Size**: 4KB read buffer, 256B line buffer
385
- - **Cleanup Interval**: 50ms for timed-out commands
386
-
387
- ---
388
-
389
- ## 📜 License
390
-
391
- GPL License © SleepyTotem
392
-
393
- ---
394
-
395
- ## 🙋 Support
396
-
397
- - **Issues**: [GitHub Issues](https://github.com/SleepyTotem/makcu-py-lib/issues)
398
-
399
- ---
400
-
401
- ## 🌐 Links
402
-
403
- - [GitHub Repository](https://github.com/SleepyTotem/makcu-py-lib)
404
- - [PyPI Package](https://pypi.org/project/makcu/)
405
- - [Documentation](https://makcu.readthedocs.io/) *(coming soon)*
1
+ # 🖱️ Makcu Python Library v2.2.0
2
+
3
+ [![PyPI Version](https://img.shields.io/pypi/v/makcu.svg)](https://pypi.org/project/makcu/)
4
+ [![Python Support](https://img.shields.io/pypi/pyversions/makcu.svg)](https://pypi.org/project/makcu/)
5
+ [![License](https://img.shields.io/badge/license-GPL-blue.svg)](LICENSE)
6
+
7
+ Makcu Py Lib is a high-performance Python library for controlling Makcu devices — now with **async/await support**, **zero-delay command execution**, and **automatic reconnection**!
8
+
9
+ ---
10
+
11
+ ## 📦 Installation
12
+
13
+ ### Recommended: PyPI
14
+
15
+ ```bash
16
+ pip install makcu
17
+ ```
18
+
19
+ ### From Source
20
+
21
+ ```bash
22
+ git clone https://github.com/SleepyTotem/makcu-py-lib
23
+ cd makcu-py-lib
24
+ pip install .
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 🧠 Quick Start
30
+
31
+ ### Synchronous API (Classic)
32
+
33
+ ```python
34
+ from makcu import create_controller, MouseButton
35
+
36
+ # Create and connect
37
+ makcu = create_controller(debug=True, auto_reconnect=True)
38
+
39
+ # Basic operations
40
+ makcu.click(MouseButton.LEFT)
41
+ makcu.move(100, 50)
42
+ makcu.scroll(-1)
43
+
44
+ # Human-like interaction
45
+ makcu.click_human_like(MouseButton.LEFT, count=2, profile="gaming", jitter=3)
46
+
47
+ # Clean disconnect
48
+ makcu.disconnect()
49
+ ```
50
+
51
+ ### Asynchronous API (New!)
52
+
53
+ ```python
54
+ import asyncio
55
+ from makcu import create_async_controller, MouseButton
56
+
57
+ async def main():
58
+ # Auto-connect with context manager
59
+ async with await create_async_controller(debug=True) as makcu:
60
+ # Parallel operations
61
+ await asyncio.gather(
62
+ makcu.move(100, 0),
63
+ makcu.click(MouseButton.LEFT),
64
+ makcu.scroll(-1)
65
+ )
66
+
67
+ # Human-like clicking
68
+ await makcu.click_human_like(MouseButton.RIGHT, count=3)
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ ---
74
+
75
+ ## 🎮 Core Features
76
+
77
+ ### Mouse Control
78
+
79
+ ```python
80
+ # Button actions
81
+ await makcu.click(MouseButton.LEFT)
82
+ await makcu.double_click(MouseButton.RIGHT)
83
+ await makcu.press(MouseButton.MIDDLE)
84
+ await makcu.release(MouseButton.MIDDLE)
85
+
86
+ # Movement
87
+ await makcu.move(100, 50) # Relative movement
88
+ await makcu.move_smooth(200, 100, segments=20) # Smooth interpolation
89
+ await makcu.move_bezier(150, 150, segments=30, ctrl_x=75, ctrl_y=200) # Bezier curve
90
+
91
+ # Scrolling
92
+ await makcu.scroll(-5) # Scroll down
93
+ await makcu.scroll(3) # Scroll up
94
+
95
+ # Dragging
96
+ await makcu.drag(0, 0, 300, 200, button=MouseButton.LEFT, duration=1.5)
97
+ ```
98
+
99
+ ### Button & Axis Locking
100
+
101
+ ```python
102
+ # New unified locking API
103
+ await makcu.lock(MouseButton.LEFT) # Lock left button
104
+ await makcu.unlock(MouseButton.RIGHT) # Unlock right button
105
+ await makcu.lock("X") # Lock X-axis movement
106
+ await makcu.unlock("Y") # Unlock Y-axis movement
107
+
108
+ # Query lock states (no delays!)
109
+ is_locked = await makcu.is_locked(MouseButton.LEFT)
110
+ all_states = await makcu.get_all_lock_states()
111
+ # Returns: {"LEFT": True, "RIGHT": False, "X": True, ...}
112
+ ```
113
+
114
+ ### Human-like Interactions
115
+
116
+ ```python
117
+ # Realistic clicking with timing variations
118
+ await makcu.click_human_like(
119
+ button=MouseButton.LEFT,
120
+ count=5,
121
+ profile="gaming", # "fast", "normal", "slow", "variable", "gaming"
122
+ jitter=5 # Random mouse movement between clicks
123
+ )
124
+ ```
125
+
126
+ ### Button Event Monitoring
127
+
128
+ ```python
129
+ # Real-time button monitoring
130
+ def on_button_event(button: MouseButton, pressed: bool):
131
+ print(f"{button.name} {'pressed' if pressed else 'released'}")
132
+
133
+ makcu.set_button_callback(on_button_event)
134
+ await makcu.enable_button_monitoring(True)
135
+
136
+ # Check current button states
137
+ states = makcu.get_button_states()
138
+ if makcu.is_pressed(MouseButton.RIGHT):
139
+ print("Right button is pressed")
140
+ ```
141
+
142
+ ### Connection Management
143
+
144
+ ```python
145
+ # Auto-reconnection on disconnect
146
+ makcu = await create_async_controller(auto_reconnect=True)
147
+
148
+ # Connection status callbacks
149
+ @makcu.on_connection_change
150
+ async def handle_connection(connected: bool):
151
+ if connected:
152
+ print("Device reconnected!")
153
+ else:
154
+ print("Device disconnected!")
155
+
156
+ # Manual reconnection
157
+ if not makcu.is_connected():
158
+ await makcu.connect()
159
+ ```
160
+
161
+ ---
162
+
163
+ ## 🔧 Advanced Features
164
+
165
+ ### Batch Operations
166
+
167
+ ```python
168
+ # Execute multiple commands efficiently
169
+ async def combo_action():
170
+ await makcu.batch_execute([
171
+ lambda: makcu.move(50, 0),
172
+ lambda: makcu.click(MouseButton.LEFT),
173
+ lambda: makcu.move(-50, 0),
174
+ lambda: makcu.click(MouseButton.RIGHT)
175
+ ])
176
+ ```
177
+
178
+ ### Device Information
179
+
180
+ ```python
181
+ # Get device details
182
+ info = await makcu.get_device_info()
183
+ # {'port': 'COM3', 'vid': '0x1a86', 'pid': '0x55d3', ...}
184
+
185
+ # Firmware version
186
+ version = await makcu.get_firmware_version()
187
+ ```
188
+
189
+ ### Serial Spoofing
190
+
191
+ ```python
192
+ # Spoof device serial
193
+ await makcu.spoof_serial("CUSTOM123456")
194
+
195
+ # Reset to default
196
+ await makcu.reset_serial()
197
+ ```
198
+
199
+ ### Low-Level Access
200
+
201
+ ```python
202
+ # Send raw commands with tracked responses
203
+ response = await makcu.transport.async_send_command(
204
+ "km.version()",
205
+ expect_response=True,
206
+ timeout=0.1 # Optimized for gaming
207
+ )
208
+ ```
209
+
210
+ ---
211
+
212
+ ## 🧪 Command-Line Tools
213
+
214
+ ```bash
215
+ # Interactive debug console
216
+ python -m makcu --debug
217
+
218
+ # Test specific port
219
+ python -m makcu --testPort COM3
220
+
221
+ # Run automated tests
222
+ python -m makcu --runtest
223
+ ```
224
+
225
+ ### Tool Descriptions
226
+
227
+ - `--debug`: Launches an interactive console where you can type raw device commands and see live responses.
228
+ - `--testPort COMx`: Attempts to connect to the given COM port and reports success or failure.
229
+ - `--runtest`: Runs `test_suite.py` using `pytest` and opens a detailed HTML test report.
230
+
231
+ ---
232
+
233
+ ### Test Suite
234
+
235
+ - File: `test_suite.py`
236
+ - Run with: `python -m makcu --runtest`
237
+ - Output: `latest_pytest.html`
238
+
239
+ Includes tests for:
240
+ - Port connection
241
+ - Firmware version check
242
+ - Mouse movement and button control
243
+ - Button masking and locking
244
+
245
+ ---
246
+
247
+ ## Test Timings (v1.3 vs v1.4 vs v2.0)
248
+
249
+ | Test Name | v1.3 | v1.4 | v2.0 | Improvement (v1.3 → v2.0) |
250
+ |--------------------------|--------|-------|-------|----------------------------|
251
+ | connect_to_port | ~100ms | ~55ms | **46ms** | ~2.2x faster |
252
+ | press_and_release | ~18ms | ~9ms | **1ms** | ~18x faster |
253
+ | firmware_version | ~20ms | ~9ms | **1ms** | ~20x faster |
254
+ | middle_click | ~18ms | ~9ms | **1ms** | ~18x faster |
255
+ | device_info | ~25ms | ~13ms | **6ms** | ~4.1x faster |
256
+ | port_connection | ~20ms | ~9ms | **1ms** | ~20x faster |
257
+ | button_mask | ~17ms | ~8ms | **1ms** | ~17x faster |
258
+ | get_button_states | ~18ms | ~9ms | **1ms** | ~18x faster |
259
+ | lock_state | ~33ms | ~10ms | **1ms** | ~33x faster |
260
+ | makcu_behavior | ~20ms | ~10ms | **1ms** | ~20x faster |
261
+ | batch_commands | ~350ms | ~90ms | **3ms** | ~117x faster |
262
+ | rapid_moves | ~17ms | ~8ms | **2ms** | ~8.5x faster |
263
+ | button_performance | ~18ms | ~9ms | **2ms** | ~9x faster |
264
+ | mixed_operations | ~22ms | ~10ms | **2ms** | ~11x faster |
265
+
266
+ Based on the measured test suite, v2.0 is on average **~17× faster** than v1.3 across all core operations.
267
+
268
+
269
+ ### Gaming Performance Targets (v2.0)
270
+
271
+ - **144Hz Gaming**: 7ms frame time — ✅ Easily met (avg 1–3ms per operation)
272
+ - **240Hz Gaming**: 4.2ms frame time — ✅ Consistently met (most ops ≤ 2ms)
273
+ - **360Hz Gaming**: 2.8ms frame time — ⚡ Achievable for atomic/single ops
274
+
275
+ ---
276
+
277
+ ## 🏎️ Performance Optimization Details
278
+
279
+ ### Version History & Performance
280
+
281
+ - **v1.3 and earlier**: Original implementation with sleep delays
282
+ - **v1.4**: Initial optimizations, removed some sleep delays
283
+ - **v2.0**: Complete rewrite with zero-delay architecture
284
+
285
+ ### Key Optimizations in v2.0
286
+
287
+ 1. **Pre-computed Commands**: All commands are pre-formatted at initialization
288
+ 2. **Bitwise Operations**: Button states use single integer with bit manipulation
289
+ 3. **Zero-Copy Buffers**: Pre-allocated buffers for parsing
290
+ 4. **Reduced Timeouts**: Gaming-optimized timeouts (100ms default)
291
+ 5. **Cache Everything**: Connection states, lock states, and device info cached
292
+ 6. **Minimal Allocations**: Reuse objects and avoid string formatting
293
+ 7. **Fast Serial Settings**: 1ms read timeout, 10ms write timeout
294
+ 8. **Optimized Listener**: Batch processing with minimal overhead
295
+
296
+ ### Tips for Maximum Performance
297
+
298
+ ```python
299
+ # Disable debug mode in production
300
+ makcu = create_controller(debug=False)
301
+
302
+ # Use cached connection checks
303
+ if makcu.is_connected(): # Cached, no serial check
304
+ makcu.click(MouseButton.LEFT)
305
+
306
+ # Batch similar operations
307
+ with makcu: # Context manager ensures connection
308
+ for _ in range(10):
309
+ makcu.move(10, 0) # No connection check per call
310
+ ```
311
+
312
+ ---
313
+
314
+ ## 🔍 Debugging
315
+
316
+ Enable debug mode for detailed logging:
317
+
318
+ ```python
319
+ makcu = await create_async_controller(debug=True)
320
+
321
+ # View command flow (optimized timestamps)
322
+ # [123.456] [INFO] Sent command #42: km.move(100,50)
323
+ # [123.458] [DEBUG] Command #42 completed in 0.002s
324
+ ```
325
+
326
+ ---
327
+
328
+ ## 🏗️ Migration from v1.x
329
+
330
+ Most code works without changes! Key differences:
331
+
332
+ ```python
333
+ # v1.x (still works)
334
+ makcu = create_controller()
335
+ makcu.move(100, 100)
336
+
337
+ # v2.0 (async)
338
+ makcu = await create_async_controller()
339
+ await makcu.move(100, 100)
340
+
341
+ # v2.0 context manager (auto cleanup)
342
+ async with await create_async_controller() as makcu:
343
+ await makcu.click(MouseButton.LEFT)
344
+ ```
345
+
346
+ ---
347
+
348
+ ## 📚 API Reference
349
+
350
+ ### Enumerations
351
+
352
+ ```python
353
+ from makcu import MouseButton
354
+
355
+ MouseButton.LEFT # Left mouse button
356
+ MouseButton.RIGHT # Right mouse button
357
+ MouseButton.MIDDLE # Middle mouse button
358
+ MouseButton.MOUSE4 # Side button 1
359
+ MouseButton.MOUSE5 # Side button 2
360
+ ```
361
+
362
+ ### Exception Handling
363
+
364
+ ```python
365
+ from makcu import MakcuError, MakcuConnectionError, MakcuTimeoutError
366
+
367
+ try:
368
+ makcu = await create_async_controller()
369
+ except MakcuConnectionError as e:
370
+ print(f"Connection failed: {e}")
371
+ except MakcuTimeoutError as e:
372
+ print(f"Command timed out: {e}")
373
+ ```
374
+
375
+ ---
376
+
377
+ ## 🛠️ Technical Details
378
+
379
+ - **Protocol**: CH343 USB serial at 4Mbps
380
+ - **Command Format**: ASCII with optional ID tracking (`command#ID`)
381
+ - **Response Format**: `>>> #ID:response` for tracked commands
382
+ - **Threading**: High-priority listener thread with async bridge
383
+ - **Auto-Discovery**: VID:PID=1A86:55D3 detection
384
+ - **Buffer Size**: 4KB read buffer, 256B line buffer
385
+ - **Cleanup Interval**: 50ms for timed-out commands
386
+
387
+ ---
388
+
389
+ ## 📜 License
390
+
391
+ GPL License © SleepyTotem
392
+
393
+ ---
394
+
395
+ ## 🙋 Support
396
+
397
+ - **Issues**: [GitHub Issues](https://github.com/SleepyTotem/makcu-py-lib/issues)
398
+
399
+ ---
400
+
401
+ ## 🌐 Links
402
+
403
+ - [GitHub Repository](https://github.com/SleepyTotem/makcu-py-lib)
404
+ - [PyPI Package](https://pypi.org/project/makcu/)
405
+ - [Documentation](https://makcu-py-lib.readthedocs.io/)
406
+ - [Changelog](https://makcu-py-lib.readthedocs.io/en/latest/changelog.html)
makcu/__init__.py CHANGED
@@ -1,22 +1,22 @@
1
- from .controller import (
2
- MakcuController,
3
- create_controller,
4
- create_async_controller,
5
- maybe_async
6
- )
7
- from .enums import MouseButton
8
- from .errors import MakcuConnectionError
9
-
10
- # Version info
11
- __version__ = "2.2.0"
12
- __author__ = "SleepyTotem"
13
-
14
- # Main exports
15
- __all__ = [
16
- 'MakcuController',
17
- 'MouseButton',
18
- 'MakcuConnectionError',
19
- 'create_controller',
20
- 'create_async_controller',
21
- 'maybe_async'
1
+ from .controller import (
2
+ MakcuController,
3
+ create_controller,
4
+ create_async_controller,
5
+ maybe_async
6
+ )
7
+ from .enums import MouseButton
8
+ from .errors import MakcuConnectionError
9
+
10
+ # Version info
11
+ __version__ = "2.2.1"
12
+ __author__ = "SleepyTotem"
13
+
14
+ # Main exports
15
+ __all__ = [
16
+ 'MakcuController',
17
+ 'MouseButton',
18
+ 'MakcuConnectionError',
19
+ 'create_controller',
20
+ 'create_async_controller',
21
+ 'maybe_async'
22
22
  ]
makcu/__main__.py CHANGED
@@ -91,42 +91,48 @@ def find_writable_directory() -> Path:
91
91
  def parse_html_results(html_file: Path) -> Tuple[List[Tuple[str, str, int]], int]:
92
92
  if not html_file.exists():
93
93
  raise FileNotFoundError(f"HTML report not found: {html_file}")
94
-
94
+
95
95
  with open(html_file, 'r', encoding='utf-8') as f:
96
96
  content = f.read()
97
-
97
+
98
98
  match = re.search(r'data-jsonblob="([^"]*)"', content)
99
99
  if not match:
100
100
  raise ValueError("Could not find JSON data in HTML report")
101
-
101
+
102
102
  json_str = match.group(1)
103
103
  json_str = json_str.replace('"', '"').replace(''', "'").replace('&', '&')
104
-
104
+
105
105
  try:
106
106
  data = json.loads(json_str)
107
107
  except json.JSONDecodeError as e:
108
108
  raise ValueError(f"Failed to parse JSON data: {e}")
109
-
109
+
110
110
  test_results = []
111
111
  total_ms = 0
112
-
112
+
113
113
  skip_tests = {'test_connect_to_port'}
114
-
114
+
115
115
  for test_id, test_data_list in data.get('tests', {}).items():
116
116
  test_name = test_id.split('::')[-1]
117
+
118
+ # Skip tests that are in skip_tests
117
119
  if test_name in skip_tests:
118
120
  continue
119
-
121
+
120
122
  for test_data in test_data_list:
121
123
  status = test_data.get('result', 'UNKNOWN')
122
124
  duration_str = test_data.get('duration', '0 ms')
123
-
125
+
124
126
  duration_match = re.search(r'(\d+)\s*ms', duration_str)
125
127
  duration_ms = int(duration_match.group(1)) if duration_match else 0
126
- total_ms += duration_ms
127
128
 
129
+ # Always add test to results
128
130
  test_results.append((test_name, status, duration_ms))
129
-
131
+
132
+ # Only add time to total if it's not a cleanup test
133
+ if 'cleanup' not in test_name.lower():
134
+ total_ms += duration_ms
135
+
130
136
  return test_results, total_ms
131
137
 
132
138
  def run_tests() -> NoReturn:
@@ -244,8 +250,12 @@ def run_tests() -> NoReturn:
244
250
  display_name = test_name.replace("test_", "").replace("_", " ").title()
245
251
 
246
252
  if status.upper() == "PASSED":
247
- status_text = "[green]✅ PASSED[/green]"
248
- passed += 1
253
+ if display_name.lower().startswith("cleanup"):
254
+ status_text = ""
255
+ passed += 1
256
+ else:
257
+ status_text = "[green]✅ PASSED[/green]"
258
+ passed += 1
249
259
  elif status.upper() == "FAILED":
250
260
  status_text = "[red]❌ FAILED[/red]"
251
261
  failed += 1
@@ -263,7 +273,10 @@ def run_tests() -> NoReturn:
263
273
  elif duration_ms <= 10:
264
274
  perf = "[yellow]Good[/yellow]"
265
275
  elif duration_ms > 0:
266
- perf = "[red]🐌 Needs work[/red]"
276
+ if display_name.lower().startswith("cleanup"):
277
+ perf = ""
278
+ else:
279
+ perf = "[red]🐌 Needs work[/red]"
267
280
  else:
268
281
  perf = "-"
269
282
 
makcu/conftest.py CHANGED
@@ -3,32 +3,6 @@ import time
3
3
  from makcu import MakcuController, MouseButton
4
4
 
5
5
  @pytest.fixture(scope="session")
6
- def makcu(request):
6
+ def makcu():
7
7
  ctrl = MakcuController(fallback_com_port="COM1", debug=False)
8
-
9
- def cleanup():
10
- if ctrl.is_connected():
11
-
12
- time.sleep(0.1)
13
-
14
- ctrl.lock_left(False)
15
- ctrl.lock_right(False)
16
- ctrl.lock_middle(False)
17
- ctrl.lock_side1(False)
18
- ctrl.lock_side2(False)
19
- ctrl.lock_x(False)
20
- ctrl.lock_y(False)
21
-
22
- ctrl.release(MouseButton.LEFT)
23
- ctrl.release(MouseButton.RIGHT)
24
- ctrl.release(MouseButton.MIDDLE)
25
- ctrl.release(MouseButton.MOUSE4)
26
- ctrl.release(MouseButton.MOUSE5)
27
-
28
- ctrl.enable_button_monitoring(False)
29
-
30
- ctrl.disconnect()
31
-
32
- request.addfinalizer(cleanup)
33
-
34
8
  return ctrl
makcu/connection.py CHANGED
@@ -449,7 +449,7 @@ class SerialTransport:
449
449
  self.serial = None
450
450
  self._log("Disconnection completed")
451
451
 
452
- def send_command(self, command: str, expect_response: bool = True,
452
+ def send_command(self, command: str, expect_response: bool = False,
453
453
  timeout: float = DEFAULT_TIMEOUT) -> Optional[str]:
454
454
  command_start = time.time()
455
455
 
@@ -457,11 +457,10 @@ class SerialTransport:
457
457
  raise MakcuConnectionError("Not connected")
458
458
 
459
459
  if not expect_response:
460
- cmd_bytes = f"{command}\r\n".encode('ascii')
461
- self.serial.write(cmd_bytes)
460
+ self.serial.write(f"{command}\r\n".encode('ascii'))
462
461
  self.serial.flush()
463
462
  send_time = time.time() - command_start
464
- self._log(f"Command '{command}' sent in {send_time:.3f}s (no response expected)")
463
+ self._log(f"Command '{command}' sent in {send_time:.5f}s (no response expected)")
465
464
  return command
466
465
 
467
466
  cmd_id = self._generate_command_id()
@@ -488,7 +487,7 @@ class SerialTransport:
488
487
 
489
488
  response = result.split('#')[0] if '#' in result else result
490
489
  total_time = time.time() - command_start
491
- self._log(f"Command '{command}' completed in {total_time:.3f}s total")
490
+ self._log(f"Command '{command}' completed in {total_time:.5f}s total")
492
491
  return response
493
492
 
494
493
  except TimeoutError:
makcu/py.typed CHANGED
@@ -1,2 +1,2 @@
1
- # This file is intentionally empty.
1
+ # This file is intentionally empty.
2
2
  # It serves as a marker to indicate that this package supports type hints.
makcu/test_suite.py CHANGED
@@ -137,6 +137,23 @@ def test_mixed_operations(makcu):
137
137
  assert elapsed_ms < 15
138
138
 
139
139
 
140
- @pytest.mark.skip(reason="Capture test disabled until firmware supports tracking clicks from software input")
141
- def test_capture_right_clicks(makcu):
142
- pass
140
+ def test_cleanup(makcu):
141
+ time.sleep(0.1)
142
+
143
+ makcu.lock_left(False)
144
+ makcu.lock_right(False)
145
+ makcu.lock_middle(False)
146
+ makcu.lock_side1(False)
147
+ makcu.lock_side2(False)
148
+ makcu.lock_x(False)
149
+ makcu.lock_y(False)
150
+
151
+ makcu.release(MouseButton.LEFT)
152
+ makcu.release(MouseButton.RIGHT)
153
+ makcu.release(MouseButton.MIDDLE)
154
+ makcu.release(MouseButton.MOUSE4)
155
+ makcu.release(MouseButton.MOUSE5)
156
+
157
+ makcu.enable_button_monitoring(False)
158
+ makcu.disconnect()
159
+ assert not makcu.is_connected(), "Failed to disconnect from the makcu"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makcu
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: Python library for Makcu hardware device control
5
5
  Author: SleepyTotem
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1112,4 +1112,5 @@ GPL License © SleepyTotem
1112
1112
 
1113
1113
  - [GitHub Repository](https://github.com/SleepyTotem/makcu-py-lib)
1114
1114
  - [PyPI Package](https://pypi.org/project/makcu/)
1115
- - [Documentation](https://makcu.readthedocs.io/) *(coming soon)*
1115
+ - [Documentation](https://makcu-py-lib.readthedocs.io/)
1116
+ - [Changelog](https://makcu-py-lib.readthedocs.io/en/latest/changelog.html)
@@ -0,0 +1,17 @@
1
+ makcu/README.md,sha256=izPeCilhDbOpZwJ3sz9ZVbaFYkWFMsjYq87bAkV34EU,10788
2
+ makcu/__init__.py,sha256=lbWpmF383K2bi1-IFpaLA0G12ITD8NjQVq8t3oIzTI0,448
3
+ makcu/__main__.py,sha256=duRmMpsNqCKZQfQ-Wj57tIUkZ6hbxLV8MBxWnhyKarY,14527
4
+ makcu/conftest.py,sha256=EoX2T6dnUvWVI_VvJ0KTes6W82KR4Z69kMQVNtsxV7I,201
5
+ makcu/connection.py,sha256=1WQRoFuoXq76JIMC4aERxE8NdSevnlpQXkdj1T-w9Y4,20146
6
+ makcu/controller.py,sha256=EBtiScaOU0CV7NmwVdR3CwFpbT5RymdrNveRKo5ehFU,13242
7
+ makcu/enums.py,sha256=1SvIv5IoMIfXyoMAq8I5r_aKMVKjYR-L_hXkFLJ_5mw,119
8
+ makcu/errors.py,sha256=hXdeUCHvbzfqWX3uotG12Xv8EPwWs-5aHQ12xpgwx3Y,229
9
+ makcu/makcu.pyi,sha256=UfbX7774u_a9oyIa1rweWqIRoQxVAjhhHUuukDp3jiA,237
10
+ makcu/mouse.py,sha256=YZQDA5OGQlCxCDu3Huzwp5bjH__9yb867iLSVTYOKP4,8157
11
+ makcu/py.typed,sha256=ks2XNwsNldow4qVp9kuCuGBuHTmngmrzgvRnkzWIGMY,110
12
+ makcu/test_suite.py,sha256=mfzr7L63Eim1z4-l7WHtWs7szGr6bNk2U13vXVlWeM4,4295
13
+ makcu-2.2.1.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
14
+ makcu-2.2.1.dist-info/METADATA,sha256=zHxgcXW_fTXsW_BeLMNlGcmX1ORn95-VoOgbstikOe0,53904
15
+ makcu-2.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ makcu-2.2.1.dist-info/top_level.txt,sha256=IRO1UVb5LK_ovjau0g4oObyXQqy00tVEE-yF5lPgw1w,6
17
+ makcu-2.2.1.dist-info/RECORD,,
@@ -1,17 +0,0 @@
1
- makcu/README.md,sha256=FrZkK3T6m42s1D61WN8f7rVMWMbr9nPwMgQaXcYWKzQ,11125
2
- makcu/__init__.py,sha256=rfBNEd2V5vwfX4dHQzEowF3z0T6CoObYSsKculIgCnI,427
3
- makcu/__main__.py,sha256=WYf8QKcgMNHh0GP7szXZdnoeExT55Aokkflj-u1IaJc,14030
4
- makcu/conftest.py,sha256=BqPx6ywYONYGyKH11Bam9aV5zSGXR6odE5Z8hGFrn4U,863
5
- makcu/connection.py,sha256=LkvzPwT1Er9JblGR8pZRMLTTCBBd6a1F62X_FkLaXEg,20179
6
- makcu/controller.py,sha256=EBtiScaOU0CV7NmwVdR3CwFpbT5RymdrNveRKo5ehFU,13242
7
- makcu/enums.py,sha256=1SvIv5IoMIfXyoMAq8I5r_aKMVKjYR-L_hXkFLJ_5mw,119
8
- makcu/errors.py,sha256=hXdeUCHvbzfqWX3uotG12Xv8EPwWs-5aHQ12xpgwx3Y,229
9
- makcu/makcu.pyi,sha256=UfbX7774u_a9oyIa1rweWqIRoQxVAjhhHUuukDp3jiA,237
10
- makcu/mouse.py,sha256=YZQDA5OGQlCxCDu3Huzwp5bjH__9yb867iLSVTYOKP4,8157
11
- makcu/py.typed,sha256=lI_IPBO6A6a5eY5kRQDNsdSydUb3sFWtcC_ML8FNftU,111
12
- makcu/test_suite.py,sha256=VkEsloTX8ozprwDhWVJ5GFS5qeIxJxXz47xCcZKNIVY,3889
13
- makcu-2.2.0.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
14
- makcu-2.2.0.dist-info/METADATA,sha256=_6_IPbDGcLgm5Xb0ES6dB2dpMDb_t99Ju7WvXqidYk0,53836
15
- makcu-2.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- makcu-2.2.0.dist-info/top_level.txt,sha256=IRO1UVb5LK_ovjau0g4oObyXQqy00tVEE-yF5lPgw1w,6
17
- makcu-2.2.0.dist-info/RECORD,,
File without changes