device: store sliding DPI value in persister and respect changes in regular DPI setting
This commit is contained in:
parent
3effccf390
commit
612e8fb4f7
|
@ -341,7 +341,10 @@ def _feature_smart_shift():
|
||||||
return _Setting(_SMART_SHIFT, _SmartShiftRW(_F.SMART_SHIFT), validator, device_kind=(_DK.mouse, _DK.trackball))
|
return _Setting(_SMART_SHIFT, _SmartShiftRW(_F.SMART_SHIFT), validator, device_kind=(_DK.mouse, _DK.trackball))
|
||||||
|
|
||||||
|
|
||||||
def _feature_dpi_sliding_callback(device):
|
def _feature_dpi_sliding():
|
||||||
|
"""Implements the ability to smoothly modify the DPI
|
||||||
|
by sliding a mouse horizontally while holding the DPI button."""
|
||||||
|
def _feature_dpi_sliding_callback(device):
|
||||||
# need _F.REPROG_CONTROLS_V4 feature and a DPI Switch that can send raw XY
|
# need _F.REPROG_CONTROLS_V4 feature and a DPI Switch that can send raw XY
|
||||||
# and _F.ADJUSTABLE_DPI so that the DPI can be adjusted
|
# and _F.ADJUSTABLE_DPI so that the DPI can be adjusted
|
||||||
if _F.ADJUSTABLE_DPI in device.features:
|
if _F.ADJUSTABLE_DPI in device.features:
|
||||||
|
@ -349,36 +352,27 @@ def _feature_dpi_sliding_callback(device):
|
||||||
if key_index is not None and 'raw XY' in device.keys[key_index].flags:
|
if key_index is not None and 'raw XY' in device.keys[key_index].flags:
|
||||||
return _BooleanV()
|
return _BooleanV()
|
||||||
|
|
||||||
|
|
||||||
def _feature_dpi_sliding():
|
|
||||||
"""Implements the ability to smoothly modify the DPI
|
|
||||||
by sliding a mouse horizontally while holding the DPI button."""
|
|
||||||
class _DpiSlidingRW(object):
|
class _DpiSlidingRW(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# HACK: pretend to be FeatureRW because every setting must have this kind on HID++ 2.0 devices
|
self.kind = _FeatureRW.kind # pretend to be FeatureRW as required for HID++ 2.0 devices
|
||||||
self.kind = _FeatureRW.kind
|
|
||||||
|
|
||||||
def read(self, device):
|
def read(self, device): # need to return bytes, not a boolean
|
||||||
# HACK: the validator requires raw bytes so we pretend the device sent some
|
return b'0x01' if '_slidingDpiState' in device.__dict__ else b'0x00'
|
||||||
return b'\x01' if '_mxVerticalDpiState' in device.__dict__ else b'\x00'
|
|
||||||
|
|
||||||
class MxVerticalDpiState(object):
|
class MxVerticalDpiState(object):
|
||||||
__slots__ = (
|
__slots__ = ('device', 'pressedCids', 'dpiSetting', 'dpiChoices', 'fsmState', 'otherDpiIdx', 'dx', 'movingDpiIdx')
|
||||||
'device', 'pressedCids', 'dpiSetting', 'dpiChoices', 'fsmState', 'dpiSlots', 'dpiSlotChosen', 'dx',
|
|
||||||
'movingDpiIdx'
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, device):
|
def __init__(self, device, dpiSetting):
|
||||||
self.device = device
|
self.device = device
|
||||||
self.dpiSetting = next(filter(lambda s: s.name == _DPI[0], device.settings))
|
self.dpiSetting = dpiSetting
|
||||||
self.dpiChoices = list(self.dpiSetting.choices)
|
self.dpiChoices = list(self.dpiSetting.choices)
|
||||||
# Currently pressed/held control IDs
|
# Currently pressed/held control IDs
|
||||||
self.pressedCids = set()
|
self.pressedCids = set()
|
||||||
self.fsmState = 'idle'
|
self.fsmState = 'idle'
|
||||||
# Two slots for the user's DPI settings. Note these aren't
|
# The DPI setting index for clicking on the DPI button
|
||||||
# the actual values, but rather indices into 'dpiChoices'
|
self.otherDpiIdx = self.device.persister.get('_dpi-sliding', -1) if self.device.persister else -1
|
||||||
self.dpiSlots = [self.dpiChoices.index(self.dpiSetting.read())] * 2
|
if not isinstance(self.otherDpiIdx, int) or self.otherDpiIdx < 0 or self.otherDpiIdx >= len(self.dpiChoices):
|
||||||
self.dpiSlotChosen = 0
|
self.otherDpiIdx = self.dpiChoices.index(self.dpiSetting.read())
|
||||||
# While in 'moved' state, the total accumulated movement
|
# While in 'moved' state, the total accumulated movement
|
||||||
self.dx = 0.
|
self.dx = 0.
|
||||||
# While in 'moved' state, the index into 'dpiChoices' of
|
# While in 'moved' state, the index into 'dpiChoices' of
|
||||||
|
@ -411,12 +405,12 @@ def _feature_dpi_sliding():
|
||||||
self.dpiSetting.write(newDpi)
|
self.dpiSetting.write(newDpi)
|
||||||
from solaar.ui import status_changed as _status_changed
|
from solaar.ui import status_changed as _status_changed
|
||||||
_status_changed(self.device, refresh=True) # update main window
|
_status_changed(self.device, refresh=True) # update main window
|
||||||
self.dpiSlots[self.dpiSlotChosen] = newDpiIdx
|
|
||||||
|
|
||||||
def displayNewDpi(self, newDpiIdx):
|
def displayNewDpi(self, newDpiIdx):
|
||||||
if _notify.available:
|
if _notify.available:
|
||||||
|
reason = f'DPI {self.dpiChoices[newDpiIdx]} [min {self.dpiChoices[0]}, max {self.dpiChoices[-1]}]'
|
||||||
asPercentage = int(float(newDpiIdx) / float(len(self.dpiChoices) - 1) * 100.)
|
asPercentage = int(float(newDpiIdx) / float(len(self.dpiChoices) - 1) * 100.)
|
||||||
_notify.show(self.device, reason=f'{asPercentage}%', progress=asPercentage)
|
_notify.show(self.device, reason=reason, progress=asPercentage)
|
||||||
|
|
||||||
def handle_keys_event(self, cids):
|
def handle_keys_event(self, cids):
|
||||||
if self.fsmState == 'idle':
|
if self.fsmState == 'idle':
|
||||||
|
@ -425,9 +419,11 @@ def _feature_dpi_sliding():
|
||||||
self.dx = 0.
|
self.dx = 0.
|
||||||
elif self.fsmState == 'pressed':
|
elif self.fsmState == 'pressed':
|
||||||
if _special_keys.CONTROL.DPI_Switch not in cids:
|
if _special_keys.CONTROL.DPI_Switch not in cids:
|
||||||
# Swap DPI slots
|
# Swap with other DPI
|
||||||
self.dpiSlotChosen = 1 - self.dpiSlotChosen
|
thisIdx = self.dpiChoices.index(self.dpiSetting.read())
|
||||||
newDpiIdx = self.dpiSlots[self.dpiSlotChosen]
|
newDpiIdx, self.otherDpiIdx = self.otherDpiIdx, thisIdx
|
||||||
|
if self.device.persister:
|
||||||
|
self.device.persister['_dpi-sliding'] = self.otherDpiIdx
|
||||||
self.setNewDpi(newDpiIdx)
|
self.setNewDpi(newDpiIdx)
|
||||||
self.fsmState = 'idle'
|
self.fsmState = 'idle'
|
||||||
self.displayNewDpi(newDpiIdx)
|
self.displayNewDpi(newDpiIdx)
|
||||||
|
@ -437,37 +433,27 @@ def _feature_dpi_sliding():
|
||||||
self.fsmState = 'idle'
|
self.fsmState = 'idle'
|
||||||
|
|
||||||
def handle_move_event(self, dx, dy):
|
def handle_move_event(self, dx, dy):
|
||||||
currDpiIdx = self.dpiSlots[self.dpiSlotChosen]
|
currDpi = self.dpiSetting.read()
|
||||||
currDpi = self.dpiChoices[currDpiIdx]
|
# This multiplier yields a more-or-less DPI-independent dx of about 5/cm
|
||||||
# This multiplier yields a more-or-less DPI-independent total dx of about 5/cm
|
|
||||||
# The multiplier could be configurable to allow adjusting dx
|
# The multiplier could be configurable to allow adjusting dx
|
||||||
dx = float(dx) / float(currDpi) * 15.
|
dx = float(dx) / float(currDpi) * 15.
|
||||||
self.dx += dx
|
self.dx += dx
|
||||||
if self.fsmState == 'pressed':
|
if self.fsmState == 'pressed':
|
||||||
if abs(self.dx) >= 1.:
|
if abs(self.dx) >= 1.:
|
||||||
self.fsmState = 'moved'
|
self.fsmState = 'moved'
|
||||||
self.movingDpiIdx = currDpiIdx
|
self.movingDpiIdx = self.dpiChoices.index(currDpi)
|
||||||
elif self.fsmState == 'moved':
|
elif self.fsmState == 'moved':
|
||||||
currIdx = self.dpiSlots[self.dpiSlotChosen]
|
currIdx = self.dpiChoices.index(self.dpiSetting.read())
|
||||||
newMovingDpiIdx = min(max(currIdx + int(self.dx), 0), len(self.dpiChoices) - 1)
|
newMovingDpiIdx = min(max(currIdx + int(self.dx), 0), len(self.dpiChoices) - 1)
|
||||||
if newMovingDpiIdx != self.movingDpiIdx:
|
if newMovingDpiIdx != self.movingDpiIdx:
|
||||||
self.movingDpiIdx = newMovingDpiIdx
|
self.movingDpiIdx = newMovingDpiIdx
|
||||||
self.displayNewDpi(newMovingDpiIdx)
|
self.displayNewDpi(newMovingDpiIdx)
|
||||||
|
|
||||||
def write(self, device, data_bytes):
|
def write(self, device, data_bytes):
|
||||||
if bool(data_bytes): # Enable DPI sliding
|
|
||||||
# Enable HID++ events on sliding the mouse while DPI button held
|
|
||||||
DPI_key_index = device.keys.index(_special_keys.CONTROL.DPI_Switch)
|
|
||||||
DPI_key = device.keys[DPI_key_index] if DPI_key_index is not None else None
|
|
||||||
if DPI_key and 'raw XY' in DPI_key.flags:
|
|
||||||
DPI_key.set_rawXY_reporting(True)
|
|
||||||
# Store our variables in the device object
|
|
||||||
device._mxVerticalDpiState = self.MxVerticalDpiState(device)
|
|
||||||
|
|
||||||
def handler(device, n):
|
def handler(device, n):
|
||||||
"""Called on notification events from the mouse."""
|
"""Called on notification events from the mouse."""
|
||||||
if n.sub_id < 0x40 and device.features[n.sub_id] == _F.REPROG_CONTROLS_V4:
|
if n.sub_id < 0x40 and device.features[n.sub_id] == _F.REPROG_CONTROLS_V4:
|
||||||
state = device._mxVerticalDpiState
|
state = device._slidingDpiState
|
||||||
if n.address == 0x00:
|
if n.address == 0x00:
|
||||||
cid1, cid2, cid3, cid4 = _unpack('!HHHH', n.data[:8])
|
cid1, cid2, cid3, cid4 = _unpack('!HHHH', n.data[:8])
|
||||||
state.handle_keys_event({cid1, cid2, cid3, cid4})
|
state.handle_keys_event({cid1, cid2, cid3, cid4})
|
||||||
|
@ -475,16 +461,26 @@ def _feature_dpi_sliding():
|
||||||
dx, dy = _unpack('!hh', n.data[:4])
|
dx, dy = _unpack('!hh', n.data[:4])
|
||||||
state.handle_move_event(dx, dy)
|
state.handle_move_event(dx, dy)
|
||||||
|
|
||||||
|
if bool(data_bytes): # Enable DPI sliding
|
||||||
|
# Enable HID++ events on sliding the mouse while DPI button held
|
||||||
|
dpiSetting = next(filter(lambda s: s.name == _DPI[0], device.settings), None)
|
||||||
|
DPI_key_index = device.keys.index(_special_keys.CONTROL.DPI_Switch)
|
||||||
|
DPI_key = device.keys[DPI_key_index] if DPI_key_index is not None else None
|
||||||
|
if dpiSetting and DPI_key and 'raw XY' in DPI_key.flags:
|
||||||
|
DPI_key.set_rawXY_reporting(True)
|
||||||
|
# Store our variables in the device object
|
||||||
|
device._slidingDpiState = self.MxVerticalDpiState(device, dpiSetting)
|
||||||
device.add_notification_handler('mx-vertical-dpi-handler', handler)
|
device.add_notification_handler('mx-vertical-dpi-handler', handler)
|
||||||
|
else:
|
||||||
|
_log.error('cannot enable DPI sliding on %s', device)
|
||||||
else: # Disable DPI sliding
|
else: # Disable DPI sliding
|
||||||
try:
|
try:
|
||||||
device.remove_notification_handler('mx-vertical-dpi-handler')
|
device.remove_notification_handler('mx-vertical-dpi-handler')
|
||||||
del device._mxVerticalDpiState
|
del device._slidingDpiState
|
||||||
DPI_key = device.keys[device.keys.index(_special_keys.CONTROL.DPI_Switch)]
|
DPI_key = device.keys[device.keys.index(_special_keys.CONTROL.DPI_Switch)]
|
||||||
DPI_key.set_rawXY_reporting(False)
|
DPI_key.set_rawXY_reporting(False)
|
||||||
except Exception:
|
except Exception:
|
||||||
_log.error('cannot disable DPI sliding on %s', device)
|
_log.error('cannot disable DPI sliding on %s', device)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return _Setting(_DPI_SLIDING, _DpiSlidingRW(), callback=_feature_dpi_sliding_callback, device_kind=(_DK.mouse, ))
|
return _Setting(_DPI_SLIDING, _DpiSlidingRW(), callback=_feature_dpi_sliding_callback, device_kind=(_DK.mouse, ))
|
||||||
|
|
Loading…
Reference in New Issue