separated trackball handlers from keyboard layers. added key definitions for separated handler selectaion

This commit is contained in:
Björn Bösel 2022-05-25 10:28:54 +02:00 committed by Kyle Brown
parent 80660e4336
commit 9a1fc95f54

View File

@ -1,13 +1,13 @@
''' """
Extension handles usage of Trackball Breakout by Pimoroni Extension handles usage of Trackball Breakout by Pimoroni
Product page: https://shop.pimoroni.com/products/trackball-breakout Product page: https://shop.pimoroni.com/products/trackball-breakout
''' """
from micropython import const
import math import math
import struct import struct
from kmk.keys import make_key from micropython import const
from kmk.keys import make_key, make_argumented_key
from kmk.kmktime import PeriodicTimer from kmk.kmktime import PeriodicTimer
from kmk.modules import Module from kmk.modules import Module
from kmk.modules.mouse_keys import PointingDevice from kmk.modules.mouse_keys import PointingDevice
@ -48,23 +48,28 @@ MSK_CTRL_FWRITE = 0b00001000
ANGLE_OFFSET = 0 ANGLE_OFFSET = 0
class TrackballHandlerKeyMeta:
def __init__(self, handler=0):
self.handler = handler
def layer_key_validator(handler):
return TrackballHandlerKeyMeta(handler=handler)
class TrackballMode: class TrackballMode:
'''Behaviour mode of trackball: mouse movement or vertical scroll''' """Behaviour mode of trackball: mouse movement or vertical scroll"""
MOUSE_MODE = const(0) MOUSE_MODE = const(0)
SCROLL_MODE = const(1) SCROLL_MODE = const(1)
class TrackballLayer: class TrackballHandler:
'''Just to show an interface for layers'''
def handle(self, keyboard, trackball, x, y, switch, state): def handle(self, keyboard, trackball, x, y, switch, state):
raise NotImplementedError raise NotImplementedError
class PointingLayer(TrackballLayer): class PointingHandler(TrackballHandler):
'''the default behavior: act as trackball'''
def handle(self, keyboard, trackball, x, y, switch, state): def handle(self, keyboard, trackball, x, y, switch, state):
if trackball.mode == TrackballMode.MOUSE_MODE: if trackball.mode == TrackballMode.MOUSE_MODE:
if x >= 0: if x >= 0:
@ -95,9 +100,7 @@ class PointingLayer(TrackballLayer):
trackball.previous_state = state trackball.previous_state = state
class KeyLayer(TrackballLayer): class KeyHandler(TrackballHandler):
'''Act like an encoder.'''
x = 0 x = 0
y = 0 y = 0
@ -107,9 +110,7 @@ class KeyLayer(TrackballLayer):
self.down = down self.down = down
self.left = left self.left = left
self.press = press self.press = press
'''snap to axis. the higher the value, the more we snap to the dominant direction'''
self.axis_snap = axis_snap self.axis_snap = axis_snap
'''how many px we move to trigger a key tap, less means more key taps'''
self.steps = steps self.steps = steps
def handle(self, keyboard, trackball, x, y, switch, state): def handle(self, keyboard, trackball, x, y, switch, state):
@ -137,7 +138,7 @@ class KeyLayer(TrackballLayer):
class Trackball(Module): class Trackball(Module):
'''Module handles usage of Trackball Breakout by Pimoroni''' """Module handles usage of Trackball Breakout by Pimoroni"""
def __init__( def __init__(
self, self,
@ -145,39 +146,51 @@ class Trackball(Module):
mode=TrackballMode.MOUSE_MODE, mode=TrackballMode.MOUSE_MODE,
address=I2C_ADDRESS, address=I2C_ADDRESS,
angle_offset=ANGLE_OFFSET, angle_offset=ANGLE_OFFSET,
layers=None, handlers=None,
): ):
self.angle_offset = angle_offset self.angle_offset = angle_offset
if layers is None: if handlers is None or len(handlers) == 0:
layers = [PointingLayer()] handlers = [PointingHandler()]
self._i2c_address = address self._i2c_address = address
self._i2c_bus = i2c self._i2c_bus = i2c
self.pointing_device = PointingDevice() self.pointing_device = PointingDevice()
self.mode = mode self.mode = mode
self.previous_state = False # click state self.previous_state = False # click state
self.layers = layers self.handlers = handlers
self.current_handler = self.handlers[0]
self.polling_interval = 20 self.polling_interval = 20
chip_id = struct.unpack('<H', bytearray(self._i2c_rdwr([REG_CHIP_ID_L], 2)))[0] chip_id = struct.unpack("<H", bytearray(self._i2c_rdwr([REG_CHIP_ID_L], 2)))[0]
if chip_id != CHIP_ID: if chip_id != CHIP_ID:
raise RuntimeError( raise RuntimeError(
'Invalid chip ID: 0x{:04X}, expected 0x{:04X}'.format(chip_id, CHIP_ID) "Invalid chip ID: 0x{:04X}, expected 0x{:04X}".format(chip_id, CHIP_ID)
) )
make_key( make_key(
names=('TB_MODE',), names=("TB_MODE",),
on_press=self._tb_mode_press, on_press=self._tb_mode_press,
on_release=self._tb_mode_press, on_release=self._tb_mode_press,
) )
make_key(
names=("TB_NEXT_HANDLER", "TB_N"),
on_press=self._tb_handler_next_press,
)
make_argumented_key(
validator=layer_key_validator,
names=("TB_HANDLER", "TB_H"),
on_press=self._tb_handler_press,
)
def during_bootup(self, keyboard): def during_bootup(self, keyboard):
self._timer = PeriodicTimer(self.polling_interval) self._timer = PeriodicTimer(self.polling_interval)
def before_matrix_scan(self, keyboard): def before_matrix_scan(self, keyboard):
''' """
Return value will be injected as an extra matrix update Return value will be injected as an extra matrix update
''' """
if not self._timer.tick(): if not self._timer.tick():
return return
@ -185,11 +198,7 @@ class Trackball(Module):
x, y = self._calculate_movement(right - left, down - up) x, y = self._calculate_movement(right - left, down - up)
layer = self.layers[-1] self.current_handler.handle(keyboard, self, x, y, switch, state)
active_layer = keyboard.active_layers[0]
if len(self.layers) > active_layer:
layer = self.layers[active_layer]
layer.handle(keyboard, self, x, y, switch, state)
return return
@ -212,25 +221,40 @@ class Trackball(Module):
return return
def set_rgbw(self, r, g, b, w): def set_rgbw(self, r, g, b, w):
'''Set all LED brightness as RGBW.''' """Set all LED brightness as RGBW."""
self._i2c_rdwr([REG_LED_RED, r, g, b, w]) self._i2c_rdwr([REG_LED_RED, r, g, b, w])
def set_red(self, value): def set_red(self, value):
'''Set brightness of trackball red LED.''' """Set brightness of trackball red LED."""
self._i2c_rdwr([REG_LED_RED, value & 0xFF]) self._i2c_rdwr([REG_LED_RED, value & 0xFF])
def set_green(self, value): def set_green(self, value):
'''Set brightness of trackball green LED.''' """Set brightness of trackball green LED."""
self._i2c_rdwr([REG_LED_GRN, value & 0xFF]) self._i2c_rdwr([REG_LED_GRN, value & 0xFF])
def set_blue(self, value): def set_blue(self, value):
'''Set brightness of trackball blue LED.''' """Set brightness of trackball blue LED."""
self._i2c_rdwr([REG_LED_BLU, value & 0xFF]) self._i2c_rdwr([REG_LED_BLU, value & 0xFF])
def set_white(self, value): def set_white(self, value):
'''Set brightness of trackball white LED.''' """Set brightness of trackball white LED."""
self._i2c_rdwr([REG_LED_WHT, value & 0xFF]) self._i2c_rdwr([REG_LED_WHT, value & 0xFF])
def activate_handler(self, handler):
if isinstance(handler, TrackballHandler):
self.current_handler = handler
else:
try:
self.current_handler = self.handlers[handler]
except KeyError:
print(f"no handler found with id %s" % handler)
def next_handler(self):
next_index = self.handlers.index(self.current_handler) + 1
if next_index >= len(self.handlers):
next_index = 0
self.activate_handler(next_index)
def _clear_pending_hid(self): def _clear_pending_hid(self):
self.pointing_device.hid_pending = False self.pointing_device.hid_pending = False
self.pointing_device.report_x[0] = 0 self.pointing_device.report_x[0] = 0
@ -239,7 +263,7 @@ class Trackball(Module):
self.pointing_device.button_status[0] = 0 self.pointing_device.button_status[0] = 0
def _read_raw_state(self): def _read_raw_state(self):
'''Read up, down, left, right and switch data from trackball.''' """Read up, down, left, right and switch data from trackball."""
left, right, up, down, switch = self._i2c_rdwr([REG_LEFT], 5) left, right, up, down, switch = self._i2c_rdwr([REG_LEFT], 5)
switch, switch_state = ( switch, switch_state = (
switch & ~MSK_SWITCH_STATE, switch & ~MSK_SWITCH_STATE,
@ -248,7 +272,7 @@ class Trackball(Module):
return up, down, left, right, switch, switch_state return up, down, left, right, switch, switch_state
def _i2c_rdwr(self, data, length=0): def _i2c_rdwr(self, data, length=0):
'''Write and optionally read I2C data.''' """Write and optionally read I2C data."""
while not self._i2c_bus.try_lock(): while not self._i2c_bus.try_lock():
pass pass
@ -270,8 +294,14 @@ class Trackball(Module):
def _tb_mode_press(self, key, keyboard, *args, **kwargs): def _tb_mode_press(self, key, keyboard, *args, **kwargs):
self.mode = not self.mode self.mode = not self.mode
def _tb_handler_press(self, key, keyboard, *args, **kwargs):
self.activate_handler(key.meta.handler)
def _tb_handler_next_press(self, key, keyboard, *args, **kwargs):
self.next_handler()
def _calculate_movement(self, raw_x, raw_y): def _calculate_movement(self, raw_x, raw_y):
'''Calculate accelerated movement vector from raw data''' """Calculate accelerated movement vector from raw data"""
if raw_x == 0 and raw_y == 0: if raw_x == 0 and raw_y == 0:
return 0, 0 return 0, 0