rules: add single depress and release options for rule mouse click action
This commit is contained in:
parent
fc38862e8b
commit
90a0408bd6
|
@ -119,6 +119,8 @@ or the window's Window manager class or instance name starts with their string a
|
|||
`Device` and `Active` conditions take one argument, which is the Serial number or Unit ID of a device,
|
||||
as shown in Solaar's detail pane.
|
||||
|
||||
`Host' conditions are true if the computers hostname starts with the condition's argument.
|
||||
|
||||
`Setting` conditions check the value of a Solaar setting on a device.
|
||||
`Setting` conditions take three or four arguments, depending on the setting:
|
||||
the Serial number or Unit ID of a device, as shown in Solaar's detail pane,
|
||||
|
@ -214,6 +216,8 @@ to go wrong under Wayland than under X11.
|
|||
|
||||
A `MouseScroll` action takes a sequence of two numbers and simulates a horizontal and vertical mouse scroll of these amounts.
|
||||
If the previous condition in the parent rule returns a number the scroll amounts are multiplied by this number.
|
||||
A `MouseClick` action takes a mouse button name (`left`, `middle` or `right`) and a positive number or 'click', 'depress', or 'release'.
|
||||
The action simulates that number of clicks of the specified button or just one click, depress, or release of the button.
|
||||
A `MouseClick` action takes a mouse button name (`left`, `middle` or `right`) and a positive number, and simulates that number of clicks of the specified button.
|
||||
An `Execute` action takes a program and arguments and executes it asynchronously.
|
||||
|
||||
|
|
|
@ -88,6 +88,8 @@ _KEY_PRESS = 1
|
|||
_BUTTON_RELEASE = 2
|
||||
_BUTTON_PRESS = 3
|
||||
|
||||
CLICK, DEPRESS, RELEASE = 'click', 'depress', 'release'
|
||||
|
||||
gdisplay = Gdk.Display.get_default() # can be None if Solaar is run without a full window system
|
||||
gkeymap = Gdk.Keymap.get_for_display(gdisplay) if gdisplay else None
|
||||
if _log.isEnabledFor(_INFO):
|
||||
|
@ -312,20 +314,36 @@ def simulate_key(code, event): # X11 keycode but Solaar event code
|
|||
|
||||
|
||||
def click_xtest(button, count):
|
||||
if isinstance(count, int):
|
||||
for _ in range(count):
|
||||
if not simulate_xtest(button[0], _BUTTON_PRESS):
|
||||
return False
|
||||
if not simulate_xtest(button[0], _BUTTON_RELEASE):
|
||||
return False
|
||||
else:
|
||||
if count != RELEASE:
|
||||
if not simulate_xtest(button[0], _BUTTON_PRESS):
|
||||
return False
|
||||
if count != DEPRESS:
|
||||
if not simulate_xtest(button[0], _BUTTON_RELEASE):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def click_uinput(button, count):
|
||||
if isinstance(count, int):
|
||||
for _ in range(count):
|
||||
if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 1):
|
||||
return False
|
||||
if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 0):
|
||||
return False
|
||||
else:
|
||||
if count != RELEASE:
|
||||
if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 1):
|
||||
return False
|
||||
if count != DEPRESS:
|
||||
if not simulate_uinput(evdev.ecodes.EV_KEY, button[1], 0):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
@ -1073,7 +1091,6 @@ class Action(RuleComponent):
|
|||
|
||||
|
||||
class KeyPress(Action):
|
||||
CLICK, DEPRESS, RELEASE = 'click', 'depress', 'release'
|
||||
|
||||
def __init__(self, args, warn=True):
|
||||
self.key_names, self.action = self.regularize_args(args)
|
||||
|
@ -1089,11 +1106,11 @@ class KeyPress(Action):
|
|||
self.key_symbols = []
|
||||
|
||||
def regularize_args(self, args):
|
||||
action = self.CLICK
|
||||
action = CLICK
|
||||
if not isinstance(args, list):
|
||||
args = [args]
|
||||
keys = args
|
||||
if len(args) == 2 and args[1] in [self.CLICK, self.DEPRESS, self.RELEASE]:
|
||||
if len(args) == 2 and args[1] in [CLICK, DEPRESS, RELEASE]:
|
||||
keys = [args[0]] if isinstance(args[0], str) else args[0]
|
||||
action = args[1]
|
||||
return keys, action
|
||||
|
@ -1139,14 +1156,14 @@ class KeyPress(Action):
|
|||
(keycode, level) = self.keysym_to_keycode(k, modifiers)
|
||||
if keycode is None:
|
||||
_log.warn('rule KeyPress key symbol not currently available %s', self)
|
||||
elif self.action != self.CLICK or self.needed(keycode, modifiers): # only check needed when clicking
|
||||
elif self.action != CLICK or self.needed(keycode, modifiers): # only check needed when clicking
|
||||
self.mods(level, modifiers, _KEY_PRESS)
|
||||
simulate_key(keycode, _KEY_PRESS)
|
||||
|
||||
def keyUp(self, keysyms, modifiers):
|
||||
for k in keysyms:
|
||||
(keycode, level) = self.keysym_to_keycode(k, modifiers)
|
||||
if keycode and (self.action != self.CLICK or self.needed(keycode, modifiers)): # only check needed when clicking
|
||||
if keycode and (self.action != CLICK or self.needed(keycode, modifiers)): # only check needed when clicking
|
||||
simulate_key(keycode, _KEY_RELEASE)
|
||||
self.mods(level, modifiers, _KEY_RELEASE)
|
||||
|
||||
|
@ -1155,9 +1172,9 @@ class KeyPress(Action):
|
|||
current = gkeymap.get_modifier_state()
|
||||
if _log.isEnabledFor(_INFO):
|
||||
_log.info('KeyPress action: %s %s, group %s, modifiers %s', self.key_names, self.action, kbdgroup(), current)
|
||||
if self.action != self.RELEASE:
|
||||
if self.action != RELEASE:
|
||||
self.keyDown(self.key_symbols, current)
|
||||
if self.action != self.DEPRESS:
|
||||
if self.action != DEPRESS:
|
||||
self.keyUp(reversed(self.key_symbols), current)
|
||||
_time.sleep(0.01)
|
||||
else:
|
||||
|
@ -1225,8 +1242,10 @@ class MouseClick(Action):
|
|||
try:
|
||||
self.count = int(count)
|
||||
except (ValueError, TypeError):
|
||||
if warn:
|
||||
_log.warn('rule MouseClick action: count %s should be an integer', count)
|
||||
if count in [CLICK, DEPRESS, RELEASE]:
|
||||
self.count = count
|
||||
elif warn:
|
||||
_log.warn('rule MouseClick action: argument %s should be an integer or CLICK, PRESS, or RELEASE', count)
|
||||
self.count = 1
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
@ -29,9 +29,9 @@ from typing import Dict
|
|||
from gi.repository import Gdk, GObject, Gtk
|
||||
from logitech_receiver import diversion as _DIV
|
||||
from logitech_receiver.common import NamedInt, NamedInts, UnsortedNamedInts
|
||||
from logitech_receiver.diversion import CLICK, DEPRESS, RELEASE
|
||||
from logitech_receiver.diversion import XK_KEYS as _XK_KEYS
|
||||
from logitech_receiver.diversion import Key as _Key
|
||||
from logitech_receiver.diversion import KeyPress as _KeyPress
|
||||
from logitech_receiver.diversion import buttons as _buttons
|
||||
from logitech_receiver.hidpp20 import FEATURE as _ALL_FEATURES
|
||||
from logitech_receiver.settings import KIND as _SKIND
|
||||
|
@ -1770,13 +1770,13 @@ class KeyPressUI(ActionUI):
|
|||
self.add_btn.connect('clicked', self._clicked_add)
|
||||
self.widgets[self.add_btn] = (1, 1, 1, 1)
|
||||
self.action_clicked_radio = Gtk.RadioButton.new_with_label_from_widget(None, _('Click'))
|
||||
self.action_clicked_radio.connect('toggled', self._on_update, _KeyPress.CLICK)
|
||||
self.action_clicked_radio.connect('toggled', self._on_update, CLICK)
|
||||
self.widgets[self.action_clicked_radio] = (0, 3, 1, 1)
|
||||
self.action_pressed_radio = Gtk.RadioButton.new_with_label_from_widget(self.action_clicked_radio, _('Depress'))
|
||||
self.action_pressed_radio.connect('toggled', self._on_update, _KeyPress.DEPRESS)
|
||||
self.action_pressed_radio.connect('toggled', self._on_update, DEPRESS)
|
||||
self.widgets[self.action_pressed_radio] = (1, 3, 1, 1)
|
||||
self.action_released_radio = Gtk.RadioButton.new_with_label_from_widget(self.action_pressed_radio, _('Release'))
|
||||
self.action_released_radio.connect('toggled', self._on_update, _KeyPress.RELEASE)
|
||||
self.action_released_radio.connect('toggled', self._on_update, RELEASE)
|
||||
self.widgets[self.action_released_radio] = (2, 3, 1, 1)
|
||||
|
||||
def _create_field(self):
|
||||
|
@ -1836,8 +1836,8 @@ class KeyPressUI(ActionUI):
|
|||
self.del_btns[i].hide()
|
||||
|
||||
def collect_value(self):
|
||||
action = _KeyPress.CLICK if self.action_clicked_radio.get_active() else \
|
||||
_KeyPress.DEPRESS if self.action_pressed_radio.get_active() else _KeyPress.RELEASE
|
||||
action = CLICK if self.action_clicked_radio.get_active() else \
|
||||
DEPRESS if self.action_pressed_radio.get_active() else RELEASE
|
||||
return [[f.get_text().strip() for f in self.fields if f.get_visible()], action]
|
||||
|
||||
@classmethod
|
||||
|
@ -1846,8 +1846,7 @@ class KeyPressUI(ActionUI):
|
|||
|
||||
@classmethod
|
||||
def right_label(cls, component):
|
||||
return ' + '.join(component.key_names
|
||||
) + (' (' + component.action + ')' if component.action != _KeyPress.CLICK else '')
|
||||
return ' + '.join(component.key_names) + (' (' + component.action + ')' if component.action != CLICK else '')
|
||||
|
||||
|
||||
class MouseScrollUI(ActionUI):
|
||||
|
@ -1911,6 +1910,7 @@ class MouseClickUI(ActionUI):
|
|||
MIN_VALUE = 1
|
||||
MAX_VALUE = 9
|
||||
BUTTONS = list(_buttons.keys())
|
||||
ACTIONS = [CLICK, DEPRESS, RELEASE]
|
||||
|
||||
def create_widgets(self):
|
||||
self.widgets = {}
|
||||
|
@ -1919,29 +1919,39 @@ class MouseClickUI(ActionUI):
|
|||
)
|
||||
self.widgets[self.label] = (0, 0, 4, 1)
|
||||
self.label_b = Gtk.Label(label=_('Button'), halign=Gtk.Align.END, valign=Gtk.Align.CENTER, hexpand=True)
|
||||
self.label_c = Gtk.Label(label=_('Count'), halign=Gtk.Align.END, valign=Gtk.Align.CENTER, hexpand=True)
|
||||
self.label_c = Gtk.Label(label=_('Count and Action'), halign=Gtk.Align.END, valign=Gtk.Align.CENTER, hexpand=True)
|
||||
self.field_b = CompletionEntry(self.BUTTONS)
|
||||
self.field_c = Gtk.SpinButton.new_with_range(self.MIN_VALUE, self.MAX_VALUE, 1)
|
||||
self.field_d = CompletionEntry(self.ACTIONS)
|
||||
for f in [self.field_b, self.field_c]:
|
||||
f.set_halign(Gtk.Align.CENTER)
|
||||
f.set_valign(Gtk.Align.START)
|
||||
self.field_b.connect('changed', self._on_update)
|
||||
self.field_c.connect('changed', self._on_update)
|
||||
self.field_d.connect('changed', self._on_update)
|
||||
self.widgets[self.label_b] = (0, 1, 1, 1)
|
||||
self.widgets[self.field_b] = (1, 1, 1, 1)
|
||||
self.widgets[self.label_c] = (2, 1, 1, 1)
|
||||
self.widgets[self.field_c] = (3, 1, 1, 1)
|
||||
self.widgets[self.field_d] = (4, 1, 1, 1)
|
||||
|
||||
def show(self, component, editable):
|
||||
super().show(component, editable)
|
||||
with self.ignore_changes():
|
||||
self.field_b.set_text(component.button)
|
||||
if isinstance(component.count, int):
|
||||
self.field_c.set_value(component.count)
|
||||
self.field_d.set_text(CLICK)
|
||||
else:
|
||||
self.field_c.set_value(1)
|
||||
self.field_d.set_text(component.count)
|
||||
|
||||
def collect_value(self):
|
||||
b, c = self.field_b.get_text(), int(self.field_c.get_value())
|
||||
b, c, d = self.field_b.get_text(), int(self.field_c.get_value()), self.field_d.get_text()
|
||||
if b not in self.BUTTONS:
|
||||
b = 'unknown'
|
||||
if d != CLICK:
|
||||
c = d
|
||||
return [b, c]
|
||||
|
||||
@classmethod
|
||||
|
@ -1950,7 +1960,7 @@ class MouseClickUI(ActionUI):
|
|||
|
||||
@classmethod
|
||||
def right_label(cls, component):
|
||||
return f'{component.button} (x{component.count})'
|
||||
return f'{component.button} ({"x" if isinstance(component.count, int) else ""}{component.count})'
|
||||
|
||||
|
||||
class ExecuteUI(ActionUI):
|
||||
|
|
Loading…
Reference in New Issue