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
pull/77/head
Benjamin Tissoires 2018-02-13 20:01:56 +01:00 committed by Peter Hutterer
parent 41a3c85b59
commit 1a6d0205df
3 changed files with 131 additions and 7 deletions

View File

@ -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

View File

@ -52,6 +52,9 @@ INTROSPECTION_XML = '''
<property type='b' name='Listening' access='read'>
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
</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'>
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
</property>
@ -74,6 +77,15 @@ INTROSPECTION_XML = '''
<annotation name='org.freedesktop.DBus.Method.NoReply' value='true'/>
</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'>
<arg name='index' type='u' direction='in'/>
<arg name='json' type='s' direction='out'/>
@ -84,6 +96,10 @@ INTROSPECTION_XML = '''
<signal name='ListeningStopped'>
<arg name='status' type='i' />
</signal>
<signal name='LiveStopped'>
<arg name='status' type='i' />
</signal>
</interface>
</node>
'''
@ -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:

View File

@ -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()