makcu 2.2.1__py3-none-any.whl → 2.2.2__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,4 +1,4 @@
1
- # 🖱️ Makcu Python Library v2.2.0
1
+ # 🖱️ Makcu Python Library v2.2.2
2
2
 
3
3
  [![PyPI Version](https://img.shields.io/pypi/v/makcu.svg)](https://pypi.org/project/makcu/)
4
4
  [![Python Support](https://img.shields.io/pypi/pyversions/makcu.svg)](https://pypi.org/project/makcu/)
makcu/connection.py CHANGED
@@ -161,31 +161,33 @@ class SerialTransport:
161
161
  content = line.decode('ascii', 'ignore').strip()
162
162
  return ParsedResponse(None, content, False)
163
163
 
164
+
164
165
  def _handle_button_data(self, byte_val: int) -> None:
165
166
  if byte_val == self._last_button_mask:
166
167
  return
167
-
168
+
168
169
  changed_bits = byte_val ^ self._last_button_mask
170
+ print("\n", end='')
169
171
  self._log(f"Button state changed: 0x{self._last_button_mask:02X} -> 0x{byte_val:02X}")
170
172
 
171
- for bit in range(5):
173
+ for bit in range(8):
172
174
  if changed_bits & (1 << bit):
173
175
  is_pressed = bool(byte_val & (1 << bit))
174
176
  button_name = self.BUTTON_MAP[bit] if bit < len(self.BUTTON_MAP) else f"bit{bit}"
175
-
177
+
176
178
  self._log(f"Button {button_name}: {'PRESSED' if is_pressed else 'RELEASED'}")
177
-
179
+ print(">>> ", end='', flush=True)
178
180
  if is_pressed:
179
181
  self._button_states |= (1 << bit)
180
182
  else:
181
183
  self._button_states &= ~(1 << bit)
182
-
184
+
183
185
  if self._button_callback and bit < len(self.BUTTON_ENUM_MAP):
184
186
  try:
185
187
  self._button_callback(self.BUTTON_ENUM_MAP[bit], is_pressed)
186
188
  except Exception as e:
187
189
  self._log(f"Button callback failed: {e}", "ERROR")
188
-
190
+
189
191
  self._last_button_mask = byte_val
190
192
 
191
193
  def _process_pending_commands(self, content: str) -> None:
@@ -234,54 +236,114 @@ class SerialTransport:
234
236
 
235
237
  def _listen(self) -> None:
236
238
  self._log("Starting listener thread")
237
-
238
239
  read_buffer = bytearray(4096)
239
240
  line_buffer = bytearray(256)
240
241
  line_pos = 0
241
-
242
242
 
243
243
  serial_read = self.serial.read
244
244
  serial_in_waiting = lambda: self.serial.in_waiting
245
245
  is_connected = lambda: self._is_connected
246
246
  stop_requested = self._stop_event.is_set
247
-
248
247
 
249
248
  last_cleanup = time.time()
250
249
  cleanup_interval = 0.05
251
250
 
251
+ expecting_text_mode = False
252
+ last_byte = None
253
+
252
254
  while is_connected() and not stop_requested():
253
255
  try:
254
256
  bytes_available = serial_in_waiting()
255
257
  if not bytes_available:
256
258
  time.sleep(0.001)
257
259
  continue
258
-
260
+
259
261
  bytes_read = serial_read(min(bytes_available, 4096))
260
-
261
262
  for byte_val in bytes_read:
262
- if byte_val < 32 and byte_val not in (0x0D, 0x0A):
263
- # Button data
264
- self._handle_button_data(byte_val)
265
- else:
266
- if byte_val == 0x0A: # LF
267
- if line_pos > 0:
263
+
264
+ if last_byte == 0x0D and byte_val == 0x0A:
265
+ if line_pos > 0:
266
+ line = bytes(line_buffer[:line_pos])
267
+ line_pos = 0
268
+ if line:
269
+ response = self._parse_response_line(line)
270
+ if response.content:
271
+ self._process_pending_commands(response.content)
272
+ expecting_text_mode = False
273
+
274
+ elif byte_val >= 32 or byte_val in (0x09,):
275
+ expecting_text_mode = True
276
+ if line_pos < 256:
277
+ line_buffer[line_pos] = byte_val
278
+ line_pos += 1
279
+
280
+ elif byte_val == 0x0D:
281
+ if expecting_text_mode or line_pos > 0:
282
+ expecting_text_mode = True
283
+ else:
284
+ pass
285
+
286
+ elif byte_val == 0x0A:
287
+ last_byte_str = f"0x{last_byte:02X}" if last_byte is not None else "None"
288
+ # self._log(f"LF detected: last_byte={last_byte_str}, expecting_text={expecting_text_mode}, line_pos={line_pos}", "DEBUG")
289
+
290
+ button_combination_detected = False
291
+
292
+ if (self._last_button_mask != 0 or
293
+ (last_byte is not None and last_byte < 32 and last_byte != 0x0D) or
294
+ (line_pos > 0 and not expecting_text_mode)):
295
+
296
+ # self._log("LF: Detected as button combination (right + mouse4 = 0x0A)", "DEBUG")
297
+ self._handle_button_data(byte_val)
298
+ expecting_text_mode = False
299
+ button_combination_detected = True
300
+ line_pos = 0
301
+
302
+ if not button_combination_detected:
303
+ if last_byte == 0x0D:
304
+ self._log("LF: Completing CRLF sequence", "DEBUG")
305
+ if line_pos > 0:
306
+ line = bytes(line_buffer[:line_pos])
307
+ line_pos = 0
308
+ if line:
309
+ response = self._parse_response_line(line)
310
+ if response.content:
311
+ self._process_pending_commands(response.content)
312
+ expecting_text_mode = False
313
+ elif line_pos > 0 and expecting_text_mode:
314
+ self._log("LF: Ending line with text in buffer", "DEBUG")
268
315
  line = bytes(line_buffer[:line_pos])
269
316
  line_pos = 0
270
-
271
317
  if line:
272
318
  response = self._parse_response_line(line)
273
319
  if response.content:
274
320
  self._process_pending_commands(response.content)
275
- elif byte_val != 0x0D: # Not CR
276
- if line_pos < 256:
277
- line_buffer[line_pos] = byte_val
278
- line_pos += 1
279
-
321
+ expecting_text_mode = False
322
+ elif expecting_text_mode:
323
+ self._log("LF: Empty line in text mode", "DEBUG")
324
+ expecting_text_mode = False
325
+ else:
326
+ self._log("LF: Treating as button data (no text context)", "DEBUG")
327
+ self._handle_button_data(byte_val)
328
+ expecting_text_mode = False
329
+ line_pos = 0 # Clear any accumulated data
330
+
331
+ elif byte_val < 32:
332
+ if last_byte == 0x0D:
333
+ self._log(f"Processing delayed CR as button data: 0x0D", "DEBUG")
334
+ self._handle_button_data(0x0D)
335
+
336
+ self._handle_button_data(byte_val)
337
+ expecting_text_mode = False
338
+ line_pos = 0
339
+
340
+ last_byte = byte_val
341
+
280
342
  current_time = time.time()
281
343
  if current_time - last_cleanup > cleanup_interval:
282
344
  self._cleanup_timed_out_commands()
283
345
  last_cleanup = current_time
284
-
346
+
285
347
  except serial.SerialException as e:
286
348
  self._log(f"Serial exception in listener: {e}", "ERROR")
287
349
  if self.auto_reconnect:
@@ -290,7 +352,7 @@ class SerialTransport:
290
352
  break
291
353
  except Exception as e:
292
354
  self._log(f"Unexpected exception in listener: {e}", "ERROR")
293
-
355
+
294
356
  self._log("Listener thread ending")
295
357
 
296
358
  def _attempt_reconnect(self) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makcu
3
- Version: 2.2.1
3
+ Version: 2.2.2
4
4
  Summary: Python library for Makcu hardware device control
5
5
  Author: SleepyTotem
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -708,7 +708,7 @@ Requires-Dist: twine>=4.0
708
708
  Requires-Dist: rich>=14.0
709
709
  Dynamic: license-file
710
710
 
711
- # 🖱️ Makcu Python Library v2.2.0
711
+ # 🖱️ Makcu Python Library v2.2.2
712
712
 
713
713
  [![PyPI Version](https://img.shields.io/pypi/v/makcu.svg)](https://pypi.org/project/makcu/)
714
714
  [![Python Support](https://img.shields.io/pypi/pyversions/makcu.svg)](https://pypi.org/project/makcu/)
@@ -1,8 +1,8 @@
1
- makcu/README.md,sha256=izPeCilhDbOpZwJ3sz9ZVbaFYkWFMsjYq87bAkV34EU,10788
1
+ makcu/README.md,sha256=2sEl6BpxwLZpv0B5eDMG-1-6vrAXnUijl5im3NDVbck,10788
2
2
  makcu/__init__.py,sha256=lbWpmF383K2bi1-IFpaLA0G12ITD8NjQVq8t3oIzTI0,448
3
3
  makcu/__main__.py,sha256=duRmMpsNqCKZQfQ-Wj57tIUkZ6hbxLV8MBxWnhyKarY,14527
4
4
  makcu/conftest.py,sha256=EoX2T6dnUvWVI_VvJ0KTes6W82KR4Z69kMQVNtsxV7I,201
5
- makcu/connection.py,sha256=1WQRoFuoXq76JIMC4aERxE8NdSevnlpQXkdj1T-w9Y4,20146
5
+ makcu/connection.py,sha256=xvgFUPz9JBVHhatlVFtj6zhnfTamT7_o8M3msxOjVMs,23767
6
6
  makcu/controller.py,sha256=EBtiScaOU0CV7NmwVdR3CwFpbT5RymdrNveRKo5ehFU,13242
7
7
  makcu/enums.py,sha256=1SvIv5IoMIfXyoMAq8I5r_aKMVKjYR-L_hXkFLJ_5mw,119
8
8
  makcu/errors.py,sha256=hXdeUCHvbzfqWX3uotG12Xv8EPwWs-5aHQ12xpgwx3Y,229
@@ -10,8 +10,8 @@ makcu/makcu.pyi,sha256=UfbX7774u_a9oyIa1rweWqIRoQxVAjhhHUuukDp3jiA,237
10
10
  makcu/mouse.py,sha256=YZQDA5OGQlCxCDu3Huzwp5bjH__9yb867iLSVTYOKP4,8157
11
11
  makcu/py.typed,sha256=ks2XNwsNldow4qVp9kuCuGBuHTmngmrzgvRnkzWIGMY,110
12
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,,
13
+ makcu-2.2.2.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
14
+ makcu-2.2.2.dist-info/METADATA,sha256=a1xZlmx3dgZfhwidbupCWnks-oVpeRfUYj1k-WNl18U,53904
15
+ makcu-2.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ makcu-2.2.2.dist-info/top_level.txt,sha256=IRO1UVb5LK_ovjau0g4oObyXQqy00tVEE-yF5lPgw1w,6
17
+ makcu-2.2.2.dist-info/RECORD,,
File without changes