rules: remove use of pynput

This commit is contained in:
Peter F. Patel-Schneider 2020-11-25 20:54:43 -05:00
parent 808a719823
commit 244d48d245
4 changed files with 43 additions and 25 deletions

View File

@ -30,7 +30,7 @@ in Fedora you need `gtk3` and `python3-gobject`;
if you're using another if you're using another
distribution the required packages are most likely named something similar. distribution the required packages are most likely named something similar.
The Solaar GUI also requires Python packages The Solaar GUI also requires Python packages
`PyYAML` (>= 5.1), `python-xlib` (>= 0.27), `pynput` (>= 1.7.0), and `psutil` (>= 5.7.3). `PyYAML` (>= 5.1), `python-xlib` (>= 0.27), and `psutil` (>= 5.7.3).
These are best installed using `pip` via `pip install --user <package>` These are best installed using `pip` via `pip install --user <package>`
if they are not already available. if they are not already available.
You may have to install the `gcc` and the Python development package (`python3-dev` or `python3-devel`, You may have to install the `gcc` and the Python development package (`python3-dev` or `python3-devel`,

View File

@ -27,8 +27,6 @@ import _thread
import psutil import psutil
import Xlib import Xlib
from pynput import keyboard as _keyboard
from pynput import mouse as _mouse
from Xlib import X from Xlib import X
from Xlib.display import Display from Xlib.display import Display
from Xlib.ext import record from Xlib.ext import record
@ -125,9 +123,6 @@ _thread.start_new_thread(display.record_enable_context, (context, key_press_hand
# See docs/rules.md for documentation # See docs/rules.md for documentation
keyboard = _keyboard.Controller()
mouse = _mouse.Controller()
keys_down = [] keys_down = []
key_down = None key_down = None
@ -157,6 +152,8 @@ TESTS = {
COMPONENTS = {} COMPONENTS = {}
displayt = Display()
class RuleComponent(object): class RuleComponent(object):
def compile(self, c): def compile(self, c):
@ -411,7 +408,7 @@ class KeyPress(Action):
if isinstance(keys, str): if isinstance(keys, str):
keys = [keys] keys = [keys]
self.key_symbols = keys self.key_symbols = keys
key_from_string = lambda s: s if isinstance(s, str) and len(s) == 1 else _keyboard.KeyCode._from_symbol(s) key_from_string = lambda s: displayt.keysym_to_keycode(Xlib.XK.string_to_keysym(s))
self.keys = [isinstance(k, str) and key_from_string(k) for k in keys] self.keys = [isinstance(k, str) and key_from_string(k) for k in keys]
if not all(self.keys): if not all(self.keys):
_log.warn('rule KeyPress argument not sequence of key names %s', keys) _log.warn('rule KeyPress argument not sequence of key names %s', keys)
@ -421,30 +418,26 @@ class KeyPress(Action):
return 'KeyPress: ' + ' '.join(self.key_symbols) return 'KeyPress: ' + ' '.join(self.key_symbols)
def needed(self, k, current_key_modifiers): def needed(self, k, current_key_modifiers):
code = modifier_code(display.keysym_to_keycode(k.vk if isinstance(k, _keyboard.KeyCode) else k)) code = modifier_code(k)
return not (code and current_key_modifiers & (1 << code)) return not (code and current_key_modifiers & (1 << code))
def keyDown(self, keys, modifiers): def keyDown(self, keys, modifiers):
for k in keys: for k in keys:
if self.needed(k, modifiers): if self.needed(k, modifiers):
keyboard.press(k) Xlib.ext.xtest.fake_input(displayt, X.KeyPress, k)
def keyUp(self, keys, modifiers): def keyUp(self, keys, modifiers):
for k in keys: for k in keys:
if self.needed(k, modifiers): if self.needed(k, modifiers):
keyboard.release(k) Xlib.ext.xtest.fake_input(displayt, X.KeyRelease, k)
def evaluate(self, feature, notification, device, status, last_result): def evaluate(self, feature, notification, device, status, last_result):
current = current_key_modifiers current = current_key_modifiers
if _log.isEnabledFor(_INFO): if _log.isEnabledFor(_INFO):
_log.info( _log.info('KeyPress action: %s, modifiers %s %s', self.key_symbols, current, [hex(k) for k in self.keys])
'KeyPress action: %s, modifiers %s %s', self.key_symbols, current,
[(hex(k.vk) if isinstance(k, _keyboard.KeyCode) else k) for k in self.keys]
)
self.keyDown(self.keys, current) self.keyDown(self.keys, current)
import time
time.sleep(0.1)
self.keyUp(reversed(self.keys), current) self.keyUp(reversed(self.keys), current)
displayt.sync()
return None return None
def data(self): def data(self):
@ -459,6 +452,25 @@ class KeyPress(Action):
# def evaluate(self, feature, notification, device, status, last_result): # def evaluate(self, feature, notification, device, status, last_result):
# super().keyUp(self.keys, current_key_modifiers) # super().keyUp(self.keys, current_key_modifiers)
buttons = {
'unknown': None,
'left': 1,
'middle': 2,
'right': 3,
'scroll_up': 4,
'scroll_down': 5,
'scroll_left': 6,
'scroll_right': 7
}
for i in range(8, 31):
buttons['button%d' % i] = i
def click(button, count):
for _ in range(count):
Xlib.ext.xtest.fake_input(displayt, Xlib.X.ButtonPress, button)
Xlib.ext.xtest.fake_input(displayt, Xlib.X.ButtonRelease, button)
class MouseScroll(Action): class MouseScroll(Action):
def __init__(self, amounts): def __init__(self, amounts):
@ -481,7 +493,12 @@ class MouseScroll(Action):
amounts = [math.floor(last_result * a) for a in self.amounts] amounts = [math.floor(last_result * a) for a in self.amounts]
if _log.isEnabledFor(_INFO): if _log.isEnabledFor(_INFO):
_log.info('MouseScroll action: %s %s %s', self.amounts, last_result, amounts) _log.info('MouseScroll action: %s %s %s', self.amounts, last_result, amounts)
mouse.scroll(*amounts) dx, dy = amounts
if dx:
click(button=buttons['scroll_right'] if dx > 0 else buttons['scroll_left'], count=abs(dx))
if dy:
click(button=buttons['scroll_up'] if dy > 0 else buttons['scroll_down'], count=abs(dy))
displayt.sync()
return None return None
def data(self): def data(self):
@ -494,11 +511,11 @@ class MouseClick(Action):
args = args[0] args = args[0]
if not isinstance(args, list): if not isinstance(args, list):
args = [args] args = [args]
self.button = str(args[0]) if len(args) >= 0 else 'unknown' self.button = str(args[0]) if len(args) >= 0 else None
if not hasattr(_mouse.Button, self.button): if self.button not in buttons:
_log.warn('rule MouseClick action: button %s not known', self.button) _log.warn('rule MouseClick action: button %s not known', self.button)
self.button = 'unknown' self.button = None
count = args[1] if len(args) >= 1 else 1 count = args[1] if len(args) >= 2 else 1
try: try:
self.count = int(count) self.count = int(count)
except ValueError | TypeError: except ValueError | TypeError:
@ -511,7 +528,9 @@ class MouseClick(Action):
def evaluate(self, feature, notification, device, status, last_result): def evaluate(self, feature, notification, device, status, last_result):
if _log.isEnabledFor(_INFO): if _log.isEnabledFor(_INFO):
_log.info('MouseClick action: %d %s' % (self.count, self.button)) _log.info('MouseClick action: %d %s' % (self.count, self.button))
mouse.click(getattr(_mouse.Button, self.button), self.count) if self.button and self.count:
click(buttons[self.button], self.count)
displayt.sync()
return None return None
def data(self): def data(self):

View File

@ -28,9 +28,9 @@ import Xlib.XK
from gi.repository import Gdk, GObject, Gtk from gi.repository import Gdk, GObject, Gtk
from logitech_receiver import diversion as _DIV from logitech_receiver import diversion as _DIV
from logitech_receiver.diversion import buttons as _buttons
from logitech_receiver.hidpp20 import FEATURE as _ALL_FEATURES from logitech_receiver.hidpp20 import FEATURE as _ALL_FEATURES
from logitech_receiver.special_keys import CONTROL as _CONTROL from logitech_receiver.special_keys import CONTROL as _CONTROL
from pynput import mouse as _mouse
from solaar.i18n import _ from solaar.i18n import _
_log = getLogger(__name__) _log = getLogger(__name__)
@ -1218,7 +1218,7 @@ class MouseClickUI(ActionUI):
CLASS = _DIV.MouseClick CLASS = _DIV.MouseClick
MIN_VALUE = 1 MIN_VALUE = 1
MAX_VALUE = 9 MAX_VALUE = 9
BUTTONS = [b for b in dir(_mouse.Button) if not b.startswith('__')] BUTTONS = list(_buttons.keys())
def create_widgets(self): def create_widgets(self):
self.widgets = {} self.widgets = {}

View File

@ -63,7 +63,6 @@ battery status, and show and modify some of the modifiable features of devices.
'pyudev (>= 0.13)', 'pyudev (>= 0.13)',
'PyYAML (>= 5.1)', 'PyYAML (>= 5.1)',
'python-xlib (>= 0.27)', 'python-xlib (>= 0.27)',
'pynput (>= 1.7.0)',
'psutil (>= 5.7.3)', 'psutil (>= 5.7.3)',
], ],
package_dir={'': 'lib'}, package_dir={'': 'lib'},