Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
feat: implement scroll scaling and sensitivity config
src/clearctl.rs | 1 +
src/server/config.rs | 11 ++++++++
src/server/cursor.rs | 18 ++++++++++--
src/server/input_manager.rs | 13 +++++++++
src/server/window_manager.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 106 insertions(+), 2 deletions(-)
diff --git a/src/clearctl.rs b/src/clearctl.rs
index a8b811a..752989a 100644
--- a/src/clearctl.rs
+++ b/src/clearctl.rs
@@ -44,6 +44,7 @@ fn usage(name: &str, to_stderr: bool) {
print(" restart");
print(" reload");
print(" repeat <rate> <delay>");
+ print(" input <device_name|*> scroll-factor <value>");
print(" config-done");
print(" spawn <command>");
print(" notify <title> [body]");
diff --git a/src/server/config.rs b/src/server/config.rs
index 565590e..fde12a7 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -171,6 +171,12 @@ fn default_scale() -> f64 {
1.0
}
+#[derive(Debug, Deserialize, Clone)]
+pub struct InputDeviceConfigRule {
+ pub name: String,
+ pub scroll_factor: Option<f64>,
+}
+
#[derive(Debug, Deserialize)]
pub struct Config {
#[serde(default)]
@@ -189,6 +195,8 @@ pub struct Config {
pub startup: Vec<StartupConfig>,
#[serde(default)]
pub output: Option<OutputConfig>,
+ #[serde(default)]
+ pub device: Vec<InputDeviceConfigRule>,
}
#[derive(Debug, Deserialize)]
@@ -598,6 +606,9 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
std::env::set_var(key, &expanded);
}
+ state.input_rules = config.device.clone();
+ unsafe { state.apply_input_rules(); }
+
state.keybinds.clear();
for kb in &config.keybind {
let mods = parse_modifiers(&kb.mods);
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 7d47b2b..0458992 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -658,12 +658,26 @@ unsafe extern "C" fn handle_axis(listener: *mut ffi::wl_listener, data: *mut std
let event = data as *mut ffi::wlr_pointer_axis_event;
let seat = &mut *cursor.seat;
+
+ let mut delta = (*event).delta;
+ let mut delta_discrete = (*event).delta_discrete;
+
+ if !(*event).pointer.is_null() {
+ let wlr_device = &mut (*(*event).pointer).base as *mut ffi::wlr_input_device;
+ let device_ptr = ffi::river_wlr_input_device_get_data(wlr_device) as *mut crate::input_device::InputDevice;
+ if !device_ptr.is_null() {
+ let factor = (*device_ptr).config.scroll_factor;
+ delta *= factor;
+ delta_discrete = (delta_discrete as f64 * factor) as i32;
+ }
+ }
+
ffi::wlr_seat_pointer_notify_axis(
seat.wlr_seat,
(*event).time_msec,
(*event).orientation,
- (*event).delta,
- (*event).delta_discrete,
+ delta,
+ delta_discrete,
(*event).source,
(*event).relative_direction,
);
diff --git a/src/server/input_manager.rs b/src/server/input_manager.rs
index fff4453..54f8d54 100644
--- a/src/server/input_manager.rs
+++ b/src/server/input_manager.rs
@@ -309,6 +309,19 @@ unsafe extern "C" fn handle_new_input(listener: *mut ffi::wl_listener, data: *mu
let device = crate::input_device::InputDevice::new(im.default_seat, wlr_device, false);
+ let name_ptr = (*wlr_device).name;
+ if !name_ptr.is_null() {
+ let name = std::ffi::CStr::from_ptr(name_ptr).to_string_lossy();
+ let wm = &mut (*im.server).wm;
+ for rule in &wm.input_rules {
+ if rule.name == "*" || name.contains(&rule.name) {
+ if let Some(factor) = rule.scroll_factor {
+ (*device).config.scroll_factor = factor;
+ }
+ }
+ }
+ }
+
let dev_type = ffi::river_wlr_input_device_get_type(wlr_device);
if dev_type == ffi::wlr_input_device_type_WLR_INPUT_DEVICE_KEYBOARD {
crate::keyboard::Keyboard::create(device);
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index e040ddf..009ef07 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -67,6 +67,7 @@ pub struct WindowManager {
pub startup: Vec<crate::config::StartupConfig>,
pub status_sender: Option<crate::status_server::StatusSender>,
pub output_scale: f32,
+ pub input_rules: Vec<crate::config::InputDeviceConfigRule>,
}
impl WindowManager {
@@ -77,6 +78,7 @@ impl WindowManager {
self.scheduled.output_config = std::ptr::null_mut();
self.sent.output_config = std::ptr::null_mut();
self.output_scale = 1.0;
+ self.input_rules = Vec::new();
Ok(())
}
@@ -118,6 +120,7 @@ impl WindowManager {
self.ipc_timer = std::ptr::null_mut();
self.startup = Vec::new();
self.status_sender = None;
+ self.input_rules = Vec::new();
ffi::wl_list_init(&mut self.sent.outputs);
ffi::wl_list_init(&mut self.sent.seats);
@@ -1548,9 +1551,71 @@ impl WindowManager {
self.dirty_windowing();
"ok\n".to_string()
}
+ "input" => {
+ if parts.len() < 4 { return "error: usage: input <device_name|*> scroll-factor <value>\n".to_string(); }
+ let device_name = parts[1];
+ let key = parts[2];
+ let val = parts[3];
+ if key == "scroll-factor" {
+ if let Ok(factor) = val.parse::<f64>() {
+ if factor < 0.0 {
+ return "error: scroll factor cannot be negative\n".to_string();
+ }
+ let mut found = false;
+ 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);
+ let name_ptr = (*(*device).wlr_device).name;
+ if !name_ptr.is_null() {
+ let name = std::ffi::CStr::from_ptr(name_ptr).to_string_lossy();
+ if device_name == "*" || name.contains(device_name) {
+ (*device).config.scroll_factor = factor;
+ found = true;
+ }
+ }
+ curr = next;
+ }
+ if found {
+ "ok\n".to_string()
+ } else {
+ "error: no matching device found\n".to_string()
+ }
+ } else {
+ "error: invalid scroll-factor value\n".to_string()
+ }
+ } else {
+ format!("error: unknown input command: {}\n", key)
+ }
+ }
_ => format!("error: unknown command: {}\n", action),
}
}
+
+ pub unsafe fn apply_input_rules(&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);
+ let name_ptr = (*(*device).wlr_device).name;
+ if !name_ptr.is_null() {
+ let name = std::ffi::CStr::from_ptr(name_ptr).to_string_lossy();
+ for rule in &self.input_rules {
+ if rule.name == "*" || name.contains(&rule.name) {
+ if let Some(factor) = rule.scroll_factor {
+ (*device).config.scroll_factor = factor;
+ }
+ }
+ }
+ }
+ curr = next;
+ }
+ }
}
unsafe extern "C" fn handle_ipc_timer(data: *mut std::ffi::c_void) -> std::os::raw::c_int {