settings: add setting for HAPTIC feature
This commit is contained in:
parent
97b6b958c8
commit
0fd262424e
|
|
@ -66,6 +66,7 @@ class SupportedFeature(IntEnum):
|
||||||
BACKLIGHT3 = 0x1983
|
BACKLIGHT3 = 0x1983
|
||||||
ILLUMINATION = 0x1990
|
ILLUMINATION = 0x1990
|
||||||
FORCE_SENSING_BUTTON = 0x19C0
|
FORCE_SENSING_BUTTON = 0x19C0
|
||||||
|
HAPTIC = 0x19B0
|
||||||
PRESENTER_CONTROL = 0x1A00
|
PRESENTER_CONTROL = 0x1A00
|
||||||
SENSOR_3D = 0x1A01
|
SENSOR_3D = 0x1A01
|
||||||
REPROG_CONTROLS = 0x1B00
|
REPROG_CONTROLS = 0x1B00
|
||||||
|
|
@ -277,3 +278,23 @@ class ParamId(IntEnum):
|
||||||
PIXEL_ZONE = 2 # 4 2-byte integers, left, bottom, width, height; pixels
|
PIXEL_ZONE = 2 # 4 2-byte integers, left, bottom, width, height; pixels
|
||||||
RATIO_ZONE = 3 # 4 bytes, left, bottom, width, height; unit 1/240 pad size
|
RATIO_ZONE = 3 # 4 bytes, left, bottom, width, height; unit 1/240 pad size
|
||||||
SCALE_FACTOR = 4 # 2-byte integer, with 256 as normal scale
|
SCALE_FACTOR = 4 # 2-byte integer, with 256 as normal scale
|
||||||
|
|
||||||
|
|
||||||
|
HapticWaveForms = NamedInts(
|
||||||
|
SHARP_STATE_CHANGE=0x00,
|
||||||
|
DAMP_STATE_CHANGE=0x01,
|
||||||
|
SHARP_COLLISION=0x02,
|
||||||
|
DAMP_COLLISION=0x03,
|
||||||
|
SUBTLE_COLLISION=0x04,
|
||||||
|
HAPPY_ALERT=0x05,
|
||||||
|
ANGRY_ALERT=0x06,
|
||||||
|
COMPLETED=0x07,
|
||||||
|
SQUARE=0x08,
|
||||||
|
WAVE=0x09,
|
||||||
|
FIREWORK=0x0A,
|
||||||
|
MAD=0x0B,
|
||||||
|
KNOCK=0x0C,
|
||||||
|
JINGLE=0x0D,
|
||||||
|
RINGING=0xE,
|
||||||
|
WHISPER_COLLISION=0x1B,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ class Setting:
|
||||||
rw_options = {}
|
rw_options = {}
|
||||||
validator_class = None
|
validator_class = None
|
||||||
validator_options = {}
|
validator_options = {}
|
||||||
|
display = True # display setting in UI
|
||||||
|
|
||||||
def __init__(self, device, rw, validator):
|
def __init__(self, device, rw, validator):
|
||||||
self._device = device
|
self._device = device
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ class Setting:
|
||||||
choices_universe = None # All possible acceptable keys, for settings with keys
|
choices_universe = None # All possible acceptable keys, for settings with keys
|
||||||
kind = Kind.NONE # What GUI interface to use
|
kind = Kind.NONE # What GUI interface to use
|
||||||
persist = True # Whether to remember the setting
|
persist = True # Whether to remember the setting
|
||||||
|
display = True # display setting in UI
|
||||||
_device = None # The device that this setting is for
|
_device = None # The device that this setting is for
|
||||||
_device_object = None # The object that interacts with the feature for the device
|
_device_object = None # The object that interacts with the feature for the device
|
||||||
_value = None # Stored value as maintained by Solaar, used for persistence
|
_value = None # Stored value as maintained by Solaar, used for persistence
|
||||||
|
|
|
||||||
|
|
@ -1809,6 +1809,72 @@ class ForceSensing(settings_new.Settings):
|
||||||
return setting
|
return setting
|
||||||
|
|
||||||
|
|
||||||
|
class HapticLevel(settings.Setting):
|
||||||
|
name = "haptic-level"
|
||||||
|
label = _("Haptic Feeback Level")
|
||||||
|
description = _("Change power of haptic feedback. (Zero to turn off.)")
|
||||||
|
feature = _F.HAPTIC
|
||||||
|
choices_universe = common.NamedInts(Off=0, Low=25, Medium=50, High=75, Maximum=100)
|
||||||
|
min_value = 0
|
||||||
|
max_value = 100
|
||||||
|
|
||||||
|
class rw_class(settings.FeatureRW):
|
||||||
|
def __init__(self, feature):
|
||||||
|
super().__init__(feature, read_fnid=0x10, write_fnid=0x20)
|
||||||
|
|
||||||
|
def read(self, device, data_bytes=b""):
|
||||||
|
result = device.feature_request(self.feature, 0x10)
|
||||||
|
if result[0] & 0x01 == 0: # disabled, return 0
|
||||||
|
return b"\x00"
|
||||||
|
else: # enabled, return second byte
|
||||||
|
return result[1:2]
|
||||||
|
|
||||||
|
def write(self, device, data_bytes):
|
||||||
|
if data_bytes == b"\x00":
|
||||||
|
write_bytes = b"\x00\x32" # disable, at 50 percent
|
||||||
|
else:
|
||||||
|
write_bytes = b"\x01" + data_bytes
|
||||||
|
reply = device.feature_request(self.feature, 0x20, write_bytes)
|
||||||
|
return reply
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def build(cls, device):
|
||||||
|
response = device.feature_request(cls.feature, 0x10)
|
||||||
|
if response:
|
||||||
|
rw = cls.rw_class(cls.feature)
|
||||||
|
levels = response[2] & 0x01
|
||||||
|
if levels: # device only has four levels
|
||||||
|
validator = settings_validator.ChoicesValidator(choices=cls.choices_universe)
|
||||||
|
else: # device has all levels
|
||||||
|
validator = settings_validator.RangeValidator(min_value=cls.min_value, max_value=cls.max_value)
|
||||||
|
return cls(device, rw, validator)
|
||||||
|
|
||||||
|
|
||||||
|
# This setting is not displayed in the UI
|
||||||
|
# Use `solaar config <device> haptic-play <form>` to play a haptic form
|
||||||
|
class PlayHapticWaveForm(settings.Setting):
|
||||||
|
name = "haptic-play"
|
||||||
|
label = _("Play Haptic Waveform")
|
||||||
|
description = _("Tell device to play a haptic waveform.")
|
||||||
|
feature = _F.HAPTIC
|
||||||
|
choices_universe = hidpp20_constants.HapticWaveForms
|
||||||
|
rw_options = {"read_fnid": None, "write_fnid": 0x40} # nothing to read
|
||||||
|
persist = False # persisting this setting is useless
|
||||||
|
display = False # don't display in UI, interact using `solaar config ...`
|
||||||
|
|
||||||
|
class validator_class(settings_validator.ChoicesValidator):
|
||||||
|
@classmethod
|
||||||
|
def build(cls, setting_class, device):
|
||||||
|
response = device.feature_request(_F.HAPTIC, 0x00)
|
||||||
|
if response:
|
||||||
|
waves = common.NamedInts()
|
||||||
|
waveforms = int.from_bytes(response[4:8])
|
||||||
|
for waveform in hidpp20_constants.HapticWaveForms:
|
||||||
|
if (1 << int(waveform)) & waveforms:
|
||||||
|
waves[int(waveform)] = str(waveform)
|
||||||
|
return cls(choices=waves, byte_count=1)
|
||||||
|
|
||||||
|
|
||||||
SETTINGS: list[settings.Setting] = [
|
SETTINGS: list[settings.Setting] = [
|
||||||
RegisterHandDetection, # simple
|
RegisterHandDetection, # simple
|
||||||
RegisterSmoothScroll, # simple
|
RegisterSmoothScroll, # simple
|
||||||
|
|
@ -1866,6 +1932,8 @@ SETTINGS: list[settings.Setting] = [
|
||||||
Gesture2Gestures, # working
|
Gesture2Gestures, # working
|
||||||
Gesture2Divert,
|
Gesture2Divert,
|
||||||
Gesture2Params, # working
|
Gesture2Params, # working
|
||||||
|
HapticLevel,
|
||||||
|
PlayHapticWaveForm,
|
||||||
Sidetone,
|
Sidetone,
|
||||||
Equalizer,
|
Equalizer,
|
||||||
ADCPower,
|
ADCPower,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from threading import Timer
|
from threading import Timer
|
||||||
|
|
@ -70,7 +69,6 @@ def _write_async(setting, value, sbox, sensitive=True, key=None):
|
||||||
v = setting.write_key_value(key, v)
|
v = setting.write_key_value(key, v)
|
||||||
v = {key: v}
|
v = {key: v}
|
||||||
except Exception:
|
except Exception:
|
||||||
traceback.print_exc()
|
|
||||||
v = None
|
v = None
|
||||||
if sb:
|
if sb:
|
||||||
GLib.idle_add(_update_setting_item, sb, v, True, sensitive, priority=99)
|
GLib.idle_add(_update_setting_item, sb, v, True, sensitive, priority=99)
|
||||||
|
|
@ -660,6 +658,8 @@ def _change_icon(allowed, icon):
|
||||||
|
|
||||||
|
|
||||||
def _create_sbox(s, _device):
|
def _create_sbox(s, _device):
|
||||||
|
if not s.display:
|
||||||
|
return
|
||||||
sbox = Gtk.HBox(homogeneous=False, spacing=6)
|
sbox = Gtk.HBox(homogeneous=False, spacing=6)
|
||||||
sbox.setting = s
|
sbox.setting = s
|
||||||
sbox.kind = s.kind
|
sbox.kind = s.kind
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue