Add support for process condition under wayland using solaar-gnome-extension (#2101)

* Add support for process condition under wayland using solaar-gnome-extension

* Fix typo

* Improvements

* Rename dbus extension

* Final fixes

* Fix style checks

* More styling fixes

* More fixes

* More fixes

---------

Co-authored-by: Peter F. Patel-Schneider <pfpschneider@gmail.com>
This commit is contained in:
Swapnil Devesh 2023-07-26 16:23:57 +05:30 committed by GitHub
parent 4160b0e74f
commit 91f1894e8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 49 additions and 8 deletions

View File

@ -33,7 +33,7 @@ The `udev` package must be installed and its daemon running.
Solaar requires Python 3.7+ and requires several packages to be installed. Solaar requires Python 3.7+ and requires several packages to be installed.
If you are running the system version of Python you should have the If you are running the system version of Python you should have the
`python3-pyudev`, `python3-psutil`, `python3-xlib`, `python3-evdev`, `python3-typing-extensions`, `python3-pyudev`, `python3-psutil`, `python3-xlib`, `python3-evdev`, `python3-typing-extensions`, `dbus-python`,
and `python3-yaml` or `python3-pyyaml` packages installed. and `python3-yaml` or `python3-pyyaml` packages installed.
To run the GUI Solaar also requires Gtk3 and its GObject introspection bindings. To run the GUI Solaar also requires Gtk3 and its GObject introspection bindings.

View File

@ -28,6 +28,7 @@ from logging import getLogger
from math import sqrt as _sqrt from math import sqrt as _sqrt
from struct import unpack as _unpack from struct import unpack as _unpack
import dbus
import evdev import evdev
import keysyms.keysymdef as _keysymdef import keysyms.keysymdef as _keysymdef
import psutil import psutil
@ -94,7 +95,10 @@ if _log.isEnabledFor(_INFO):
wayland = _os.getenv('WAYLAND_DISPLAY') # is this Wayland? wayland = _os.getenv('WAYLAND_DISPLAY') # is this Wayland?
if wayland: if wayland:
_log.warn('rules cannot access active process or modifier keys in Wayland') _log.warn(
'rules cannot access modifier keys in Wayland, '
'accessing process only works on GNOME with Solaar Gnome extension installed'
)
try: try:
import Xlib import Xlib
@ -108,6 +112,8 @@ Xkbdisplay = None # xkb might be available
modifier_keycodes = [] modifier_keycodes = []
XkbUseCoreKbd = 0x100 XkbUseCoreKbd = 0x100
_dbus_interface = None
class XkbDisplay(_ctypes.Structure): class XkbDisplay(_ctypes.Structure):
""" opaque struct """ """ opaque struct """
@ -143,6 +149,20 @@ def x11_setup():
return _x11 return _x11
def gnome_dbus_interface_setup():
global _dbus_interface
if _dbus_interface is not None:
return _dbus_interface
try:
bus = dbus.SessionBus()
remote_object = bus.get_object('org.gnome.Shell', '/io/github/pwr_solaar/solaar')
_dbus_interface = dbus.Interface(remote_object, 'io.github.pwr_solaar.solaar')
except dbus.exceptions.DBusException:
_log.warn('Solaar Gnome extension not installed - some rule capabilities inoperable', exc_info=_sys.exc_info())
_dbus_interface = False
return _dbus_interface
def xkb_setup(): def xkb_setup():
global X11Lib, Xkbdisplay global X11Lib, Xkbdisplay
if Xkbdisplay is not None: if Xkbdisplay is not None:
@ -562,13 +582,30 @@ def x11_pointer_prog():
return (wm_class[0], wm_class[1], name) if wm_class else (name, ) return (wm_class[0], wm_class[1], name) if wm_class else (name, )
def gnome_dbus_focus_prog():
if not gnome_dbus_interface_setup():
return None
wm_class = _dbus_interface.ActiveWindow()
return (wm_class, ) if wm_class else None
def gnome_dbus_pointer_prog():
if not gnome_dbus_interface_setup():
return None
wm_class = _dbus_interface.PointerOverWindow()
return (wm_class, ) if wm_class else None
class Process(Condition): class Process(Condition):
def __init__(self, process, warn=True): def __init__(self, process, warn=True):
self.process = process self.process = process
if wayland or not x11_setup(): if (not wayland and not x11_setup()) or (wayland and not gnome_dbus_interface_setup()):
if warn: if warn:
_log.warn('rules can only access active process in X11 - %s', self) _log.warn(
'rules can only access active process in X11 or in wayland under GNOME with Solaar Gnome extension - %s',
self
)
if not isinstance(process, str): if not isinstance(process, str):
if warn: if warn:
_log.warn('rule Process argument not a string: %s', process) _log.warn('rule Process argument not a string: %s', process)
@ -582,7 +619,7 @@ class Process(Condition):
_log.debug('evaluate condition: %s', self) _log.debug('evaluate condition: %s', self)
if not isinstance(self.process, str): if not isinstance(self.process, str):
return False return False
focus = x11_focus_prog() focus = x11_focus_prog() if not wayland else gnome_dbus_focus_prog()
result = any(bool(s and s.startswith(self.process)) for s in focus) if focus else None result = any(bool(s and s.startswith(self.process)) for s in focus) if focus else None
return result return result
@ -594,9 +631,12 @@ class MouseProcess(Condition):
def __init__(self, process, warn=True): def __init__(self, process, warn=True):
self.process = process self.process = process
if wayland or not x11_setup(): if (not wayland and not x11_setup()) or (wayland and not gnome_dbus_interface_setup()):
if warn: if warn:
_log.warn('rules cannot access active mouse process in X11 - %s', self) _log.warn(
'rules cannot access active mouse process '
'in X11 or in wayland under GNOME with Solaar Gnome extension - %s', self
)
if not isinstance(process, str): if not isinstance(process, str):
if warn: if warn:
_log.warn('rule MouseProcess argument not a string: %s', process) _log.warn('rule MouseProcess argument not a string: %s', process)
@ -610,7 +650,7 @@ class MouseProcess(Condition):
_log.debug('evaluate condition: %s', self) _log.debug('evaluate condition: %s', self)
if not isinstance(self.process, str): if not isinstance(self.process, str):
return False return False
pointer_focus = x11_pointer_prog() pointer_focus = x11_pointer_prog() if not wayland else gnome_dbus_pointer_prog()
result = any(bool(s and s.startswith(self.process)) for s in pointer_focus) if pointer_focus else None result = any(bool(s and s.startswith(self.process)) for s in pointer_focus) if pointer_focus else None
return result return result

View File

@ -82,6 +82,7 @@ For instructions on installing Solaar see https://pwr-solaar.github.io/Solaar/in
'PyYAML (>= 3.12)', 'PyYAML (>= 3.12)',
'python-xlib (>= 0.27)', 'python-xlib (>= 0.27)',
'psutil (>= 5.4.3)', 'psutil (>= 5.4.3)',
'dbus-python (>=1.3.2)',
], ],
extras_require={ extras_require={
'report-descriptor': ['hid-parser'], 'report-descriptor': ['hid-parser'],