Add support for cirque glidepoint trackpad
This commit is contained in:
		
							
								
								
									
										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)
 | 
			
		||||
		Reference in New Issue
	
	Block a user