Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
feat: parse and apply touchpad/mouse input settings in monolithic server
src/server/config.rs | 28 ++++++++++++++-
src/server/input_device.rs | 5 ++-
src/server/libinput_device.rs | 81 +++++++++++++++++++++++++++++++++++++++++++
src/server/window_manager.rs | 19 ++++++++++
4 files changed, 131 insertions(+), 2 deletions(-)
diff --git a/src/server/config.rs b/src/server/config.rs
index fde12a7..74456c1 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -177,6 +177,18 @@ pub struct InputDeviceConfigRule {
pub scroll_factor: Option<f64>,
}
+#[derive(Debug, Deserialize, Clone, Default)]
+pub struct InputConfig {
+ pub tap_to_click: Option<bool>,
+ pub accel_speed: Option<f64>,
+ pub accel_profile: Option<String>,
+ pub natural_scroll: Option<bool>,
+ pub dwt: Option<bool>,
+ pub dwtp: Option<bool>,
+ pub trackpoint_accel_speed: Option<f64>,
+ pub trackpoint_accel_profile: Option<String>,
+}
+
#[derive(Debug, Deserialize)]
pub struct Config {
#[serde(default)]
@@ -197,6 +209,8 @@ pub struct Config {
pub output: Option<OutputConfig>,
#[serde(default)]
pub device: Vec<InputDeviceConfigRule>,
+ #[serde(default)]
+ pub input: Option<InputConfig>,
}
#[derive(Debug, Deserialize)]
@@ -607,7 +621,11 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
}
state.input_rules = config.device.clone();
- unsafe { state.apply_input_rules(); }
+ state.input_config = config.input.clone().unwrap_or_default();
+ unsafe {
+ state.apply_input_rules();
+ state.apply_input_config();
+ }
state.keybinds.clear();
for kb in &config.keybind {
@@ -707,6 +725,14 @@ mod tests {
println!("TEST_WM_STARTUP: {:?}", server.wm.startup);
println!("TEST_WM_PATH: {:?}", std::env::var("PATH"));
assert!(!server.wm.startup.is_empty(), "Startup list should not be empty!");
+ assert_eq!(server.wm.input_config.tap_to_click, Some(true));
+ assert_eq!(server.wm.input_config.accel_speed, Some(0.2));
+ assert_eq!(server.wm.input_config.accel_profile, Some("adaptive".to_string()));
+ assert_eq!(server.wm.input_config.natural_scroll, Some(true));
+ assert_eq!(server.wm.input_config.dwt, Some(true));
+ assert_eq!(server.wm.input_config.dwtp, Some(true));
+ assert_eq!(server.wm.input_config.trackpoint_accel_speed, Some(0.6));
+ assert_eq!(server.wm.input_config.trackpoint_accel_profile, Some("flat".to_string()));
}
}
}
diff --git a/src/server/input_device.rs b/src/server/input_device.rs
index bc8e8eb..c3dd1b6 100644
--- a/src/server/input_device.rs
+++ b/src/server/input_device.rs
@@ -89,7 +89,10 @@ impl InputDevice {
std::ptr::null_mut()
};
if !handle.is_null() {
- (*device).libinput = Some(crate::libinput_device::LibinputDevice::init(device, handle));
+ let libinput_dev = crate::libinput_device::LibinputDevice::init(device, handle);
+ let wm = &(*(*seat).server).wm;
+ libinput_dev.apply_config(&wm.input_config);
+ (*device).libinput = Some(libinput_dev);
}
let dev_type = ffi::river_wlr_input_device_get_type(wlr_device);
diff --git a/src/server/libinput_device.rs b/src/server/libinput_device.rs
index d5642c4..d0e0b0d 100644
--- a/src/server/libinput_device.rs
+++ b/src/server/libinput_device.rs
@@ -49,6 +49,87 @@ impl LibinputDevice {
dev
}
+ pub unsafe fn apply_config(&self, config: &crate::config::InputConfig) {
+ let handle = self.libinput;
+ if handle.is_null() {
+ return;
+ }
+
+ // Tap to click
+ if let Some(tap) = config.tap_to_click {
+ if ffi::libinput_device_config_tap_get_finger_count(handle) > 0 {
+ let state = if tap { 1 } else { 0 };
+ ffi::libinput_device_config_tap_set_enabled(handle, state);
+ }
+ }
+
+ // Natural scroll
+ if let Some(natural) = config.natural_scroll {
+ if ffi::libinput_device_config_scroll_has_natural_scroll(handle) != 0 {
+ let state = if natural { 1 } else { 0 };
+ ffi::libinput_device_config_scroll_set_natural_scroll_enabled(handle, state);
+ }
+ }
+
+ // Dwt (Disable while typing)
+ if let Some(dwt) = config.dwt {
+ if ffi::libinput_device_config_dwt_is_available(handle) != 0 {
+ let state = if dwt { 1 } else { 0 };
+ ffi::libinput_device_config_dwt_set_enabled(handle, state);
+ }
+ }
+
+ // Dwtp (Disable while trackpointing)
+ if let Some(dwtp) = config.dwtp {
+ if ffi::libinput_device_config_dwtp_is_available(handle) != 0 {
+ let state = if dwtp { 1 } else { 0 };
+ ffi::libinput_device_config_dwtp_set_enabled(handle, state);
+ }
+ }
+
+ // Check if device name contains "trackpoint" (case-insensitive)
+ let name_ptr = ffi::river_wlr_input_device_get_name((*self.parent_device).wlr_device);
+ let name = if !name_ptr.is_null() {
+ std::ffi::CStr::from_ptr(name_ptr).to_string_lossy().to_lowercase()
+ } else {
+ String::new()
+ };
+ let is_trackpoint = name.contains("trackpoint");
+
+ // Acceleration Speed
+ let speed = if is_trackpoint {
+ config.trackpoint_accel_speed.or(config.accel_speed)
+ } else {
+ config.accel_speed
+ };
+ if let Some(s) = speed {
+ if ffi::libinput_device_config_accel_get_profiles(handle) != 0 {
+ ffi::libinput_device_config_accel_set_speed(handle, s);
+ }
+ }
+
+ // Acceleration Profile
+ let profile_str = if is_trackpoint {
+ config.trackpoint_accel_profile.as_ref().or(config.accel_profile.as_ref())
+ } else {
+ config.accel_profile.as_ref()
+ };
+ if let Some(ref p_str) = profile_str {
+ if ffi::libinput_device_config_accel_get_profiles(handle) != 0 {
+ let profile = match p_str.as_str() {
+ "flat" => Some(ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT),
+ "adaptive" => Some(ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE),
+ "none" => Some(ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_NONE),
+ "custom" => Some(ffi::libinput_config_accel_profile_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM),
+ _ => None,
+ };
+ if let Some(p) = profile {
+ ffi::libinput_device_config_accel_set_profile(handle, p);
+ }
+ }
+ }
+ }
+
pub unsafe fn deinit(&mut self) {
let objects_head = &mut self.objects as *mut ffi::wl_list as *mut WlList;
let mut curr = (*objects_head).next;
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 1f3a741..c705c96 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -68,6 +68,7 @@ pub struct WindowManager {
pub status_sender: Option<crate::status_server::StatusSender>,
pub output_scale: f32,
pub input_rules: Vec<crate::config::InputDeviceConfigRule>,
+ pub input_config: crate::config::InputConfig,
}
impl WindowManager {
@@ -79,6 +80,7 @@ impl WindowManager {
self.sent.output_config = std::ptr::null_mut();
self.output_scale = 1.0;
self.input_rules = Vec::new();
+ self.input_config = crate::config::InputConfig::default();
Ok(())
}
@@ -121,6 +123,7 @@ impl WindowManager {
self.startup = Vec::new();
self.status_sender = None;
self.input_rules = Vec::new();
+ self.input_config = crate::config::InputConfig::default();
ffi::wl_list_init(&mut self.sent.outputs);
ffi::wl_list_init(&mut self.sent.seats);
@@ -1630,6 +1633,22 @@ impl WindowManager {
curr = next;
}
}
+
+ pub unsafe fn apply_input_config(&mut self) {
+ if self.server.is_null() {
+ return;
+ }
+ let devices_head = &mut (*self.server).input_manager.devices as *mut ffi::wl_list as *mut WlList;
+ let mut curr = (*devices_head).next;
+ while curr != devices_head {
+ let next = (*curr).next;
+ let device = crate::container_of!(curr, crate::input_device::InputDevice, link);
+ if let Some(ref mut libinput) = (*device).libinput {
+ libinput.apply_config(&self.input_config);
+ }
+ curr = next;
+ }
+ }
}
unsafe extern "C" fn handle_ipc_timer(data: *mut std::ffi::c_void) -> std::os::raw::c_int {