feat: hi-res scroll action
This commit is contained in:
parent
898349c403
commit
3359ee374c
|
|
@ -246,7 +246,12 @@ if evdev:
|
|||
key_events.append(evcode)
|
||||
devicecap = {
|
||||
evdev.ecodes.EV_KEY: key_events,
|
||||
evdev.ecodes.EV_REL: [evdev.ecodes.REL_WHEEL, evdev.ecodes.REL_HWHEEL],
|
||||
evdev.ecodes.EV_REL: [
|
||||
evdev.ecodes.REL_WHEEL,
|
||||
evdev.ecodes.REL_HWHEEL,
|
||||
evdev.ecodes.REL_WHEEL_HI_RES,
|
||||
evdev.ecodes.REL_HWHEEL_HI_RES,
|
||||
],
|
||||
}
|
||||
else:
|
||||
# Just mock these since they won't be useful without evdev anyway
|
||||
|
|
@ -360,15 +365,23 @@ def click(button, count):
|
|||
return False
|
||||
|
||||
|
||||
def simulate_scroll(dx, dy):
|
||||
def simulate_scroll(dx, dy, hi_res_dx=None, hi_res_dy=None):
|
||||
hi_res_dx = hi_res_dx or dx * 120
|
||||
hi_res_dy = hi_res_dy or dy * 120
|
||||
if setup_uinput():
|
||||
success = True
|
||||
if dx:
|
||||
success = simulate_uinput(evdev.ecodes.EV_REL, evdev.ecodes.REL_HWHEEL, dx)
|
||||
if dy and success:
|
||||
success = simulate_uinput(evdev.ecodes.EV_REL, evdev.ecodes.REL_WHEEL, dy)
|
||||
if success:
|
||||
return True
|
||||
for code, delta in (
|
||||
(evdev.ecodes.REL_HWHEEL_HI_RES, hi_res_dx),
|
||||
(evdev.ecodes.REL_WHEEL_HI_RES, hi_res_dy),
|
||||
(evdev.ecodes.REL_HWHEEL, dx),
|
||||
(evdev.ecodes.REL_WHEEL, dy),
|
||||
):
|
||||
if delta:
|
||||
success = simulate_uinput(evdev.ecodes.EV_REL, code, delta)
|
||||
if not success:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
logger.warning("no way to simulate scrolling")
|
||||
|
||||
|
||||
|
|
@ -621,8 +634,7 @@ class Process(Condition):
|
|||
if (not wayland and not x11_setup()) or (wayland and not gnome_dbus_interface_setup()):
|
||||
if warn:
|
||||
logger.warning(
|
||||
"rules can only access active process in X11 or in Wayland under GNOME with Solaar Gnome "
|
||||
"extension - %s",
|
||||
"rules can only access active process in X11 or in Wayland under GNOME with Solaar Gnome extension - %s",
|
||||
self,
|
||||
)
|
||||
if not isinstance(process, str):
|
||||
|
|
@ -1218,26 +1230,45 @@ class MouseScroll(Action):
|
|||
amounts = amounts[0]
|
||||
if not (len(amounts) == 2 and all([isinstance(a, numbers.Number) for a in amounts])):
|
||||
if warn:
|
||||
logger.warning("rule MouseScroll argument not two numbers %s", amounts)
|
||||
logger.warning(f"rule {self.__class__.__name__} argument not two numbers {amounts}")
|
||||
amounts = [0, 0]
|
||||
self.amounts = amounts
|
||||
|
||||
def __str__(self):
|
||||
return "MouseScroll: " + " ".join([str(a) for a in self.amounts])
|
||||
return f"{self.__class__.__name__}: " + " ".join([str(a) for a in self.amounts])
|
||||
|
||||
def evaluate(self, feature, notification: HIDPPNotification, device, last_result):
|
||||
amounts = self.amounts
|
||||
if isinstance(last_result, numbers.Number):
|
||||
amounts = [math.floor(last_result * a) for a in self.amounts]
|
||||
if logger.isEnabledFor(logging.INFO):
|
||||
logger.info("MouseScroll action: %s %s %s", self.amounts, last_result, amounts)
|
||||
logger.info(f"{self.__class__.__name__} action: {self.amounts} {last_result} {amounts}")
|
||||
dx, dy = amounts
|
||||
simulate_scroll(dx, dy)
|
||||
self._scroll(dx, dy)
|
||||
time.sleep(0.01)
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _scroll(cls, dx, dy):
|
||||
simulate_scroll(dx, dy)
|
||||
|
||||
def data(self):
|
||||
return {"MouseScroll": self.amounts[:]}
|
||||
return {f"{self.__class__.__name__}": self.amounts[:]}
|
||||
|
||||
|
||||
class MouseScrollHiRes(MouseScroll):
|
||||
accumulated_x = 0
|
||||
accumulated_y = 0
|
||||
|
||||
@classmethod
|
||||
def _scroll(cls, hi_res_dx, hi_res_dy):
|
||||
cls.accumulated_x += hi_res_dx
|
||||
cls.accumulated_y += hi_res_dy
|
||||
dx = cls.accumulated_x // 120
|
||||
dy = cls.accumulated_y // 120
|
||||
cls.accumulated_x %= 120
|
||||
cls.accumulated_y %= 120
|
||||
simulate_scroll(dx, dy, hi_res_dx, hi_res_dy)
|
||||
|
||||
|
||||
class MouseClick(Action):
|
||||
|
|
@ -1405,6 +1436,7 @@ COMPONENTS = {
|
|||
"Host": Host,
|
||||
"KeyPress": KeyPress,
|
||||
"MouseScroll": MouseScroll,
|
||||
"MouseScrollHiRes": MouseScrollHiRes,
|
||||
"MouseClick": MouseClick,
|
||||
"Set": Set,
|
||||
"Execute": Execute,
|
||||
|
|
|
|||
|
|
@ -407,6 +407,7 @@ class ActionMenu:
|
|||
[
|
||||
(_("Key press"), diversion.KeyPress, "space"),
|
||||
(_("Mouse scroll"), diversion.MouseScroll, [0, 0]),
|
||||
(_("Mouse scroll (hi-res)"), diversion.MouseScrollHiRes, [0, 0]),
|
||||
(_("Mouse click"), diversion.MouseClick, ["left", 1]),
|
||||
(_("Set"), diversion.Set, [None, "", None]),
|
||||
(_("Execute"), diversion.Execute, [""]),
|
||||
|
|
@ -1865,6 +1866,7 @@ COMPONENT_UI: dict[Any, RuleComponentUI] = {
|
|||
diversion.MouseGesture: rule_conditions.MouseGestureUI,
|
||||
diversion.KeyPress: rule_actions.KeyPressUI,
|
||||
diversion.MouseScroll: rule_actions.MouseScrollUI,
|
||||
diversion.MouseScrollHiRes: rule_actions.MouseScrollHiResUI,
|
||||
diversion.MouseClick: rule_actions.MouseClickUI,
|
||||
diversion.Execute: rule_actions.ExecuteUI,
|
||||
diversion.Set: SetUI,
|
||||
|
|
|
|||
|
|
@ -198,6 +198,24 @@ class MouseScrollUI(ActionUI):
|
|||
return f"{x}, {y}"
|
||||
|
||||
|
||||
class MouseScrollHiResUI(MouseScrollUI):
|
||||
CLASS = diversion.MouseScrollHiRes
|
||||
|
||||
def create_widgets(self):
|
||||
super().create_widgets()
|
||||
self.label.set_text(
|
||||
_(
|
||||
"Simulate a high-resolution scroll.\n"
|
||||
'One detent click, or "regular" scroll unit, is represented by 120 high resolution scroll units.\n'
|
||||
"Always requires write access to /dev/uinput."
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def left_label(cls, component):
|
||||
return _("Mouse scroll (hi-res)")
|
||||
|
||||
|
||||
class MouseClickUI(ActionUI):
|
||||
CLASS = diversion.MouseClick
|
||||
MIN_VALUE = 1
|
||||
|
|
|
|||
Loading…
Reference in New Issue