wacom: add support for the Intuos Pro 2 paper
parent
7796d3c110
commit
138497a73b
|
@ -27,7 +27,7 @@ logging.basicConfig(format='%(levelname)s: %(name)s: %(message)s',
|
||||||
level=logging.INFO)
|
level=logging.INFO)
|
||||||
logger = logging.getLogger('tuhi')
|
logger = logging.getLogger('tuhi')
|
||||||
|
|
||||||
WACOM_COMPANY_IDS = [0x4755]
|
WACOM_COMPANY_IDS = [0x4755, 0x4157]
|
||||||
|
|
||||||
|
|
||||||
class TuhiDevice(GObject.Object):
|
class TuhiDevice(GObject.Object):
|
||||||
|
|
128
tuhi/wacom.py
128
tuhi/wacom.py
|
@ -287,11 +287,13 @@ class WacomRegisterHelper(WacomProtocolLowLevelComm):
|
||||||
self.emit('button-press-required')
|
self.emit('button-press-required')
|
||||||
|
|
||||||
# Wait for the button confirmation event, or any error
|
# Wait for the button confirmation event, or any error
|
||||||
data = self.wait_nordic_data([0xe4, 0xb3], 10)
|
data = self.wait_nordic_data([0xe4, 0xb3, 0x53], 10)
|
||||||
|
|
||||||
if protocol == Protocol.UNKNOWN:
|
if protocol == Protocol.UNKNOWN:
|
||||||
if data.opcode == 0xe4:
|
if data.opcode == 0xe4:
|
||||||
protocol = Protocol.SLATE
|
protocol = Protocol.SLATE
|
||||||
|
elif data.opcode == 0x53:
|
||||||
|
protocol = Protocol.INTUOS_PRO
|
||||||
else:
|
else:
|
||||||
raise WacomException(f'unexpected opcode to register reply: {data.opcode:02x}')
|
raise WacomException(f'unexpected opcode to register reply: {data.opcode:02x}')
|
||||||
|
|
||||||
|
@ -836,6 +838,125 @@ class WacomProtocolSlate(WacomProtocolSpark):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class WacomProtocolIntuosPro(WacomProtocolSlate):
|
||||||
|
'''
|
||||||
|
Subclass to handle the communication oddities with the Wacom
|
||||||
|
IntuosPro-like devices.
|
||||||
|
|
||||||
|
:param device: the BlueZDevice object that is this wacom device
|
||||||
|
:param uuid: the UUID {to be} assigned to the device
|
||||||
|
'''
|
||||||
|
width = 44800
|
||||||
|
height = 29600
|
||||||
|
protocol = Protocol.INTUOS_PRO
|
||||||
|
|
||||||
|
def __init__(self, device, uuid):
|
||||||
|
super().__init__(device, uuid)
|
||||||
|
|
||||||
|
def time_to_bytes(self):
|
||||||
|
t = int(time.time())
|
||||||
|
return list(t.to_bytes(length=4, byteorder='little')) + [0x00, 0x00]
|
||||||
|
|
||||||
|
def time_from_bytes(self, data):
|
||||||
|
seconds = int.from_bytes(data[0:4], byteorder='little')
|
||||||
|
return time.gmtime(seconds)
|
||||||
|
|
||||||
|
# set_time is identical to spark/slate except the timestamp format
|
||||||
|
|
||||||
|
def read_time(self):
|
||||||
|
data = self.send_nordic_command_sync(command=0xd6,
|
||||||
|
expected_opcode=0xbd)
|
||||||
|
|
||||||
|
# Last two bytes are unknown
|
||||||
|
t = self.time_from_bytes(data)
|
||||||
|
ts = time.strftime('%y-%m-%d %H:%M:%S', time.localtime(t))
|
||||||
|
logger.debug(f'b6 returned: {ts}')
|
||||||
|
|
||||||
|
def get_firmware_version(self, arg):
|
||||||
|
data = self.send_nordic_command_sync(command=0xb7,
|
||||||
|
expected_opcode=0xb8,
|
||||||
|
arguments=(arg,))
|
||||||
|
fw = ''.join([chr(d) for d in data[1:]])
|
||||||
|
return fw
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
data = self.send_nordic_command_sync(command=0xdb,
|
||||||
|
expected_opcode=0xbc)
|
||||||
|
return bytes(data)
|
||||||
|
|
||||||
|
def set_name(self, name):
|
||||||
|
args = [ord(c) for c in name]
|
||||||
|
data = self.send_nordic_command_sync(command=0xbb,
|
||||||
|
arguments=args,
|
||||||
|
expected_opcode=0xb3)
|
||||||
|
return bytes(data)
|
||||||
|
|
||||||
|
def check_connection(self):
|
||||||
|
args = [int(i) for i in binascii.unhexlify(self._uuid)]
|
||||||
|
self.send_nordic_command_sync(command=0xe6,
|
||||||
|
expected_opcode=[0x50, 0x51],
|
||||||
|
arguments=args)
|
||||||
|
|
||||||
|
def parse_pen_data_prefix(self, data):
|
||||||
|
expected_prefix = b'\x67\x82\x69\x65'
|
||||||
|
prefix = data[:4]
|
||||||
|
offset = len(prefix)
|
||||||
|
# not sure if we really need this check
|
||||||
|
if bytes(prefix) != expected_prefix:
|
||||||
|
logger.debug(f'Expected pen data prefix {expected_prefix} but got {prefix}')
|
||||||
|
return False, 0
|
||||||
|
|
||||||
|
# This is the time the button was pressed after drawing, i.e. the
|
||||||
|
# end of the drawing
|
||||||
|
t = self.time_from_bytes(data[offset:])
|
||||||
|
offset += 6
|
||||||
|
|
||||||
|
# Confirmed it's LE for at least 2 bytes (that was fun...), but
|
||||||
|
# could be 4 or more. Are 0xffff strokes enough for everybody?
|
||||||
|
nstrokes = int.from_bytes(data[offset:offset + 2], byteorder='little')
|
||||||
|
offset += 2
|
||||||
|
|
||||||
|
# Can't have enough zeroes. They'll come in handy one day
|
||||||
|
expected_header = b'\x00\x00\x00\x00'
|
||||||
|
data_header = data[offset:offset + len(expected_header)]
|
||||||
|
if bytes(data_header) != expected_header:
|
||||||
|
logger.debug(f'Missing zeroes, got {data_header}')
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
# First stroke timestamp, note this is less than the above timestamp
|
||||||
|
# ff fa c3 <6-byte-timestamp>
|
||||||
|
expected_header = b'\xff\xfa\xc3'
|
||||||
|
data_header = data[offset:offset + len(expected_header)]
|
||||||
|
if bytes(data_header) != expected_header:
|
||||||
|
logger.debug(f'Missing first stroke timestamp, got {data_header}')
|
||||||
|
offset += len(expected_header)
|
||||||
|
|
||||||
|
ot = self.time_from_bytes(data[offset:])
|
||||||
|
offset += 6
|
||||||
|
|
||||||
|
# Unclear what this is
|
||||||
|
expected_header = b'\xff\x0a\x87\x75\x80\x28\x42\x00\x10'
|
||||||
|
data_header = data[offset:offset + len(expected_header)]
|
||||||
|
if bytes(data_header) != expected_header:
|
||||||
|
logger.debug(f'Missing header 2, got {data_header}')
|
||||||
|
offset += len(expected_header)
|
||||||
|
|
||||||
|
t = time.strftime("%y%m%d%H%M%S", t)
|
||||||
|
ot = time.strftime("%y%m%d%H%M%S", ot)
|
||||||
|
logger.debug(f'Drawing timestamp: {t}, {nstrokes} strokes, other timestamp {ot}')
|
||||||
|
|
||||||
|
return True, offset
|
||||||
|
|
||||||
|
def parse_next_stroke_prefix(self, opcode, raw_args):
|
||||||
|
if opcode != 0x03fa:
|
||||||
|
return False
|
||||||
|
|
||||||
|
t = self.time_from_bytes(raw_args[2:])
|
||||||
|
t = time.strftime("%y%m%d%H%M%S", t)
|
||||||
|
logger.info(f'stroke time: {t}')
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class WacomDevice(GObject.Object):
|
class WacomDevice(GObject.Object):
|
||||||
'''
|
'''
|
||||||
Class to communicate with the Wacom device. Communication is handled in
|
Class to communicate with the Wacom device. Communication is handled in
|
||||||
|
@ -896,10 +1017,9 @@ class WacomDevice(GObject.Object):
|
||||||
self._wacom_protocol = WacomProtocolSpark(self._device, self._uuid)
|
self._wacom_protocol = WacomProtocolSpark(self._device, self._uuid)
|
||||||
elif protocol == Protocol.SLATE:
|
elif protocol == Protocol.SLATE:
|
||||||
self._wacom_protocol = WacomProtocolSlate(self._device, self._uuid)
|
self._wacom_protocol = WacomProtocolSlate(self._device, self._uuid)
|
||||||
|
elif protocol == Protocol.INTUOS_PRO:
|
||||||
|
self._wacom_protocol = WacomProtocolIntuosPro(self._device, self._uuid)
|
||||||
else:
|
else:
|
||||||
# FIXME: change to an assert once intuos-pro is implemented, we
|
|
||||||
# never get here
|
|
||||||
logger.error(f'Unknown Protocol {protocol}')
|
|
||||||
raise WacomCorruptDataException(f'Protocol "{protocol}" not implemented')
|
raise WacomCorruptDataException(f'Protocol "{protocol}" not implemented')
|
||||||
|
|
||||||
logger.debug(f'{self._device.name} is using protocol {protocol}')
|
logger.debug(f'{self._device.name} is using protocol {protocol}')
|
||||||
|
|
Loading…
Reference in New Issue