Add support for cirque glidepoint trackpad
This commit is contained in:
parent
5448cb4479
commit
ddaf9946eb
212
kmk/modules/glidepoint.py
Normal file
212
kmk/modules/glidepoint.py
Normal file
@ -0,0 +1,212 @@
|
||||
try:
|
||||
from typing import Callable
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from micropython import const
|
||||
|
||||
from kmk.keys import AX
|
||||
from kmk.modules import Module
|
||||
from kmk.utils import Debug
|
||||
|
||||
debug = Debug(__name__)
|
||||
|
||||
_ADDRESS = const(0x2A)
|
||||
|
||||
_MASK_READ = const(0xA0)
|
||||
_MASK_WRITE = const(0x80)
|
||||
|
||||
_REG_FW_ID = const(0x00)
|
||||
_REG_FW_VER = const(0x01)
|
||||
_REG_STATUS = const(0x02)
|
||||
_REG_SYS_CFG = const(0x03)
|
||||
_REG_FEED_CFG1 = const(0x04)
|
||||
_REG_FEED_CFG2 = const(0x05)
|
||||
_REG_CAL_CFG = const(0x07)
|
||||
_REG_AUX_CTL = const(0x08)
|
||||
_REG_SAMPLE_RATE = const(0x09)
|
||||
_REG_ZIDLE = const(0x0A)
|
||||
_REG_Z_SCALER = const(0x0B)
|
||||
_REG_SLEEP_INTERVAL = const(0x0C)
|
||||
_REG_SLEEP_TIMER = const(0x0D)
|
||||
_REG_DATA = const(0x12)
|
||||
|
||||
_FEED1_ENABLE = const(0x01)
|
||||
_FEED1_ABSOLUTE = const(0x02)
|
||||
_FEED1_NO_FILTER = const(0x04)
|
||||
_FEED1_NO_X_DATA = const(0x08)
|
||||
_FEED1_NO_Y_DATA = const(0x10)
|
||||
_FEED1_INVERT_X = const(0x40)
|
||||
_FEED1_INVERT_Y = const(0x80)
|
||||
|
||||
_FEED2_INTELLIMOUSE = const(0x01)
|
||||
_FEED2_NO_TAPS = const(0x02)
|
||||
_FEED2_NO_SEC_TAPS = const(0x04)
|
||||
_FEED2_NO_SCROLL = const(0x08)
|
||||
_FEED2_NO_GLIDEEXTEND = const(0x10)
|
||||
_FEED2_SWAP_XY = const(0x80)
|
||||
|
||||
|
||||
def with_i2c_lock(rw: Callable[[object, int], None]) -> Callable[[object, int], None]:
|
||||
def _(self, n: int) -> None:
|
||||
if not self._i2c.try_lock():
|
||||
if debug.enabled:
|
||||
debug("can't acquire lock")
|
||||
return
|
||||
try:
|
||||
rw(self, n)
|
||||
finally:
|
||||
self._i2c.unlock()
|
||||
|
||||
return _
|
||||
|
||||
|
||||
class AbsoluteHandler:
|
||||
cfg = (
|
||||
_REG_FEED_CFG2,
|
||||
0x00,
|
||||
_REG_FEED_CFG1,
|
||||
_FEED1_ENABLE | _FEED1_NO_FILTER | _FEED1_ABSOLUTE | _FEED1_INVERT_X,
|
||||
)
|
||||
|
||||
def handle(buffer: bytearray, keyboard) -> None:
|
||||
button = buffer[0]
|
||||
x_low = buffer[2]
|
||||
y_low = buffer[3]
|
||||
high = buffer[4]
|
||||
z_lvl = buffer[5]
|
||||
|
||||
x_pos = ((high & 0x0F) << 8) | x_low
|
||||
y_pos = ((high & 0xF0) << 4) | y_low
|
||||
|
||||
if debug.enabled:
|
||||
debug(
|
||||
'buttons:',
|
||||
bin(button),
|
||||
' x_pos:',
|
||||
x_pos,
|
||||
' y_pos:',
|
||||
y_pos,
|
||||
'z_lvl:',
|
||||
z_lvl,
|
||||
)
|
||||
|
||||
|
||||
class RelativeHandler:
|
||||
cfg = (
|
||||
_REG_FEED_CFG2,
|
||||
0x00,
|
||||
_REG_FEED_CFG1,
|
||||
_FEED1_ENABLE | _FEED1_NO_FILTER | _FEED1_INVERT_X,
|
||||
)
|
||||
|
||||
def handle(buffer: bytearray, keyboard) -> None:
|
||||
button = buffer[0] & 0b00000111
|
||||
x_sign = buffer[0] & 0b00010000
|
||||
y_sign = buffer[0] & 0b00100000
|
||||
x_delta = buffer[1]
|
||||
y_delta = buffer[2]
|
||||
w_delta = buffer[3]
|
||||
|
||||
if x_sign:
|
||||
x_delta -= 0xFF
|
||||
if y_sign:
|
||||
y_delta -= 0xFF
|
||||
|
||||
if x_delta != 0:
|
||||
AX.X.move(keyboard, x_delta)
|
||||
if y_delta != 0:
|
||||
AX.Y.move(keyboard, y_delta)
|
||||
|
||||
if debug.enabled:
|
||||
debug(
|
||||
'buttons:',
|
||||
bin(button),
|
||||
' x_delta:',
|
||||
x_delta,
|
||||
' y_delta:',
|
||||
y_delta,
|
||||
' w_delta',
|
||||
w_delta,
|
||||
)
|
||||
|
||||
|
||||
class GlidePoint(Module):
|
||||
def __init__(self, i2c):
|
||||
self._i2c = i2c
|
||||
self._buffer = bytearray(8)
|
||||
|
||||
# self.handler = RelativeHandler
|
||||
self.handler = AbsoluteHandler
|
||||
|
||||
@with_i2c_lock
|
||||
def _read(self, n: int) -> None:
|
||||
self._buffer[0] |= _MASK_READ
|
||||
self._i2c.writeto_then_readfrom(
|
||||
_ADDRESS, self._buffer, self._buffer, out_end=1, in_end=n
|
||||
)
|
||||
|
||||
@with_i2c_lock
|
||||
def _write(self, n: int) -> None:
|
||||
for i in range(0, n, 2):
|
||||
self._buffer[i] |= _MASK_WRITE
|
||||
self._i2c.writeto(_ADDRESS, self._buffer, end=n)
|
||||
|
||||
def _check_firmware(self) -> bool:
|
||||
self._buffer[0] = _REG_FW_ID
|
||||
self._read(2)
|
||||
return self._buffer[:2] == b'\x07:'
|
||||
|
||||
def _clear_status_flags(self) -> None:
|
||||
self._buffer[0] = _REG_STATUS
|
||||
self._buffer[1] = 0x00
|
||||
self._write(2)
|
||||
|
||||
def _configure(self) -> None:
|
||||
self._buffer[0] = _REG_SYS_CFG
|
||||
self._buffer[1] = 0x00
|
||||
for idx, val in enumerate(self.handler.cfg):
|
||||
self._buffer[idx + 2] = val
|
||||
self._write(2 + len(self.handler.cfg))
|
||||
|
||||
def _data_ready(self) -> bool:
|
||||
self._buffer[0] = _REG_STATUS
|
||||
self._read(1)
|
||||
return self._buffer[0] != 0x00
|
||||
|
||||
def during_bootup(self, keyboard):
|
||||
if not self._check_firmware:
|
||||
raise OSError('Firmware ID mismatch')
|
||||
|
||||
self._clear_status_flags()
|
||||
self._configure()
|
||||
|
||||
def before_matrix_scan(self, keyboard):
|
||||
pass
|
||||
|
||||
def after_matrix_scan(self, keyboard):
|
||||
if not self._data_ready():
|
||||
return
|
||||
|
||||
self._buffer[0] = _REG_DATA
|
||||
self._read(6)
|
||||
|
||||
self.handler.handle(self._buffer, keyboard)
|
||||
|
||||
self._clear_status_flags()
|
||||
|
||||
def before_hid_send(self, keyboard):
|
||||
pass
|
||||
|
||||
def after_hid_send(self, keyboard):
|
||||
pass
|
||||
|
||||
def on_powersave_enable(self, keyboard):
|
||||
self._buffer[0] = _REG_SYS_CFG
|
||||
self._buffer[1] = 0x04
|
||||
self._write(2)
|
||||
|
||||
def on_powersave_disable(self, keyboard):
|
||||
self._buffer[0] = _REG_SYS_CFG
|
||||
self._buffer[1] = 0x00
|
||||
self._write(2)
|
Loading…
Reference in New Issue
Block a user