diff --git a/meson.build b/meson.build
index c5987607f..73178d211 100644
--- a/meson.build
+++ b/meson.build
@@ -34,6 +34,7 @@ pixman = dependency('pixman-1')
xkbcommon = dependency('xkbcommon')
libdl = meson.get_compiler('cpp').find_library('dl')
json = dependency('nlohmann_json', version: '>= 3.11.2')
+udev = dependency('libudev')
# We're not to use system wlroots: So we'll use the subproject
if get_option('use_system_wlroots').disabled()
diff --git a/metadata/input-device.xml b/metadata/input-device.xml
index 63ed6769b..54a8f45f4 100644
--- a/metadata/input-device.xml
+++ b/metadata/input-device.xml
@@ -5,5 +5,8 @@
+
diff --git a/src/api/wayfire/config-backend.hpp b/src/api/wayfire/config-backend.hpp
index 8a47601e7..54db71bb6 100644
--- a/src/api/wayfire/config-backend.hpp
+++ b/src/api/wayfire/config-backend.hpp
@@ -49,7 +49,7 @@ class config_backend_t
* described in input-device.xml
*/
virtual std::shared_ptr get_input_device_section(
- wlr_input_device *device);
+ std::string const & prefix, wlr_input_device *device);
virtual ~config_backend_t() = default;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 6c13c1ccc..1552f8b7a 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -242,7 +242,7 @@ void wf::compositor_core_impl_t::post_init()
seat->focus_output(wo);
// Refresh device mappings when we have all outputs and devices
- input->refresh_device_mappings();
+ input->configure_input_devices();
// Start processing cursor events
seat->priv->cursor->setup_listeners();
diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp
index 6d7d8917d..c9ffd8bec 100644
--- a/src/core/plugin.cpp
+++ b/src/core/plugin.cpp
@@ -5,6 +5,7 @@
#include "wayfire/signal-definitions.hpp"
#include
#include
+#include
void wf::plugin_interface_t::fini()
{}
@@ -27,18 +28,76 @@ std::shared_ptr wf::config_backend_t::get_output_section(
return config.get_section(name);
}
+static struct udev_property_and_desc
+{
+ char const *property_name;
+ char const *description;
+} properties_and_descs[] =
+{
+ {"ID_PATH", "stable physical connection path"},
+ {"ID_SERIAL", "stable vendor+pn+sn info"},
+ {"LIBINPUT_DEVICE_GROUP", "stable libinput info"},
+ // sometimes it contains info "by path", sometimes "by id"
+ {"DEVPATH", "unstable devpath"},
+ // used for debugging, to find DEVPATH and match the right
+ // device in `udevadm info --tree`
+};
+
std::shared_ptr wf::config_backend_t::get_input_device_section(
- wlr_input_device *device)
+ std::string const & prefix, wlr_input_device *device)
{
- std::string name = nonull(device->name);
- name = "input-device:" + name;
auto& config = wf::get_core().config;
- if (!config.get_section(name))
+ std::shared_ptr section;
+ auto print_devpath = getenv("WF_PRINT_UDEV_DEVPATH");
+
+ if (wlr_input_device_is_libinput(device))
{
- config.merge_section(
- config.get_section("input-device")->clone_with_name(name));
+ auto libinput_dev = wlr_libinput_get_device_handle(device);
+ if (libinput_dev)
+ {
+ udev_device *udev_dev = libinput_device_get_udev_device(libinput_dev);
+ if (udev_dev)
+ {
+ for (struct udev_property_and_desc const & pd : properties_and_descs)
+ {
+ if (!print_devpath && !strncmp(pd.property_name, "DEVPATH", strlen("DEVPATH")))
+ {
+ continue;
+ }
+
+ const char *value = udev_device_get_property_value(udev_dev, pd.property_name);
+ if (value == nullptr)
+ {
+ continue;
+ }
+
+ std::string name = prefix + ":" + nonull(value);
+ LOGD("Checking for config section [", name, "] ",
+ pd.property_name, " (", pd.description, ")");
+ section = config.get_section(name);
+ if (section)
+ {
+ LOGD("Using config section [", name, "] for ", nonull(device->name));
+ return section;
+ }
+ }
+ }
+ }
+ }
+
+ std::string name = nonull(device->name);
+ name = prefix + ":" + name;
+ LOGD("Checking for config section [", name, "]");
+ section = config.get_section(name);
+ if (section)
+ {
+ LOGD("Using config section [", name, "]");
+ return section;
}
+ config.merge_section(
+ config.get_section(prefix)->clone_with_name(name));
+
return config.get_section(name);
}
diff --git a/src/core/seat/input-manager.cpp b/src/core/seat/input-manager.cpp
index a91153259..2af3cd659 100644
--- a/src/core/seat/input-manager.cpp
+++ b/src/core/seat/input-manager.cpp
@@ -52,53 +52,97 @@ void wf::input_manager_t::handle_new_input(wlr_input_device *dev)
data.device = nonstd::make_observer(input_devices.back().get());
wf::get_core().emit(&data);
- refresh_device_mappings();
+ configure_input_devices();
}
-void wf::input_manager_t::refresh_device_mappings()
+void wf::input_manager_t::calibrate_touch_device(wlr_input_device *dev, std::string const & cal)
{
- // Might trigger motion events which we want to avoid at other stages
- auto state = wf::get_core().get_current_state();
- if (state != wf::compositor_state_t::RUNNING)
+ if (!wlr_input_device_is_libinput(dev) || (dev->type != WLR_INPUT_DEVICE_TOUCH))
{
return;
}
- auto cursor = wf::get_core().get_wlr_cursor();
- for (auto& device : this->input_devices)
+ float m[6];
+ auto libinput_dev = wlr_libinput_get_device_handle(dev);
+ if (sscanf(cal.c_str(), "%f %f %f %f %f %f",
+ &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) == 6)
{
- wlr_input_device *dev = device->get_wlr_handle();
- auto section =
- wf::get_core().config_backend->get_input_device_section(dev);
+ enum libinput_config_status status;
- auto mapped_output = section->get_option("output")->get_value_str();
- if (mapped_output.empty())
+ status = libinput_device_config_calibration_set_matrix(libinput_dev, m);
+ if (status != LIBINPUT_CONFIG_STATUS_SUCCESS)
{
- if (dev->type == WLR_INPUT_DEVICE_POINTER)
- {
- mapped_output = nonull(wlr_pointer_from_input_device(
- dev)->output_name);
- } else if (dev->type == WLR_INPUT_DEVICE_TOUCH)
- {
- mapped_output =
- nonull(wlr_touch_from_input_device(dev)->output_name);
- } else
- {
- mapped_output = nonull(dev->name);
- }
+ LOGE("Failed to apply calibration for ", nonull(dev->name));
+ LOGE(" ", m[0], " ", m[1], " ", m[2], " ", m[3], " ", m[4], " ", m[5]);
+ } else
+ {
+ LOGI("Calibrated input device successfully: ", nonull(dev->name));
+ LOGI(" ", m[0], " ", m[1], " ", m[2], " ", m[3], " ", m[4], " ", m[5]);
}
+ } else
+ {
+ LOGE("Incorrect calibration configuration for ", nonull(dev->name));
+ LOGI("Setting default matrix calibration: ");
+ libinput_device_config_calibration_get_default_matrix(libinput_dev, m);
+ LOGI(" ", m[0], " ", m[1], " ", m[2], " ", m[3], " ", m[4], " ", m[5]);
+ libinput_device_config_calibration_set_matrix(libinput_dev, m);
+ }
+}
- auto wo = wf::get_core().output_layout->find_output(mapped_output);
- if (wo)
+void wf::input_manager_t::configure_input_device(wlr_input_device *dev)
+{
+ auto cursor = wf::get_core().get_wlr_cursor();
+ auto section =
+ wf::get_core().config_backend->get_input_device_section("input-device", dev);
+
+ auto mapped_output = section->get_option("output")->get_value_str();
+ if (mapped_output.empty())
+ {
+ if (dev->type == WLR_INPUT_DEVICE_POINTER)
+ {
+ mapped_output = nonull(wlr_pointer_from_input_device(
+ dev)->output_name);
+ } else if (dev->type == WLR_INPUT_DEVICE_TOUCH)
{
- LOGD("Mapping input ", dev->name, " to output ", wo->to_string(), ".");
- wlr_cursor_map_input_to_output(cursor, dev, wo->handle);
+ mapped_output =
+ nonull(wlr_touch_from_input_device(dev)->output_name);
} else
{
- LOGD("Mapping input ", dev->name, " to output null.");
- wlr_cursor_map_input_to_output(cursor, dev, nullptr);
+ mapped_output = nonull(dev->name);
}
}
+
+ auto cal = section->get_option("calibration")->get_value_str();
+ if (!cal.empty())
+ {
+ calibrate_touch_device(dev, cal);
+ }
+
+ auto wo = wf::get_core().output_layout->find_output(mapped_output);
+ if (wo)
+ {
+ LOGD("Mapping input ", dev->name, " to output ", wo->to_string(), ".");
+ wlr_cursor_map_input_to_output(cursor, dev, wo->handle);
+ } else
+ {
+ LOGD("Mapping input ", dev->name, " to output null.");
+ wlr_cursor_map_input_to_output(cursor, dev, nullptr);
+ }
+}
+
+void wf::input_manager_t::configure_input_devices()
+{
+ // Might trigger motion events which we want to avoid at other stages
+ auto state = wf::get_core().get_current_state();
+ if (state != wf::compositor_state_t::RUNNING)
+ {
+ return;
+ }
+
+ for (auto& device : this->input_devices)
+ {
+ configure_input_device(device->get_wlr_handle());
+ }
}
void wf::input_manager_t::handle_input_destroyed(wlr_input_device *dev)
@@ -170,7 +214,7 @@ wf::input_manager_t::input_manager_t()
ev->output->set_inhibited(true);
}
- refresh_device_mappings();
+ configure_input_devices();
});
wf::get_core().output_layout->connect(&output_added);
}
diff --git a/src/core/seat/input-manager.hpp b/src/core/seat/input-manager.hpp
index 54392396e..b79ddde01 100644
--- a/src/core/seat/input-manager.hpp
+++ b/src/core/seat/input-manager.hpp
@@ -37,11 +37,23 @@ class input_manager_t
*/
uint32_t locked_mods = 0;
+ /**
+ * Map a single input device to output as specified in the
+ * config file or by hints in the wlroots backend.
+ */
+ void configure_input_device(wlr_input_device *dev);
+
/**
* Go through all input devices and map them to outputs as specified in the
* config file or by hints in the wlroots backend.
*/
- void refresh_device_mappings();
+ void configure_input_devices();
+
+ /**
+ * Calibrate a touch device with a matrix. This function does nothing
+ * if called with a device that is not a touch device.
+ */
+ void calibrate_touch_device(wlr_input_device *dev, std::string const & cal);
input_manager_t();
~input_manager_t() = default;
diff --git a/src/core/seat/keyboard.cpp b/src/core/seat/keyboard.cpp
index ad3ab71b1..51a964dea 100644
--- a/src/core/seat/keyboard.cpp
+++ b/src/core/seat/keyboard.cpp
@@ -121,14 +121,18 @@ void wf::keyboard_t::setup_listeners()
wf::keyboard_t::keyboard_t(wlr_input_device *dev) :
handle(wlr_keyboard_from_input_device(dev)), device(dev)
{
- model.load_option("input/xkb_model");
- variant.load_option("input/xkb_variant");
- layout.load_option("input/xkb_layout");
- options.load_option("input/xkb_options");
- rules.load_option("input/xkb_rules");
-
- repeat_rate.load_option("input/kb_repeat_rate");
- repeat_delay.load_option("input/kb_repeat_delay");
+ auto section =
+ wf::get_core().config_backend->get_input_device_section("input", dev);
+ auto section_name = section->get_name();
+
+ model.load_option(section_name + "/xkb_model");
+ variant.load_option(section_name + "/xkb_variant");
+ layout.load_option(section_name + "/xkb_layout");
+ options.load_option(section_name + "/xkb_options");
+ rules.load_option(section_name + "/xkb_rules");
+
+ repeat_rate.load_option(section_name + "/kb_repeat_rate");
+ repeat_delay.load_option(section_name + "/kb_repeat_delay");
// When the configuration options change, mark them as dirty.
// They are applied at the config-reloaded signal.
diff --git a/src/meson.build b/src/meson.build
index 384533ae7..ebc7463e3 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -64,7 +64,8 @@ wayfire_sources = ['geometry.cpp',
wayfire_dependencies = [wayland_server, wlroots, xkbcommon, libinput,
pixman, drm, egl, glesv2, glm, wf_protos, libdl,
- wfconfig, libinotify, backtrace, wfutils, xcb, wftouch, json]
+ wfconfig, libinotify, backtrace, wfutils, xcb,
+ wftouch, json, udev]
if conf_data.get('BUILD_WITH_IMAGEIO')
wayfire_dependencies += [jpeg, png]