Source code for libqtile.backend.wayland.inputs

import enum
from typing import Any

from libqtile import configurable

try:
    from libqtile.backend.wayland._ffi import ffi, lib

except ModuleNotFoundError:
    print("Warning: Wayland backend not built. Backend will not run.")

    from libqtile.backend.wayland.ffi_stub import ffi, lib


[docs] class InputConfig(configurable.Configurable): """ This is used to configure input devices. An instance of this class represents one set of settings that can be applied to an input device. To use this, define a dictionary called ``wl_input_rules`` in your config. The keys are used to match input devices, and the values are instances of this class with the desired settings. For example: .. code-block:: python from libqtile.backend.wayland import InputConfig wl_input_rules = { "1267:12377:ELAN1300:00 04F3:3059 Touchpad": InputConfig(left_handed=True), "*": InputConfig(left_handed=True, pointer_accel=True), "type:keyboard": InputConfig(kb_options="ctrl:nocaps,compose:ralt"), } When a input device is being configured, the most specific matching key in the dictionary is found and the corresponding settings are used to configure the device. Unique identifiers are chosen first, then ``"type:X"``, then ``"*"``. The command ``qtile cmd-obj -o core -f get_inputs`` can be used to get information about connected devices, including their identifiers. Options default to ``None``, leave a device's default settings intact. For information on what each option does, see the documenation for libinput: https://wayland.freedesktop.org/libinput/doc/latest/configuration.html. Note that devices often only support a subset of settings. This tries to mirror how Sway configures libinput devices. For more information check out sway-input(5): https://man.archlinux.org/man/sway-input.5#LIBINPUT_CONFIGURATION Keyboards, managed by `xkbcommon <https://github.com/xkbcommon/libxkbcommon>`_, are configured with the options prefixed by ``kb_``. X11's helpful `XKB guide <https://www.x.org/releases/X11R7.5/doc/input/XKB-Config.html>`_ may be useful for figuring out the syntax for some of these settings. """ defaults = [ ("accel_profile", None, "``'adaptive'`` or ``'flat'``"), ("click_method", None, "``'none'``, ``'button_areas'`` or ``'clickfinger'``"), ("drag", None, "``True`` or ``False``"), ("drag_lock", None, "``True`` or ``False``"), ("dwt", None, "True or False"), ("left_handed", None, "``True`` or ``False``"), ("middle_emulation", None, "``True`` or ``False``"), ("natural_scroll", None, "``True`` or ``False``"), ("pointer_accel", None, "A ``float`` between -1 and 1."), ("scroll_button", None, "``'disable'``, 'Button[1-3,8,9]' or a keycode"), ( "scroll_method", None, "``'none'``, ``'two_finger'``, ``'edge'``, or ``'on_button_down'``", ), ("tap", None, "``True`` or ``False``"), ("tap_button_map", None, "``'lrm'`` or ``'lmr'``"), ("kb_layout", None, "Keyboard layout i.e. ``XKB_DEFAULT_LAYOUT``"), ("kb_options", None, "Keyboard options i.e. ``XKB_DEFAULT_OPTIONS``"), ("kb_variant", None, "Keyboard variant i.e. ``XKB_DEFAULT_VARIANT``"), ("kb_repeat_rate", 25, "Keyboard key repeats made per second"), ("kb_repeat_delay", 600, "Keyboard delay in milliseconds before repeating"), ] def __init__(self, **config: Any) -> None: configurable.Configurable.__init__(self, **config) self.add_defaults(InputConfig.defaults)
class InputDeviceType(enum.IntEnum): KEYBOARD = lib.WLR_INPUT_DEVICE_KEYBOARD POINTER = lib.WLR_INPUT_DEVICE_POINTER TOUCH = lib.WLR_INPUT_DEVICE_TOUCH TABLET = lib.WLR_INPUT_DEVICE_TABLET TABLET_PAD = lib.WLR_INPUT_DEVICE_TABLET_PAD SWITCH = lib.WLR_INPUT_DEVICE_SWITCH class AccelProfile(enum.IntEnum): adaptive = lib.LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE flat = lib.LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT class ClickMethod(enum.IntEnum): none = lib.LIBINPUT_CONFIG_CLICK_METHOD_NONE button_areas = lib.LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS clickfinger = lib.LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER class TapMap(enum.IntEnum): lrm = lib.LIBINPUT_CONFIG_TAP_MAP_LRM lmr = lib.LIBINPUT_CONFIG_TAP_MAP_LMR class ScrollMethod(enum.IntEnum): none = lib.LIBINPUT_CONFIG_SCROLL_NO_SCROLL two_finger = lib.LIBINPUT_CONFIG_SCROLL_2FG edge = lib.LIBINPUT_CONFIG_SCROLL_EDGE on_button_down = lib.LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN def configure_input_devices(server: ffi.CData, configs: dict[str, Any]) -> None: @ffi.callback( "void(struct qw_input_device *input_device, char *name, int type, int vendor, int product)" ) def input_device_cb( input_device: ffi.CData, name: ffi.CData, type: int, vendor: int, product: int ) -> None: # Get the device type and identifier for this input device. These can be used be # used to assign ``InputConfig`` options to devices or types of devices. name = ffi.string(name).decode() if name == " " or not name.isprintable(): name = "_" type_key = "type:" + InputDeviceType(type).name.lower() identifier = f"{vendor:d}:{product:d}:{name!s}" if type_key == "type:pointer" and lib is not None: # This checks whether the pointer is a touchpad, so that we can target those # specifically. if lib.qw_input_device_is_touchpad(input_device): type_key = "type:touchpad" if identifier in configs: conf = configs[identifier] elif type_key in configs: conf = configs[type_key] elif "*" in configs: conf = configs["*"] else: conf = None if conf is not None: if type == InputDeviceType.POINTER: device = lib.qw_input_device_get_libinput_handle(input_device) if device == ffi.NULL: return if conf.accel_profile is not None: lib.qw_input_device_config_accel_set_profile( device, AccelProfile[conf.accel_profile].value ) if conf.pointer_accel is not None: lib.qw_input_device_config_accel_set_speed(device, conf.pointer_accel) if conf.click_method is not None: lib.qw_input_device_config_click_set_method( device, ClickMethod[conf.click_method].value ) if conf.drag is not None: lib.qw_input_device_config_tap_set_drag_enabled(device, int(conf.drag)) if conf.drag_lock is not None: lib.qw_input_device_config_tap_set_drag_lock_enabled( device, int(conf.drag_lock) ) if conf.tap is not None: lib.qw_input_device_config_tap_set_enabled(device, int(conf.tap)) if conf.tap_button_map is not None: lib.qw_input_device_config_tap_set_button_map( device, TapMap[conf.tap_button_map].value ) if conf.natural_scroll is not None: lib.qw_input_device_config_scroll_set_natural_scroll_enabled( device, int(conf.natural_scroll) ) if conf.scroll_method is not None: lib.qw_input_device_config_scroll_set_method( device, ScrollMethod[conf.scroll_method].value ) if conf.scroll_button is not None: if isinstance(conf.scroll_button, str): if conf.scroll_button == "disable": button = 0 else: button = lib.qw_util_get_button_code(int(conf.scroll_button[-1]) - 1) else: button = conf.scroll_button lib.qw_input_device_config_scroll_set_button(device, button) if conf.dwt is not None: lib.qw_input_device_config_dwt_set_enabled(device, int(conf.dwt)) if conf.left_handed is not None: lib.qw_input_device_config_left_handed_set(device, int(conf.left_handed)) if conf.middle_emulation is not None: lib.qw_input_device_config_middle_emulation_set_enabled( device, int(conf.middle_emulation) ) elif type == InputDeviceType.KEYBOARD: keyboard = lib.qw_input_device_get_keyboard(input_device) if keyboard == ffi.NULL: return lib.qw_keyboard_set_repeat_info( keyboard, conf.kb_repeat_rate, conf.kb_repeat_delay ) lib.qw_keyboard_set_keymap( keyboard, ffi.new("char[]", (conf.kb_layout or "").encode()), ffi.new("char[]", (conf.kb_options or "").encode()), ffi.new("char[]", (conf.kb_variant or "").encode()), ) lib.qw_server_loop_input_devices(server, input_device_cb)