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
This commit is contained in:
parent
41a3c85b59
commit
1a6d0205df
16
tuhi/base.py
16
tuhi/base.py
|
@ -104,6 +104,7 @@ class TuhiDevice(GObject.Object):
|
||||||
self._tuhi_dbus_device = device
|
self._tuhi_dbus_device = device
|
||||||
self._tuhi_dbus_device.connect('register-requested', self._on_register_requested)
|
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::listening', self._on_listening_updated)
|
||||||
|
self._tuhi_dbus_device.connect('notify::live', self._on_live_updated)
|
||||||
|
|
||||||
drawings = self.config.load_drawings(self.address)
|
drawings = self.config.load_drawings(self.address)
|
||||||
if drawings:
|
if drawings:
|
||||||
|
@ -115,6 +116,10 @@ class TuhiDevice(GObject.Object):
|
||||||
def listening(self):
|
def listening(self):
|
||||||
return self._tuhi_dbus_device.listening
|
return self._tuhi_dbus_device.listening
|
||||||
|
|
||||||
|
@GObject.Property
|
||||||
|
def live(self):
|
||||||
|
return self._tuhi_dbus_device.live
|
||||||
|
|
||||||
@GObject.Property
|
@GObject.Property
|
||||||
def battery_percent(self):
|
def battery_percent(self):
|
||||||
return self._battery_percent
|
return self._battery_percent
|
||||||
|
@ -154,6 +159,8 @@ class TuhiDevice(GObject.Object):
|
||||||
|
|
||||||
if mode == DeviceMode.REGISTER:
|
if mode == DeviceMode.REGISTER:
|
||||||
self._wacom_device.start_register()
|
self._wacom_device.start_register()
|
||||||
|
elif mode == DeviceMode.LIVE:
|
||||||
|
self._wacom_device.start_live(self._tuhi_dbus_device.uhid_fd)
|
||||||
else:
|
else:
|
||||||
self._wacom_device.start_listen()
|
self._wacom_device.start_listen()
|
||||||
|
|
||||||
|
@ -184,6 +191,8 @@ class TuhiDevice(GObject.Object):
|
||||||
self.config.store_drawing(self.address, drawing)
|
self.config.store_drawing(self.address, drawing)
|
||||||
|
|
||||||
def _on_fetching_finished(self, device, exception, bluez_device):
|
def _on_fetching_finished(self, device, exception, bluez_device):
|
||||||
|
if self.live:
|
||||||
|
return
|
||||||
bluez_device.disconnect_device()
|
bluez_device.disconnect_device()
|
||||||
if exception is not None:
|
if exception is not None:
|
||||||
logger.info(exception)
|
logger.info(exception)
|
||||||
|
@ -202,6 +211,13 @@ class TuhiDevice(GObject.Object):
|
||||||
def _on_listening_updated(self, dbus_device, pspec):
|
def _on_listening_updated(self, dbus_device, pspec):
|
||||||
self.notify('listening')
|
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):
|
def _on_battery_status(self, wacom_device, percent, is_charging, bluez_device):
|
||||||
if is_charging:
|
if is_charging:
|
||||||
self.battery_state = TuhiDevice.BatteryState.CHARGING
|
self.battery_state = TuhiDevice.BatteryState.CHARGING
|
||||||
|
|
|
@ -52,6 +52,9 @@ INTROSPECTION_XML = '''
|
||||||
<property type='b' name='Listening' access='read'>
|
<property type='b' name='Listening' access='read'>
|
||||||
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
|
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
|
||||||
</property>
|
</property>
|
||||||
|
<property type='b' name='Live' access='read'>
|
||||||
|
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
|
||||||
|
</property>
|
||||||
<property type='u' name='BatteryPercent' access='read'>
|
<property type='u' name='BatteryPercent' access='read'>
|
||||||
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
|
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
|
||||||
</property>
|
</property>
|
||||||
|
@ -74,6 +77,15 @@ INTROSPECTION_XML = '''
|
||||||
<annotation name='org.freedesktop.DBus.Method.NoReply' value='true'/>
|
<annotation name='org.freedesktop.DBus.Method.NoReply' value='true'/>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<method name='StartLive'>
|
||||||
|
<arg name='uhid' type='h' />
|
||||||
|
<annotation name='org.freedesktop.DBus.Method.NoReply' value='true'/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name='StopLive'>
|
||||||
|
<annotation name='org.freedesktop.DBus.Method.NoReply' value='true'/>
|
||||||
|
</method>
|
||||||
|
|
||||||
<method name='GetJSONData'>
|
<method name='GetJSONData'>
|
||||||
<arg name='index' type='u' direction='in'/>
|
<arg name='index' type='u' direction='in'/>
|
||||||
<arg name='json' type='s' direction='out'/>
|
<arg name='json' type='s' direction='out'/>
|
||||||
|
@ -84,6 +96,10 @@ INTROSPECTION_XML = '''
|
||||||
<signal name='ListeningStopped'>
|
<signal name='ListeningStopped'>
|
||||||
<arg name='status' type='i' />
|
<arg name='status' type='i' />
|
||||||
</signal>
|
</signal>
|
||||||
|
|
||||||
|
<signal name='LiveStopped'>
|
||||||
|
<arg name='status' type='i' />
|
||||||
|
</signal>
|
||||||
</interface>
|
</interface>
|
||||||
</node>
|
</node>
|
||||||
'''
|
'''
|
||||||
|
@ -150,6 +166,9 @@ class TuhiDBusDevice(_TuhiDBus):
|
||||||
self.registered = device.registered
|
self.registered = device.registered
|
||||||
self._listening = False
|
self._listening = False
|
||||||
self._listening_client = None
|
self._listening_client = None
|
||||||
|
self._live = False
|
||||||
|
self._uhid_fd = None
|
||||||
|
self._live_client = None
|
||||||
self._dbusid = self._register_object(connection)
|
self._dbusid = self._register_object(connection)
|
||||||
self._battery_percent = 0
|
self._battery_percent = 0
|
||||||
self._battery_state = device.battery_state
|
self._battery_state = device.battery_state
|
||||||
|
@ -170,6 +189,22 @@ class TuhiDBusDevice(_TuhiDBus):
|
||||||
self._listening = value
|
self._listening = value
|
||||||
self.properties_changed({'Listening': GLib.Variant.new_boolean(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
|
@GObject.Property
|
||||||
def registered(self):
|
def registered(self):
|
||||||
return self._registered
|
return self._registered
|
||||||
|
@ -231,6 +266,12 @@ class TuhiDBusDevice(_TuhiDBus):
|
||||||
elif methodname == 'StopListening':
|
elif methodname == 'StopListening':
|
||||||
self._stop_listening(connection, sender)
|
self._stop_listening(connection, sender)
|
||||||
invocation.return_value()
|
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':
|
elif methodname == 'GetJSONData':
|
||||||
json = GLib.Variant.new_string(self._json_data(args))
|
json = GLib.Variant.new_string(self._json_data(args))
|
||||||
invocation.return_value(GLib.Variant.new_tuple(json))
|
invocation.return_value(GLib.Variant.new_tuple(json))
|
||||||
|
@ -252,6 +293,8 @@ class TuhiDBusDevice(_TuhiDBus):
|
||||||
return ts
|
return ts
|
||||||
elif propname == 'Listening':
|
elif propname == 'Listening':
|
||||||
return GLib.Variant.new_boolean(self.listening)
|
return GLib.Variant.new_boolean(self.listening)
|
||||||
|
elif propname == 'Live':
|
||||||
|
return GLib.Variant.new_boolean(self.live)
|
||||||
elif propname == 'BatteryPercent':
|
elif propname == 'BatteryPercent':
|
||||||
return GLib.Variant.new_uint32(self.battery_percent)
|
return GLib.Variant.new_uint32(self.battery_percent)
|
||||||
elif propname == 'BatteryState':
|
elif propname == 'BatteryState':
|
||||||
|
@ -315,6 +358,7 @@ class TuhiDBusDevice(_TuhiDBus):
|
||||||
return
|
return
|
||||||
|
|
||||||
self._stop_listening(connection, user_data)
|
self._stop_listening(connection, user_data)
|
||||||
|
self._stop_live(connection, user_data)
|
||||||
|
|
||||||
def _stop_listening(self, connection, sender, errno=0):
|
def _stop_listening(self, connection, sender, errno=0):
|
||||||
if not self.listening or sender != self._listening_client[0]:
|
if not self.listening or sender != self._listening_client[0]:
|
||||||
|
@ -331,6 +375,43 @@ class TuhiDBusDevice(_TuhiDBus):
|
||||||
self.listening = False
|
self.listening = False
|
||||||
self.notify('listening')
|
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):
|
def _json_data(self, args):
|
||||||
index = args[0]
|
index = args[0]
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -51,6 +51,7 @@ class Protocol(enum.Enum):
|
||||||
class DeviceMode(enum.Enum):
|
class DeviceMode(enum.Enum):
|
||||||
REGISTER = 1
|
REGISTER = 1
|
||||||
LISTEN = 2
|
LISTEN = 2
|
||||||
|
LIVE = 3
|
||||||
|
|
||||||
|
|
||||||
def signed_char_to_int(v):
|
def signed_char_to_int(v):
|
||||||
|
@ -274,6 +275,7 @@ class WacomProtocolBase(WacomProtocolLowLevelComm):
|
||||||
def __init__(self, device, uuid):
|
def __init__(self, device, uuid):
|
||||||
super().__init__(device)
|
super().__init__(device)
|
||||||
self._uuid = uuid
|
self._uuid = uuid
|
||||||
|
self._timestamp = 0
|
||||||
self.pen_data_buffer = []
|
self.pen_data_buffer = []
|
||||||
|
|
||||||
device.connect_gatt_value(WACOM_CHRC_LIVE_PEN_DATA_UUID,
|
device.connect_gatt_value(WACOM_CHRC_LIVE_PEN_DATA_UUID,
|
||||||
|
@ -287,12 +289,14 @@ class WacomProtocolBase(WacomProtocolLowLevelComm):
|
||||||
if value[0] == 0x10:
|
if value[0] == 0x10:
|
||||||
pressure = int.from_bytes(value[2:4], byteorder='little')
|
pressure = int.from_bytes(value[2:4], byteorder='little')
|
||||||
buttons = int(value[10])
|
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:
|
elif value[0] == 0xa2:
|
||||||
# entering proximity event
|
# entering proximity event
|
||||||
length = value[1]
|
length = value[1]
|
||||||
pen_id = binascii.hexlify(bytes(value[2:]))
|
# timestamp is now in ms
|
||||||
logger.info(f'Pen {pen_id} entered proximity')
|
timestamp = int.from_bytes(value[4:], byteorder='little') * 5
|
||||||
|
self._timestamp = timestamp
|
||||||
|
logger.debug(f'Pen entered proximity, timestamp: {timestamp}')
|
||||||
elif value[0] == 0xa1:
|
elif value[0] == 0xa1:
|
||||||
# data event
|
# data event
|
||||||
length = value[1]
|
length = value[1]
|
||||||
|
@ -302,13 +306,14 @@ class WacomProtocolBase(WacomProtocolLowLevelComm):
|
||||||
data = value[2:]
|
data = value[2:]
|
||||||
while data:
|
while data:
|
||||||
if bytes(data) == b'\xff\xff\xff\xff\xff\xff':
|
if bytes(data) == b'\xff\xff\xff\xff\xff\xff':
|
||||||
logger.info(f'Pen left proximity')
|
logger.debug(f'Pen left proximity')
|
||||||
else:
|
else:
|
||||||
x = int.from_bytes(data[0:2], byteorder='little')
|
x = int.from_bytes(data[0:2], byteorder='little')
|
||||||
y = int.from_bytes(data[2:4], byteorder='little')
|
y = int.from_bytes(data[2:4], byteorder='little')
|
||||||
pressure = int.from_bytes(data[4:6], 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:]
|
data = data[6:]
|
||||||
|
self._timestamp += 5
|
||||||
|
|
||||||
def _on_pen_data_received(self, name, data):
|
def _on_pen_data_received(self, name, data):
|
||||||
self.fw_logger.debug(f'RX Pen <-- {list2hex(data)}')
|
self.fw_logger.debug(f'RX Pen <-- {list2hex(data)}')
|
||||||
|
@ -384,9 +389,10 @@ class WacomProtocolBase(WacomProtocolLowLevelComm):
|
||||||
expected_opcode=0xb3,
|
expected_opcode=0xb3,
|
||||||
arguments=args)
|
arguments=args)
|
||||||
|
|
||||||
def start_live(self):
|
def start_live(self, uhid):
|
||||||
self.send_nordic_command_sync(command=0xb1,
|
self.send_nordic_command_sync(command=0xb1,
|
||||||
expected_opcode=0xb3)
|
expected_opcode=0xb3)
|
||||||
|
logger.debug(f'Starting wacom live mode on fd: {uhid}')
|
||||||
|
|
||||||
def stop_live(self):
|
def stop_live(self):
|
||||||
args = [0x02]
|
args = [0x02]
|
||||||
|
@ -609,6 +615,16 @@ class WacomProtocolBase(WacomProtocolLowLevelComm):
|
||||||
fw_low = self.get_firmware_version(1)
|
fw_low = self.get_firmware_version(1)
|
||||||
logger.info(f'firmware is {fw_high}-{fw_low}')
|
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):
|
class WacomProtocolSpark(WacomProtocolBase):
|
||||||
'''
|
'''
|
||||||
|
@ -829,7 +845,10 @@ class WacomDevice(GObject.Object):
|
||||||
self._is_running = True
|
self._is_running = True
|
||||||
exception = None
|
exception = None
|
||||||
try:
|
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()
|
self.register_device()
|
||||||
else:
|
else:
|
||||||
assert self._wacom_protocol is not None
|
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 = threading.Thread(target=self._run, args=(DeviceMode.LISTEN,))
|
||||||
self.thread.start()
|
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):
|
def start_register(self):
|
||||||
self.thread = threading.Thread(target=self._run, args=(DeviceMode.REGISTER,))
|
self.thread = threading.Thread(target=self._run, args=(DeviceMode.REGISTER,))
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
Loading…
Reference in New Issue