From 1a6d0205dfd04cf018f5901760045e260f1512a1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Tue, 13 Feb 2018 20:01:56 +0100 Subject: [PATCH] tuhi: hook up live mode Currently just enable/disable live mode. The int parameter is doomed to be an opened fd to the uhid node --- tuhi/base.py | 16 +++++++++ tuhi/dbusserver.py | 81 ++++++++++++++++++++++++++++++++++++++++++++++ tuhi/wacom.py | 41 +++++++++++++++++++---- 3 files changed, 131 insertions(+), 7 deletions(-) diff --git a/tuhi/base.py b/tuhi/base.py index 789ec45..86d167d 100644 --- a/tuhi/base.py +++ b/tuhi/base.py @@ -104,6 +104,7 @@ class TuhiDevice(GObject.Object): self._tuhi_dbus_device = device self._tuhi_dbus_device.connect('register-requested', self._on_register_requested) self._tuhi_dbus_device.connect('notify::listening', self._on_listening_updated) + self._tuhi_dbus_device.connect('notify::live', self._on_live_updated) drawings = self.config.load_drawings(self.address) if drawings: @@ -115,6 +116,10 @@ class TuhiDevice(GObject.Object): def listening(self): return self._tuhi_dbus_device.listening + @GObject.Property + def live(self): + return self._tuhi_dbus_device.live + @GObject.Property def battery_percent(self): return self._battery_percent @@ -154,6 +159,8 @@ class TuhiDevice(GObject.Object): if mode == DeviceMode.REGISTER: self._wacom_device.start_register() + elif mode == DeviceMode.LIVE: + self._wacom_device.start_live(self._tuhi_dbus_device.uhid_fd) else: self._wacom_device.start_listen() @@ -184,6 +191,8 @@ class TuhiDevice(GObject.Object): self.config.store_drawing(self.address, drawing) def _on_fetching_finished(self, device, exception, bluez_device): + if self.live: + return bluez_device.disconnect_device() if exception is not None: logger.info(exception) @@ -202,6 +211,13 @@ class TuhiDevice(GObject.Object): def _on_listening_updated(self, dbus_device, pspec): self.notify('listening') + def _on_live_updated(self, dbus_device, pspec): + if self.live: + self._connect_device(DeviceMode.LIVE) + else: + if self._wacom_device is not None: + self._wacom_device.stop_live() + def _on_battery_status(self, wacom_device, percent, is_charging, bluez_device): if is_charging: self.battery_state = TuhiDevice.BatteryState.CHARGING diff --git a/tuhi/dbusserver.py b/tuhi/dbusserver.py index 11b9c5a..38a5273 100755 --- a/tuhi/dbusserver.py +++ b/tuhi/dbusserver.py @@ -52,6 +52,9 @@ INTROSPECTION_XML = ''' + + + @@ -74,6 +77,15 @@ INTROSPECTION_XML = ''' + + + + + + + + + @@ -84,6 +96,10 @@ INTROSPECTION_XML = ''' + + + + ''' @@ -150,6 +166,9 @@ class TuhiDBusDevice(_TuhiDBus): self.registered = device.registered self._listening = False self._listening_client = None + self._live = False + self._uhid_fd = None + self._live_client = None self._dbusid = self._register_object(connection) self._battery_percent = 0 self._battery_state = device.battery_state @@ -170,6 +189,22 @@ class TuhiDBusDevice(_TuhiDBus): self._listening = value self.properties_changed({'Listening': GLib.Variant.new_boolean(value)}) + @GObject.Property + def live(self): + return self._live + + @live.setter + def live(self, value): + if self._live == value: + return + + self._live = value + self.properties_changed({'Live': GLib.Variant.new_boolean(value)}) + + @GObject.Property + def uhid_fd(self): + return self._uhid_fd + @GObject.Property def registered(self): return self._registered @@ -231,6 +266,12 @@ class TuhiDBusDevice(_TuhiDBus): elif methodname == 'StopListening': self._stop_listening(connection, sender) invocation.return_value() + elif methodname == 'StartLive': + self._start_live(connection, sender, args) + invocation.return_value() + elif methodname == 'StopLive': + self._stop_live(connection, sender) + invocation.return_value() elif methodname == 'GetJSONData': json = GLib.Variant.new_string(self._json_data(args)) invocation.return_value(GLib.Variant.new_tuple(json)) @@ -252,6 +293,8 @@ class TuhiDBusDevice(_TuhiDBus): return ts elif propname == 'Listening': return GLib.Variant.new_boolean(self.listening) + elif propname == 'Live': + return GLib.Variant.new_boolean(self.live) elif propname == 'BatteryPercent': return GLib.Variant.new_uint32(self.battery_percent) elif propname == 'BatteryState': @@ -315,6 +358,7 @@ class TuhiDBusDevice(_TuhiDBus): return self._stop_listening(connection, user_data) + self._stop_live(connection, user_data) def _stop_listening(self, connection, sender, errno=0): if not self.listening or sender != self._listening_client[0]: @@ -331,6 +375,43 @@ class TuhiDBusDevice(_TuhiDBus): self.listening = False self.notify('listening') + def _start_live(self, connection, sender, args): + if self.live: + logger.debug(f'{self} - already in live mode') + + # silently ignore it for the current client but send EAGAIN to + # other clients + if sender != self._listening_client[0]: + status = GLib.Variant.new_int32(-errno.EAGAIN) + self.signal('LiveStopped', status, dest=sender) + return + + s = connection.signal_subscribe(sender='org.freedesktop.DBus', + interface_name='org.freedesktop.DBus', + member='NameOwnerChanged', + object_path='/org/freedesktop/DBus', + arg0=None, + flags=Gio.DBusSignalFlags.NONE, + callback=self._on_name_owner_changed_signal_cb, + user_data=sender) + self._live_client = (sender, s) + logger.debug(f'Live mode started on {self.name} for {sender}') + + self.live = True + self._uhid_fd = args[0] + + def _stop_live(self, connection, sender, errno=0): + if not self.live or sender != self._live_client[0]: + return + + connection.signal_unsubscribe(self._live_client[1]) + self._live_client = None + logger.debug(f'Live mode stopped on {self.name} for {sender}') + + status = GLib.Variant.new_int32(errno) + self.signal('LiveStopped', status, dest=sender) + self.live = False + def _json_data(self, args): index = args[0] try: diff --git a/tuhi/wacom.py b/tuhi/wacom.py index 8e43cd4..785b2a3 100644 --- a/tuhi/wacom.py +++ b/tuhi/wacom.py @@ -51,6 +51,7 @@ class Protocol(enum.Enum): class DeviceMode(enum.Enum): REGISTER = 1 LISTEN = 2 + LIVE = 3 def signed_char_to_int(v): @@ -274,6 +275,7 @@ class WacomProtocolBase(WacomProtocolLowLevelComm): def __init__(self, device, uuid): super().__init__(device) self._uuid = uuid + self._timestamp = 0 self.pen_data_buffer = [] device.connect_gatt_value(WACOM_CHRC_LIVE_PEN_DATA_UUID, @@ -287,12 +289,14 @@ class WacomProtocolBase(WacomProtocolLowLevelComm): if value[0] == 0x10: pressure = int.from_bytes(value[2:4], byteorder='little') buttons = int(value[10]) - logger.info(f'New Pen Data: pressure: {pressure}, button: {buttons}') + logger.debug(f'New Pen Data: pressure: {pressure}, button: {buttons}') elif value[0] == 0xa2: # entering proximity event length = value[1] - pen_id = binascii.hexlify(bytes(value[2:])) - logger.info(f'Pen {pen_id} entered proximity') + # timestamp is now in ms + timestamp = int.from_bytes(value[4:], byteorder='little') * 5 + self._timestamp = timestamp + logger.debug(f'Pen entered proximity, timestamp: {timestamp}') elif value[0] == 0xa1: # data event length = value[1] @@ -302,13 +306,14 @@ class WacomProtocolBase(WacomProtocolLowLevelComm): data = value[2:] while data: if bytes(data) == b'\xff\xff\xff\xff\xff\xff': - logger.info(f'Pen left proximity') + logger.debug(f'Pen left proximity') else: x = int.from_bytes(data[0:2], byteorder='little') y = int.from_bytes(data[2:4], byteorder='little') pressure = int.from_bytes(data[4:6], byteorder='little') - self.logger.info(f'New Pen Data: ({x},{y}), pressure: {pressure}') + logger.debug(f'New Pen Data: ({x},{y}), pressure: {pressure}') data = data[6:] + self._timestamp += 5 def _on_pen_data_received(self, name, data): self.fw_logger.debug(f'RX Pen <-- {list2hex(data)}') @@ -384,9 +389,10 @@ class WacomProtocolBase(WacomProtocolLowLevelComm): expected_opcode=0xb3, arguments=args) - def start_live(self): + def start_live(self, uhid): self.send_nordic_command_sync(command=0xb1, expected_opcode=0xb3) + logger.debug(f'Starting wacom live mode on fd: {uhid}') def stop_live(self): args = [0x02] @@ -609,6 +615,16 @@ class WacomProtocolBase(WacomProtocolLowLevelComm): fw_low = self.get_firmware_version(1) logger.info(f'firmware is {fw_high}-{fw_low}') + def live_mode(self, mode, uhid): + try: + if mode: + self.check_connection() + self.start_live(uhid) + else: + self.stop_live() + except WacomEEAGAINException: + logger.warning("no data, please make sure the LED is blue and the button is pressed to switch it back to green") + class WacomProtocolSpark(WacomProtocolBase): ''' @@ -829,7 +845,10 @@ class WacomDevice(GObject.Object): self._is_running = True exception = None try: - if mode == DeviceMode.REGISTER: + if mode == DeviceMode.LIVE: + assert self._wacom_protocol is not None + self._wacom_protocol.live_mode(args[1], args[2]) + elif mode == DeviceMode.REGISTER: self.register_device() else: assert self._wacom_protocol is not None @@ -845,6 +864,14 @@ class WacomDevice(GObject.Object): self.thread = threading.Thread(target=self._run, args=(DeviceMode.LISTEN,)) self.thread.start() + def start_live(self, uhid_fd): + self.thread = threading.Thread(target=self._run, args=(DeviceMode.LIVE, True, uhid_fd)) + self.thread.start() + + def stop_live(self): + self.thread = threading.Thread(target=self._run, args=(DeviceMode.LIVE, False, -1)) + self.thread.start() + def start_register(self): self.thread = threading.Thread(target=self._run, args=(DeviceMode.REGISTER,)) self.thread.start()