From e8fdf2ac3443c5f35f2f9ac996a3d7d503a8db99 Mon Sep 17 00:00:00 2001 From: Sam Bull Date: Thu, 2 Jan 2025 19:57:05 +0000 Subject: [PATCH] Dynamically update the SDP when new devices are connected (#100) --- .mypy.ini | 2 +- a1314_message_filter.py | 123 ---------------- hid_devices.py | 134 +++++++++++++++--- hid_message_filter.py | 14 -- install/on_rpi/on_pi_setup.sh | 3 +- mouse_g502_message_filter.py | 53 ------- mouse_message_filter.py | 76 ---------- mouse_mx510_message_filter.py | 76 ---------- sdp/keyboard_hid_sdp_record.xml | 114 --------------- sdp/sdp_record.xml | 120 ---------------- sdp/sdp_record_apple.xml | 120 ---------------- sdp/sdp_record_kbd_mouse.xml | 120 ---------------- sdp/sdp_record_main.xml | 120 ---------------- .../sdp_record.xml => sdp_record_template.xml | 2 +- 14 files changed, 117 insertions(+), 960 deletions(-) delete mode 100644 a1314_message_filter.py delete mode 100644 hid_message_filter.py delete mode 100644 mouse_g502_message_filter.py delete mode 100644 mouse_message_filter.py delete mode 100644 mouse_mx510_message_filter.py delete mode 100644 sdp/keyboard_hid_sdp_record.xml delete mode 100644 sdp/sdp_record.xml delete mode 100644 sdp/sdp_record_apple.xml delete mode 100644 sdp/sdp_record_kbd_mouse.xml delete mode 100644 sdp/sdp_record_main.xml rename install/on_rpi/sdp_record.xml => sdp_record_template.xml (86%) diff --git a/.mypy.ini b/.mypy.ini index 04a8a64..662304c 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -1,5 +1,5 @@ [mypy] -files = a1314_message_filter.py, adapter.py, agent.py, bluetooth_devices.py, compatibility_device.py, hid_devices.py, hid_message_filter.py, mouse_g502_message_filter.py, mouse_message_filter.py, mouse_mx510_message_filter.py +files = adapter.py, agent.py, bluetooth_devices.py, compatibility_device.py, hid_devices.py check_untyped_defs = True follow_imports_for_stubs = True disallow_any_decorated = True diff --git a/a1314_message_filter.py b/a1314_message_filter.py deleted file mode 100644 index 0e70691..0000000 --- a/a1314_message_filter.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (c) 2020 ruundii. All rights reserved. - -from typing import Optional - -from hid_message_filter import HIDMessageFilter - -MODIFIER_MASK_A1314_FN = 0x10 -MODIFIER_MASK_A1314_EJECT = 0x08 -MODIFIER_MASK_LEFT_CONTROL = 0x01 -MODIFIER_MASK_LEFT_SHIFT = 0x02 -MODIFIER_MASK_LEFT_ALT = 0x04 -MODIFIER_MASK_LEFT_GUI_OR_CMD = 0x08 -MODIFIER_MASK_A1314_RIGHT_CMD = 0x80 -MODIFIER_MASK_RIGHT_ALT = 0x40 -MODIFIER_MASK_RIGHT_SHIFT = 0x20 - -KEY_APPLICATION = 0x65 - -KEY_DELETE_FORWARD = 0x4c -KEY_LEFT_ARROW = 0x50 -KEY_RIGHT_ARROW = 0x4f -KEY_DOWN_ARROW = 0x51 -KEY_UP_ARROW = 0x52 -KEY_HOME = 0x4a -KEY_END = 0x4d -KEY_PGUP = 0x4b -KEY_PGDN = 0x4e -KEY_PRINT_SCREEN = 0x46 - -FN_SUBSTITUTES = { - KEY_LEFT_ARROW:KEY_HOME, - KEY_RIGHT_ARROW: KEY_END, - KEY_DOWN_ARROW: KEY_PGDN, - KEY_UP_ARROW: KEY_PGUP, -} - -class A1314MessageFilter(HIDMessageFilter): - #LeftControl: 0 | LeftShift: 0 | LeftAlt: 0 | Left GUI: 0 | RightControl: 0 | RightShift: 0 | RightAlt: 0 | Right GUI: 0 | # |Keyboard ['00', '00', '00', '00', '00', '00'] - - - def __init__(self) -> None: - self.is_fn_pressed = False - self.is_eject_pressed = False - self.last_regular_report = bytearray(b'\x01\x00\x00\x00\x00\x00\x00\x00\x00') - - def filter_message_to_host(self, msg: bytes) -> Optional[bytes]: - if len(msg) < 1: - return None - - result_report = bytearray(msg) - if msg[0] == 0x11: - result_report = self.last_regular_report - - #special key report - self.is_fn_pressed = (msg[1] & MODIFIER_MASK_A1314_FN) != 0 - - old_eject_state = self.is_eject_pressed - self.is_eject_pressed = (msg[1] & MODIFIER_MASK_A1314_EJECT) != 0 - if(old_eject_state and not self.is_eject_pressed): - #eject unpressed - remove delete forward key from report - for i in range(3,9): - if result_report[i] == KEY_DELETE_FORWARD: - for j in range (i+1,9): - result_report[j-1]=result_report[j] - result_report[8]=0 - break - if (not old_eject_state and self.is_eject_pressed): - # eject pressed - for i in range(3,9): - if result_report[i] == 0: - result_report[i] = KEY_DELETE_FORWARD - break - - elif msg[0] == 0x01: - #normal key report - modifiers = result_report[1] - result_report[1] = 0 # reset modifiers - - if(modifiers & MODIFIER_MASK_LEFT_ALT): # left alt is pressed - result_report[1] = result_report[1] | MODIFIER_MASK_LEFT_GUI_OR_CMD - - if(modifiers & MODIFIER_MASK_LEFT_GUI_OR_CMD): # left cmd is pressed - result_report[1] = result_report[1] | MODIFIER_MASK_LEFT_ALT - - if(modifiers & MODIFIER_MASK_A1314_RIGHT_CMD): # right cmd is pressed - result_report[1] = result_report[1] | MODIFIER_MASK_RIGHT_ALT - - if(modifiers & MODIFIER_MASK_LEFT_SHIFT): # left shift is pressed - result_report[1] = result_report[1] | MODIFIER_MASK_LEFT_SHIFT - - if(modifiers & MODIFIER_MASK_RIGHT_SHIFT): # right shift is pressed - result_report[1] = result_report[1] | MODIFIER_MASK_RIGHT_SHIFT - - if(modifiers & MODIFIER_MASK_RIGHT_ALT): # right alt is pressed - send application - for i in range(3,9): - if result_report[i] == 0: - result_report[i] = KEY_APPLICATION - break - - my_fn_pressed = modifiers & MODIFIER_MASK_LEFT_CONTROL # left control is pressed - behave like fn button - if my_fn_pressed: - #process combinations - for i in range(3,9): - if result_report[i] == 0: - break - if result_report[i] in FN_SUBSTITUTES: - result_report[i] = FN_SUBSTITUTES[result_report[i]] - - - # set the fn state in output report - result_report[1] = (result_report[1] | MODIFIER_MASK_LEFT_CONTROL) if self.is_fn_pressed else ( - result_report[1] & ~MODIFIER_MASK_LEFT_CONTROL) - - #print(bytes(result_report).hex()) - if result_report == b'\x01\x05\x00\x2b\x00\x00\x00\x00\x00': - print("host switch") - return b'\xff' - - self.last_regular_report = result_report - return b'\xa1'+bytes(result_report) - - def filter_message_from_host(self, msg: bytes) -> bytes: - return msg[1:] diff --git a/hid_devices.py b/hid_devices.py index 5e60f8b..7058735 100644 --- a/hid_devices.py +++ b/hid_devices.py @@ -2,23 +2,26 @@ from __future__ import annotations +import array import asyncio +import fcntl import os import json import re +import struct +import subprocess +import sys import time +from pathlib import Path from typing import Awaitable, Callable, Literal, Optional, TypedDict, cast import evdev from watchfiles import awatch -from a1314_message_filter import A1314MessageFilter from bluetooth_devices import BluetoothDeviceRegistry from compatibility_device import CompatibilityModeDevice -from hid_message_filter import HIDMessageFilter -from mouse_g502_message_filter import G502MessageFilter -from mouse_message_filter import MouseMessageFilter -from mouse_mx510_message_filter import MX510MessageFilter + +HIDMessageFilter = Callable[[bytes], Optional[bytes]] class __Device(TypedDict, total=False): @@ -44,33 +47,74 @@ class _InputDevice(TypedDict): class _HIDDevices(TypedDict): devices: list[_Device] - filters: list[dict[str, str]] + filters: tuple[dict[str, Optional[str]]] input_devices: list[_InputDevice] class _DeviceConfig(TypedDict, total=False): capture: bool + descriptor: str filter: str + mapped_ids: dict[Optional[int], int] DEVICES_CONFIG_FILE_NAME = 'devices_config.json' DEVICES_CONFIG_COMPATIBILITY_DEVICE_KEY = 'compatibility_devices' CAPTURE_ELEMENT: Literal['capture'] = 'capture' FILTER_ELEMENT: Literal['filter'] = 'filter' +REPORT_ID_PATTERN = re.compile(r"(a10185)(..)") +SDP_TEMPLATE_PATH = Path(__file__).with_name("sdp_record_template.xml") +SDP_OUTPUT_PATH = Path("/etc/bluetooth/sdp_record.xml") + +FILTERS = ({"id": None, "name": "No filter"},) +FILTER_INSTANCES: dict[str | None, HIDMessageFilter] = {None: lambda m: m} + + +# https://github.com/bentiss/hid-tools/blob/59a0c4b153dbf7d443e63bf68ff830b8353f5f7a/hidtools/hidraw.py#L33-L104 + +_IOC_READ = 2 +_IOC_NRBITS = 8 +_IOC_TYPEBITS = 8 +_IOC_SIZEBITS = 14 + +_IOC_NRSHIFT = 0 +_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS +_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS +_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS + +def _IORH(nr: int, size: int) -> int: + return ( + (_IOC_READ << _IOC_DIRSHIFT) + | (ord("H") << _IOC_TYPESHIFT) + | (nr << _IOC_NRSHIFT) + | (size << _IOC_SIZESHIFT) + ) + +def _IOC_HIDIOCGRDESCSIZE(length: int) -> int: + return _IORH(0x01, length) + +def _ioctl_desc_size(fd: int) -> tuple[int]: + size = struct.calcsize("i") + abs = fcntl.ioctl(fd, _IOC_HIDIOCGRDESCSIZE(size), size * b"\x00") + return cast(tuple[int], struct.unpack("i", abs)) + +def _IOC_HIDIOCGRDESC(length: int) -> int: + return _IORH(0x02, length) + +def _HIDIOCGRDESC(fd: int) -> "array.array[int]": + """Get report descriptor.""" + size = int(*_ioctl_desc_size(fd)) + + _buffer = array.array("B", struct.pack("i", size) + bytes(4096)) + fcntl.ioctl(fd, _IOC_HIDIOCGRDESC(struct.calcsize("I4096c")), _buffer) + (size,) = cast(tuple[int], struct.unpack("i", _buffer[:4])) + return _buffer[4 : size + 4] -FILTERS = [ - {"id":"Default", "name":"Default"}, - {"id":"Mouse", "name":"Mouse"}, - {"id":"A1314", "name":"A1314"}, - {"id":"G502", "name":"G502"}, - {"id":"MX510", "name":"MX510"} -] -FILTER_INSTANCES = { -"Default" : HIDMessageFilter(), "Mouse":MouseMessageFilter(), "A1314":A1314MessageFilter(), "G502":G502MessageFilter(), "MX510":MX510MessageFilter() -} class HIDDevice: + mapped_ids: dict[Optional[int], bytes] + def __init__(self, device: _Device, filter: HIDMessageFilter, loop: asyncio.AbstractEventLoop, device_registry: HIDDeviceRegistry): self.loop = loop @@ -89,6 +133,13 @@ def __init__(self, device: _Device, filter: HIDMessageFilter, self.hidraw_file: Optional[int] = os.open('/dev/'+self.hidraw, os.O_RDWR | os.O_NONBLOCK) loop.add_reader(self.hidraw_file, self.hidraw_event) print("HID Device ",self.device_id," created") + desc = "".join(f"{b:02x}" for b in _HIDIOCGRDESC(self.hidraw_file)) + # Replace report IDs, so they can be remapped later. + self.internal_ids = tuple(m[1] for m in cast(list[str], REPORT_ID_PATTERN.findall(desc))) + self.descriptor, found = REPORT_ID_PATTERN.subn(r"\1{}", desc) + # Or insert one if no report ID exists. + if found == 0: + self.descriptor = re.sub(r"(a101)", r"\g<1>85{}", self.descriptor, count=1) def set_device_filter(self, filter: HIDMessageFilter) -> None: self.filter = filter @@ -105,13 +156,15 @@ def hidraw_event(self) -> None: self.hidraw_file = None print("HID device ",self.device_id, " exception on read. closing") return - tm = self.filter.filter_message_to_host(msg) + tm = self.filter(msg) if tm is None or self.device_registry.bluetooth_devices is None: return if tm == b'\xff': self.device_registry.bluetooth_devices.switch_host() self.indicate_switch_with_mouse_movement() else: + # TODO: Test a device without report IDs. + tm = b"\xa1" + self.mapped_ids[tm[0]] + tm[1:] self.device_registry.bluetooth_devices.send_message(tm, True, False) def indicate_switch_with_mouse_movement(self) -> None: @@ -136,9 +189,8 @@ def move_mouse(self, xy: bytes) -> None: self.device_registry.bluetooth_devices.send_message(b'\xa1\x03\x00\x00\x00\x00' + xy, True, False) async def send_message(self, msg: bytes) -> None: - tm = self.filter.filter_message_from_host(msg) - if tm is not None and self.hidraw_file is not None: - os.write(self.hidraw_file, tm) + if self.hidraw_file is not None: + os.write(self.hidraw_file, msg[1:]) def __eq__(self, other: object) -> bool: if isinstance(other, HIDDevice): @@ -267,6 +319,46 @@ def _filter(d: evdev.InputDevice) -> bool: if dev_dict["instance"] not in self.capturing_devices and self.__is_configured_capturing_device(dev_dict["id"]) and dev_dict["instance"] not in devs_in_compatibility_mode: #create capturing device self.capturing_devices[dev_dict["instance"]] = HIDDevice(dev_dict, self.__get_configured_device_filter(dev_dict["id"]), self.loop, self) + + recreate_sdp = False + # Refresh or create config details for currently connected devices. + for hid_dev in self.capturing_devices.values(): + dev_config = self.devices_config.get(hid_dev.device_class) + if not dev_config: + dev_config = {} + self.devices_config[hid_dev.device_class] = dev_config + recreate_sdp = True + + dev_config["descriptor"] = hid_dev.descriptor + # TODO(PY311): Use to_bytes() defaults. + # Need tuple to retain order (set is unordered, but dict is ordered). + keys = tuple(int(i, base=16) for i in hid_dev.internal_ids) if hid_dev.internal_ids else (None,) + if dev_config.get("mapped_ids", {}).keys() != set(keys): + dev_config["mapped_ids"] = {i: 0 for i in keys} + recreate_sdp = True + + # We need to avoid editing the SDP when possible as this requires restarting + # bluez (therefore disconnecting all BT devices). + if recreate_sdp: + report_desc = "" + report_id = 1 + for dev_config in self.devices_config.values(): + for k in dev_config["mapped_ids"]: + dev_config["mapped_ids"][k] = report_id + report_id += 1 + report_desc += dev_config["descriptor"] + report_desc = report_desc.format(*(f"{i:02x}" for i in range(1, report_id))) + + sdp = SDP_TEMPLATE_PATH.read_text().format(report_desc) + SDP_OUTPUT_PATH.write_text(sdp) + self.__save_config() + # TODO: Try reconnecting devices after restart. + subprocess.Popen(("systemctl", "restart", "bluetooth"), stderr=sys.stderr) + + # Update the mapped IDs based on latest information. + for hid_dev in self.capturing_devices.values(): + config_ids = self.devices_config[hid_dev.device_class]["mapped_ids"] + hid_dev.mapped_ids = {k: v.to_bytes(1, "big") for k,v in config_ids.items()} self.devices = devs @@ -310,7 +402,7 @@ def __get_configured_device_filter(self, device_id: str) -> HIDMessageFilter: if FILTER_ELEMENT in self.devices_config[device_id]: filter_id = self.devices_config[device_id][FILTER_ELEMENT] return FILTER_INSTANCES[filter_id] - return FILTER_INSTANCES["Default"] + return FILTER_INSTANCES[None] def get_hid_devices_with_config(self) -> _HIDDevices: for device in self.devices: diff --git a/hid_message_filter.py b/hid_message_filter.py deleted file mode 100644 index f96fa66..0000000 --- a/hid_message_filter.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2020 ruundii. All rights reserved. - -from typing import Optional - -class HIDMessageFilter: - def filter_message_to_host(self, msg: bytes) -> Optional[bytes]: - if len(msg) == 8: - return b'\xa1\x01' + msg - - return b'\xa1' + msg - - - def filter_message_from_host(self, msg: bytes) -> Optional[bytes]: - return msg[1:] diff --git a/install/on_rpi/on_pi_setup.sh b/install/on_rpi/on_pi_setup.sh index 848245c..af4a9e3 100644 --- a/install/on_rpi/on_pi_setup.sh +++ b/install/on_rpi/on_pi_setup.sh @@ -36,7 +36,8 @@ sudo systemctl disable bluetooth sudo systemctl stop bluetooth sudo make install sudo python3 $HOME/bthidhub/install/on_rpi/config_replacer.py -sudo cp $HOME/bthidhub/install/on_rpi/sdp_record.xml /etc/bluetooth/sdp_record.xml +sudo cp $HOME/bthidhub/sdp_record_template.xml /etc/bluetooth/sdp_record.xml +sudo sed -i 's/{}//' /etc/bluetooth/sdp_record.xml sudo cp $HOME/bthidhub/install/on_rpi/input.conf /etc/bluetooth/input.conf sudo cp $HOME/bthidhub/install/on_rpi/main.conf /etc/bluetooth/main.conf diff --git a/mouse_g502_message_filter.py b/mouse_g502_message_filter.py deleted file mode 100644 index 6b5c584..0000000 --- a/mouse_g502_message_filter.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2020 ruundii. All rights reserved. - - -from hid_message_filter import HIDMessageFilter -from mouse_message_filter import MouseMessageFilter - - -# 0x67, 0x05, 0x01, 0x09, 0x02, // Unit (Mass: Gram, Luminous Intensity: Candela) -# 0xA1, 0x01, // Collection (Application) -# 0x09, 0x01, // Usage (0x01) -# 0xA1, 0x00, // Collection (Physical) -# 0x05, 0x09, // Usage Page (Button) -# 0x19, 0x01, // Usage Minimum (0x01) -# 0x29, 0x10, // Usage Maximum (0x10) -# 0x15, 0x00, // Logical Minimum (0) -# 0x25, 0x01, // Logical Maximum (1) -# 0x95, 0x10, // Report Count (16) -# 0x75, 0x01, // Report Size (1) -# 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) -# 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) -# 0x16, 0x01, 0x80, // Logical Minimum (-32767) -# 0x26, 0xFF, 0x7F, // Logical Maximum (32767) -# 0x75, 0x10, // Report Size (16) -# 0x95, 0x02, // Report Count (2) -# 0x09, 0x30, // Usage (X) -# 0x09, 0x31, // Usage (Y) -# 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) -# 0x15, 0x81, // Logical Minimum (-127) -# 0x25, 0x7F, // Logical Maximum (127) -# 0x75, 0x08, // Report Size (8) -# 0x95, 0x01, // Report Count (1) -# 0x09, 0x38, // Usage (Wheel) -# 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) -# 0x05, 0x0C, // Usage Page (Consumer) -# 0x0A, 0x38, 0x02, // Usage (AC Pan) -# 0x95, 0x01, // Report Count (1) -# 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) -# 0xC0, // End Collection -# 0xC0, // End Collection -# -# // 68 bytes - - -class G502MessageFilter(MouseMessageFilter): - # first 16 bits are flags for buttons. 01 is left button, 02 is right, 04 is scroll - # second 16 bits are X -32767 to 32767 - # third 16 bits are Y -32767 to 32767 - # then 8 bits of wheel -127 to 127 - # then AC pan - - def __init__(self) -> None: - self.message_size = 8 - diff --git a/mouse_message_filter.py b/mouse_message_filter.py deleted file mode 100644 index 0cb6d0f..0000000 --- a/mouse_message_filter.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2020 ruundii. All rights reserved. -# - -from typing import Optional - -from hid_message_filter import HIDMessageFilter - -#default mouse filter - -# first 16 bits are flags for buttons. 01 is left button, 02 is right, 04 is scroll -# second 16 bits are X -32767 to 32767 -# third 16 bits are Y -32767 to 32767 -# then 8 bits of wheel -127 to 127 - - -# from the default sdp record - -# 0x05, 0x01, // Usage Page (Generic Desktop) 0 -# 0x09, 0x02, // Usage (Mouse) 2 -# 0xa1, 0x01, // Collection (Application) 4 -# 0x09, 0x01, // Usage (Pointer) 6 -# 0xa1, 0x00, // Collection (Physical) 8 -# 0x05, 0x09, // Usage Page (Button) 10 -# 0x19, 0x01, // Usage Minimum (1) 12 -# 0x29, 0x10, // Usage Maximum (16) 14 -# 0x15, 0x00, // Logical Minimum (0) 16 -# 0x25, 0x01, // Logical Maximum (1) 18 -# 0x95, 0x10, // Report Count (16) 20 -# 0x75, 0x01, // Report Size (1) 22 -# 0x81, 0x02, // Input (Data,Var,Abs) 24 -# 0x05, 0x01, // Usage Page (Generic Desktop) 26 -# 0x16, 0x01, 0x80, // Logical Minimum (-32767) 28 -# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 31 -# 0x75, 0x10, // Report Size (16) 34 -# 0x95, 0x02, // Report Count (2) 36 -# 0x09, 0x30, // Usage (X) 38 -# 0x09, 0x31, // Usage (Y) 40 -# 0x81, 0x06, // Input (Data,Var,Rel) 42 -# 0x15, 0x81, // Logical Minimum (-127) 44 -# 0x25, 0x7f, // Logical Maximum (127) 46 -# 0x75, 0x08, // Report Size (8) 48 -# 0x95, 0x01, // Report Count (1) 50 -# 0x09, 0x38, // Usage (Wheel) 52 -# 0x81, 0x06, // Input (Data,Var,Rel) 54 -# 0x05, 0x0c, // Usage Page (Consumer Devices) 56 -# 0x0a, 0x38, 0x02, // Usage (AC Pan) 58 -# 0x95, 0x01, // Report Count (1) 61 -# 0x81, 0x06, // Input (Data,Var,Rel) 63 -# 0xc0, // End Collection 65 -# 0xc0, // End Collection 66 - - -class MouseMessageFilter(HIDMessageFilter): - def __init__(self) -> None: - self.message_size = 7 - - def filter_message_to_host(self, msg: bytes) -> Optional[bytes]: - if len(msg) != self.message_size: - return None - msg = b'\xa1\x03' + self.get_buttons_flags(msg) + self.get_x(msg) + self.get_y(msg) + self.get_wheel(msg) - return msg - - def get_buttons_flags(self, msg: bytes) -> bytes: - return msg[0:2] - - def get_x(self, msg: bytes) -> bytes: - return msg[2:4] - - def get_y(self, msg: bytes) -> bytes: - return msg[4:6] - - def get_wheel(self, msg: bytes) -> bytes: - return msg[6:7] - - def filter_message_from_host(self, msg: bytes) -> None: - return None diff --git a/mouse_mx510_message_filter.py b/mouse_mx510_message_filter.py deleted file mode 100644 index 9b5d3f8..0000000 --- a/mouse_mx510_message_filter.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2020 ruundii. All rights reserved. - - -from hid_message_filter import HIDMessageFilter -from mouse_message_filter import MouseMessageFilter -from bitarray import bitarray -from bitarray.util import ba2int - - -# Logitech USB-PS/2 Optical Mouse -# 0x05, 0x01, // Usage Page (Generic Desktop) 0 -# 0x09, 0x02, // Usage (Mouse) 2 -# 0xa1, 0x01, // Collection (Application) 4 -# 0x09, 0x01, // Usage (Pointer) 6 -# 0xa1, 0x00, // Collection (Physical) 8 -# 0x05, 0x09, // Usage Page (Button) 10 -# 0x19, 0x01, // Usage Minimum (1) 12 -# 0x29, 0x08, // Usage Maximum (8) 14 -# 0x15, 0x00, // Logical Minimum (0) 16 -# 0x25, 0x01, // Logical Maximum (1) 18 -# 0x95, 0x08, // Report Count (8) 20 -# 0x75, 0x01, // Report Size (1) 22 -# 0x81, 0x02, // Input (Data,Var,Abs) 24 -# 0x95, 0x00, // Report Count (0) 26 -# 0x81, 0x03, // Input (Cnst,Var,Abs) 28 -# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 30 -# 0x09, 0x40, // Usage (Vendor Usage 0x40) 33 -# 0x95, 0x02, // Report Count (2) 35 -# 0x75, 0x08, // Report Size (8) 37 -# 0x15, 0x81, // Logical Minimum (-127) 39 -# 0x25, 0x7f, // Logical Maximum (127) 41 -# 0x81, 0x02, // Input (Data,Var,Abs) 43 -# 0x05, 0x01, // Usage Page (Generic Desktop) 45 -# 0x09, 0x38, // Usage (Wheel) 47 -# 0x15, 0x81, // Logical Minimum (-127) 49 -# 0x25, 0x7f, // Logical Maximum (127) 51 -# 0x75, 0x08, // Report Size (8) 53 -# 0x95, 0x01, // Report Count (1) 55 -# 0x81, 0x06, // Input (Data,Var,Rel) 57 -# 0x09, 0x30, // Usage (X) 59 -# 0x09, 0x31, // Usage (Y) 61 -# 0x16, 0x01, 0xf8, // Logical Minimum (-2047) 63 -# 0x26, 0xff, 0x07, // Logical Maximum (2047) 66 -# 0x75, 0x0c, // Report Size (12) 69 -# 0x95, 0x02, // Report Count (2) 71 -# 0x81, 0x06, // Input (Data,Var,Rel) 73 -# 0xc0, // End Collection 75 -# 0xc0, // End Collection 76 - - - -class MX510MessageFilter(MouseMessageFilter): - # first 8 bits are flags for buttons. 01 is left button, 02 is right, 04 is scroll - # second 16 bits are vendor specific x and y, one byte each - # then 8 bits of wheel -127 to 127 - # then x and y from -2047 to 2047, 12 bits each - - def __init__(self) -> None: - self.message_size = 7 - - def get_buttons_flags(self, msg: bytes) -> bytes: - return msg[0:1]+b'\x00' - - def get_x(self, msg: bytes) -> bytes: - a = bitarray() - a.frombytes(msg[4:6]) - return int.to_bytes(ba2int(a[12:16] + a[0:8], signed=True), 2, "little", signed=True) - - def get_y(self, msg: bytes) -> bytes: - a = bitarray() - a.frombytes(msg[5:7]) - return int.to_bytes(ba2int(a[8:16]+a[0:4],signed=True), 2, "little", signed=True) - - def get_wheel(self, msg: bytes) -> bytes: - return msg[3:4] - diff --git a/sdp/keyboard_hid_sdp_record.xml b/sdp/keyboard_hid_sdp_record.xml deleted file mode 100644 index e35977b..0000000 --- a/sdp/keyboard_hid_sdp_record.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sdp/sdp_record.xml b/sdp/sdp_record.xml deleted file mode 100644 index 554d4a3..0000000 --- a/sdp/sdp_record.xml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sdp/sdp_record_apple.xml b/sdp/sdp_record_apple.xml deleted file mode 100644 index c5b68cb..0000000 --- a/sdp/sdp_record_apple.xml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sdp/sdp_record_kbd_mouse.xml b/sdp/sdp_record_kbd_mouse.xml deleted file mode 100644 index 5c3a8e6..0000000 --- a/sdp/sdp_record_kbd_mouse.xml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sdp/sdp_record_main.xml b/sdp/sdp_record_main.xml deleted file mode 100644 index c000efd..0000000 --- a/sdp/sdp_record_main.xml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/install/on_rpi/sdp_record.xml b/sdp_record_template.xml similarity index 86% rename from install/on_rpi/sdp_record.xml rename to sdp_record_template.xml index fef4dac..7e6a29e 100644 --- a/install/on_rpi/sdp_record.xml +++ b/sdp_record_template.xml @@ -81,7 +81,7 @@ - +