diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index e303d759..1a952540 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -617,6 +617,10 @@ impl Client { self.send(&ClientMessage::SetTapEnabled { device, enabled }) } + pub fn set_input_natural_scrolling_enabled(&self, device: InputDevice, enabled: bool) { + self.send(&ClientMessage::SetNaturalScrollingEnabled { device, enabled }) + } + pub fn set_input_drag_enabled(&self, device: InputDevice, enabled: bool) { self.send(&ClientMessage::SetDragEnabled { device, enabled }) } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index e5f2def8..fdf7041a 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -330,6 +330,10 @@ pub enum ClientMessage<'a> { GetWorkspaceCapture { workspace: Workspace, }, + SetNaturalScrollingEnabled { + device: InputDevice, + enabled: bool, + }, } #[derive(Encode, Decode, Debug)] diff --git a/jay-config/src/input.rs b/jay-config/src/input.rs index 103bd228..1eaa2ca2 100644 --- a/jay-config/src/input.rs +++ b/jay-config/src/input.rs @@ -104,6 +104,13 @@ impl InputDevice { pub fn set_drag_lock_enabled(self, enabled: bool) { get!().set_input_drag_lock_enabled(self, enabled); } + + /// Sets whether natural scrolling is enabled for this device. + /// + /// See + pub fn set_natural_scrolling_enabled(self, enabled: bool) { + get!().set_input_natural_scrolling_enabled(self, enabled); + } } /// A seat. diff --git a/src/backend.rs b/src/backend.rs index 6666cedf..47b604ac 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -119,6 +119,7 @@ pub trait InputDevice { fn set_tap_enabled(&self, enabled: bool); fn set_drag_enabled(&self, enabled: bool); fn set_drag_lock_enabled(&self, enabled: bool); + fn set_natural_scrolling_enabled(&self, enabled: bool); } #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] @@ -195,6 +196,7 @@ pub enum InputEvent { AxisPx { dist: Fixed, axis: ScrollAxis, + inverted: bool, }, AxisSource { source: AxisSource, @@ -205,6 +207,7 @@ pub enum InputEvent { Axis120 { dist: i32, axis: ScrollAxis, + inverted: bool, }, AxisFrame { time_usec: u64, diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 58cd22b9..f827087c 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -283,6 +283,7 @@ struct MetalInputDevice { events: SyncQueue, cb: CloneCell>>, name: CloneCell>, + natural_scrolling: Cell, // state pressed_keys: SmallMap, @@ -296,6 +297,7 @@ struct MetalInputDevice { tap_enabled: Cell>, drag_enabled: Cell>, drag_lock_enabled: Cell>, + natural_scrolling_enabled: Cell>, } #[derive(Clone)] @@ -354,6 +356,9 @@ impl MetalInputDevice { if let Some(enabled) = self.drag_lock_enabled.get() { dev.device().set_drag_lock_enabled(enabled); } + if let Some(enabled) = self.natural_scrolling_enabled.get() { + self.do_set_natural_scrolling_enabled(&dev, enabled); + } } fn pre_pause(&self) { @@ -373,6 +378,12 @@ impl MetalInputDevice { }); } } + + fn do_set_natural_scrolling_enabled(&self, dev: &RegisteredDevice, enabled: bool) { + dev.device().set_natural_scrolling_enabled(enabled); + self.natural_scrolling + .set(dev.device().natural_scrolling_enabled()); + } } impl InputDevice for MetalInputDevice { @@ -465,6 +476,13 @@ impl InputDevice for MetalInputDevice { dev.device().set_drag_lock_enabled(enabled); } } + + fn set_natural_scrolling_enabled(&self, enabled: bool) { + self.natural_scrolling_enabled.set(Some(enabled)); + if let Some(dev) = self.inputdev.get() { + self.do_set_natural_scrolling_enabled(&dev, enabled); + } + } } impl MetalInputDevice { diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index 2c30595d..6ca6f4e5 100644 --- a/src/backends/metal/input.rs +++ b/src/backends/metal/input.rs @@ -149,11 +149,13 @@ impl MetalBackend { InputEvent::Axis120 { dist: scroll as _, axis, + inverted: dev.natural_scrolling.get(), } } else { InputEvent::AxisPx { dist: Fixed::from_f64(scroll), axis, + inverted: dev.natural_scrolling.get(), } }; dev.event(ie); diff --git a/src/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index 126ef85d..829ad07a 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -287,6 +287,7 @@ impl MetalBackend { events: Default::default(), cb: Default::default(), name: Default::default(), + natural_scrolling: Default::default(), pressed_keys: Default::default(), pressed_buttons: Default::default(), left_handed: Default::default(), @@ -296,6 +297,7 @@ impl MetalBackend { tap_enabled: Default::default(), drag_enabled: Default::default(), drag_lock_enabled: Default::default(), + natural_scrolling_enabled: Default::default(), }); slots[slot] = Some(dev.clone()); self.device_holder @@ -335,6 +337,8 @@ impl MetalBackend { }; inputdev.device().set_slot(slot); dev.name.set(Rc::new(inputdev.device().name())); + dev.natural_scrolling + .set(inputdev.device().natural_scrolling_enabled()); dev.inputdev.set(Some(inputdev)); dev.apply_config(); slf.state diff --git a/src/backends/x.rs b/src/backends/x.rs index db9943ff..d2a96bb7 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -799,6 +799,7 @@ impl XBackend { seat.mouse_event(InputEvent::Axis120 { dist: val * AXIS_120, axis, + inverted: false, }); seat.mouse_event(InputEvent::AxisFrame { time_usec: now_usec(), @@ -1162,6 +1163,10 @@ impl InputDevice for XSeatKeyboard { fn set_drag_lock_enabled(&self, enabled: bool) { let _ = enabled; } + + fn set_natural_scrolling_enabled(&self, enabled: bool) { + let _ = enabled; + } } impl InputDevice for XSeatMouse { @@ -1223,4 +1228,8 @@ impl InputDevice for XSeatMouse { fn set_drag_lock_enabled(&self, enabled: bool) { let _ = enabled; } + + fn set_natural_scrolling_enabled(&self, enabled: bool) { + let _ = enabled; + } } diff --git a/src/cli/seat_test.rs b/src/cli/seat_test.rs index a58567f2..431e01a6 100644 --- a/src/cli/seat_test.rs +++ b/src/cli/seat_test.rs @@ -6,8 +6,8 @@ use { wire::{ jay_compositor::{GetSeats, Seat, SeatEvents}, jay_seat_events::{ - Axis120, AxisFrame, AxisPx, AxisSource, AxisStop, Button, Key, Modifiers, - PointerAbs, PointerRel, + Axis120, AxisFrame, AxisInverted, AxisPx, AxisSource, AxisStop, Button, Key, + Modifiers, PointerAbs, PointerRel, }, }, }, @@ -134,6 +134,9 @@ async fn run(seat_test: Rc) { AxisSource::handle(tc, se, ps.clone(), move |ps, ev| { ps.source.set(Some(ev.source)); }); + AxisInverted::handle(tc, se, ps.clone(), move |ps, ev| { + ps.inverted[ev.axis as usize].set(ev.inverted != 0); + }); AxisPx::handle(tc, se, ps.clone(), move |ps, ev| { ps.px[ev.axis as usize].set(Some(ev.dist)); }); @@ -152,6 +155,8 @@ async fn run(seat_test: Rc) { let stop_y = ps.stop[1].take(); let v120_x = ps.v120[0].take(); let v120_y = ps.v120[1].take(); + let inverted_x = ps.inverted[0].get(); + let inverted_y = ps.inverted[1].get(); if all || ev.seat == seat { if all { print!("Seat: {}, ", st.name(ev.seat)); @@ -175,9 +180,9 @@ async fn run(seat_test: Rc) { print!("Source: {}", source); need_comma = true; } - for (axis, px, steps, stop) in [ - ("horizontal", px_x, v120_x, stop_x), - ("vertical", px_y, v120_y, stop_y), + for (axis, px, steps, stop, inverted) in [ + ("horizontal", px_x, v120_x, stop_x, inverted_x), + ("vertical", px_y, v120_y, stop_y, inverted_y), ] { if px.is_some() || steps.is_some() || stop { comma!(); @@ -197,6 +202,11 @@ async fn run(seat_test: Rc) { print!("stop"); need_comma = true; } + if inverted { + comma!(); + print!("natural scrolling"); + need_comma = true; + } } println!(); } diff --git a/src/config/handler.rs b/src/config/handler.rs index e6e7372b..347ca39c 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -512,6 +512,16 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_set_natural_scrolling_enabled( + &self, + device: InputDevice, + enabled: bool, + ) -> Result<(), CphError> { + let dev = self.get_device_handler_data(device)?; + dev.device.set_natural_scrolling_enabled(enabled); + Ok(()) + } + fn handle_set_drag_lock_enabled( &self, device: InputDevice, @@ -1296,6 +1306,9 @@ impl ConfigProxyHandler { ClientMessage::GetWorkspaceCapture { workspace } => self .handle_get_workspace_capture(workspace) .wrn("get_workspace_capture")?, + ClientMessage::SetNaturalScrollingEnabled { device, enabled } => self + .handle_set_natural_scrolling_enabled(device, enabled) + .wrn("set_natural_scrolling_enabled")?, } Ok(()) } diff --git a/src/ifs/jay_seat_events.rs b/src/ifs/jay_seat_events.rs index 81918f93..42293735 100644 --- a/src/ifs/jay_seat_events.rs +++ b/src/ifs/jay_seat_events.rs @@ -98,6 +98,11 @@ impl JaySeatEvents { }); } if let Some(dist) = ps.px[axis].get() { + self.client.event(AxisInverted { + self_id: self.id, + axis: axis as _, + inverted: ps.inverted[axis].get() as _, + }); self.client.event(AxisPx { self_id: self.id, dist, diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index a0e04b53..0bccd0f8 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -902,7 +902,7 @@ impl Global for WlSeatGlobal { } fn version(&self) -> u32 { - 8 + 9 } fn break_loops(&self) { diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index c565049e..2225ac18 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -15,9 +15,9 @@ use { wl_keyboard::{self, WlKeyboard}, wl_pointer::{ self, PendingScroll, WlPointer, AXIS_DISCRETE_SINCE_VERSION, - AXIS_SOURCE_SINCE_VERSION, AXIS_STOP_SINCE_VERSION, - AXIS_VALUE120_SINCE_VERSION, POINTER_FRAME_SINCE_VERSION, WHEEL_TILT, - WHEEL_TILT_SINCE_VERSION, + AXIS_RELATIVE_DIRECTION_SINCE_VERSION, AXIS_SOURCE_SINCE_VERSION, + AXIS_STOP_SINCE_VERSION, AXIS_VALUE120_SINCE_VERSION, IDENTICAL, INVERTED, + POINTER_FRAME_SINCE_VERSION, WHEEL_TILT, WHEEL_TILT_SINCE_VERSION, }, zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus}, zwp_relative_pointer_v1::ZwpRelativePointerV1, @@ -199,8 +199,16 @@ impl WlSeatGlobal { } => self.button_event(time_usec, button, state), InputEvent::AxisSource { source } => self.pointer_owner.axis_source(source), - InputEvent::Axis120 { dist, axis } => self.pointer_owner.axis_120(dist, axis), - InputEvent::AxisPx { dist, axis } => self.pointer_owner.axis_px(dist, axis), + InputEvent::Axis120 { + dist, + axis, + inverted, + } => self.pointer_owner.axis_120(dist, axis, inverted), + InputEvent::AxisPx { + dist, + axis, + inverted, + } => self.pointer_owner.axis_px(dist, axis, inverted), InputEvent::AxisStop { axis } => self.pointer_owner.axis_stop(axis), InputEvent::AxisFrame { time_usec } => self.pointer_owner.frame(dev, self, time_usec), } @@ -613,6 +621,13 @@ impl WlSeatGlobal { } } if let Some(delta) = event.px[i].get() { + if p.seat.version >= AXIS_RELATIVE_DIRECTION_SINCE_VERSION { + let direction = match event.inverted[i].get() { + false => IDENTICAL, + true => INVERTED, + }; + p.send_axis_relative_direction(axis, direction); + } p.send_axis(time, axis, delta); } if p.seat.version >= AXIS_STOP_SINCE_VERSION && event.stop[i].get() { diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index 7e057ad6..79746990 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -40,12 +40,14 @@ impl PointerOwnerHolder { self.pending_scroll.source.set(Some(axis_source as _)); } - pub fn axis_120(&self, delta: i32, axis: ScrollAxis) { + pub fn axis_120(&self, delta: i32, axis: ScrollAxis, inverted: bool) { self.pending_scroll.v120[axis as usize].set(Some(delta)); + self.pending_scroll.inverted[axis as usize].set(inverted); } - pub fn axis_px(&self, delta: Fixed, axis: ScrollAxis) { + pub fn axis_px(&self, delta: Fixed, axis: ScrollAxis, inverted: bool) { self.pending_scroll.px[axis as usize].set(Some(delta)); + self.pending_scroll.inverted[axis as usize].set(inverted); } pub fn axis_stop(&self, axis: ScrollAxis) { diff --git a/src/ifs/wl_seat/wl_pointer.rs b/src/ifs/wl_seat/wl_pointer.rs index 67f5297d..6bd04cc0 100644 --- a/src/ifs/wl_seat/wl_pointer.rs +++ b/src/ifs/wl_seat/wl_pointer.rs @@ -28,16 +28,21 @@ pub const CONTINUOUS: u32 = 2; #[allow(dead_code)] pub const WHEEL_TILT: u32 = 3; +pub const IDENTICAL: u32 = 0; +pub const INVERTED: u32 = 1; + pub const POINTER_FRAME_SINCE_VERSION: u32 = 5; pub const AXIS_SOURCE_SINCE_VERSION: u32 = 5; pub const AXIS_DISCRETE_SINCE_VERSION: u32 = 5; pub const AXIS_STOP_SINCE_VERSION: u32 = 5; pub const WHEEL_TILT_SINCE_VERSION: u32 = 6; pub const AXIS_VALUE120_SINCE_VERSION: u32 = 8; +pub const AXIS_RELATIVE_DIRECTION_SINCE_VERSION: u32 = 9; #[derive(Default, Debug)] pub struct PendingScroll { pub v120: [Cell>; 2], + pub inverted: [Cell; 2], pub px: [Cell>; 2], pub stop: [Cell; 2], pub source: Cell>, @@ -51,6 +56,10 @@ impl PendingScroll { Cell::new(self.v120[0].take()), Cell::new(self.v120[1].take()), ], + inverted: [ + Cell::new(self.inverted[0].take()), + Cell::new(self.inverted[1].take()), + ], px: [Cell::new(self.px[0].take()), Cell::new(self.px[1].take())], stop: [ Cell::new(self.stop[0].take()), @@ -114,6 +123,14 @@ impl WlPointer { }) } + pub fn send_axis_relative_direction(&self, axis: u32, direction: u32) { + self.seat.client.event(AxisRelativeDirection { + self_id: self.id, + axis, + direction, + }) + } + pub fn send_axis(&self, time: u32, axis: u32, value: Fixed) { self.seat.client.event(Axis { self_id: self.id, @@ -123,12 +140,10 @@ impl WlPointer { }) } - #[allow(dead_code)] pub fn send_frame(&self) { self.seat.client.event(Frame { self_id: self.id }) } - #[allow(dead_code)] pub fn send_axis_source(&self, axis_source: u32) { self.seat.client.event(AxisSource { self_id: self.id, @@ -136,7 +151,6 @@ impl WlPointer { }) } - #[allow(dead_code)] pub fn send_axis_stop(&self, time: u32, axis: u32) { self.seat.client.event(AxisStop { self_id: self.id, @@ -145,7 +159,6 @@ impl WlPointer { }) } - #[allow(dead_code)] pub fn send_axis_discrete(&self, axis: u32, discrete: i32) { self.seat.client.event(AxisDiscrete { self_id: self.id, diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 6d2bfa3b..d1c57d41 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -315,6 +315,7 @@ impl TestBackendMouse { self.common.event(InputEvent::Axis120 { dist: dy * 120, axis: ScrollAxis::Vertical, + inverted: false, }); self.common.event(InputEvent::AxisFrame { time_usec: now_usec(), @@ -328,6 +329,7 @@ impl TestBackendMouse { self.common.event(InputEvent::AxisPx { dist: Fixed::from_int(dy), axis: ScrollAxis::Vertical, + inverted: false, }); self.common.event(InputEvent::AxisFrame { time_usec: now_usec(), @@ -444,6 +446,10 @@ trait TestInputDevice: InputDevice { fn set_drag_lock_enabled(&self, enabled: bool) { let _ = enabled; } + + fn set_natural_scrolling_enabled(&self, enabled: bool) { + let _ = enabled; + } } impl InputDevice for T { @@ -502,4 +508,8 @@ impl InputDevice for T { fn set_drag_lock_enabled(&self, enabled: bool) { ::set_drag_lock_enabled(self, enabled) } + + fn set_natural_scrolling_enabled(&self, enabled: bool) { + ::set_natural_scrolling_enabled(self, enabled) + } } diff --git a/src/libinput/device.rs b/src/libinput/device.rs index b996b39f..b12f04bf 100644 --- a/src/libinput/device.rs +++ b/src/libinput/device.rs @@ -9,6 +9,8 @@ use { sys::{ libinput_device, libinput_device_config_accel_set_profile, libinput_device_config_accel_set_speed, libinput_device_config_left_handed_set, + libinput_device_config_scroll_get_natural_scroll_enabled, + libinput_device_config_scroll_set_natural_scroll_enabled, libinput_device_config_tap_get_drag_enabled, libinput_device_config_tap_get_drag_lock_enabled, libinput_device_config_tap_get_enabled, libinput_device_config_tap_set_drag_enabled, @@ -146,6 +148,16 @@ impl<'a> LibInputDevice<'a> { _ => false, } } + + pub fn set_natural_scrolling_enabled(&self, enabled: bool) { + unsafe { + libinput_device_config_scroll_set_natural_scroll_enabled(self.dev, enabled as _); + } + } + + pub fn natural_scrolling_enabled(&self) -> bool { + unsafe { libinput_device_config_scroll_get_natural_scroll_enabled(self.dev) != 0 } + } } impl RegisteredDevice { diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs index 151514bb..00f109b8 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -75,6 +75,13 @@ extern "C" { pub fn libinput_device_config_tap_get_drag_lock_enabled( device: *mut libinput_device, ) -> libinput_config_drag_lock_state; + pub fn libinput_device_config_scroll_set_natural_scroll_enabled( + device: *mut libinput_device, + enable: c::c_int, + ) -> libinput_config_status; + pub fn libinput_device_config_scroll_get_natural_scroll_enabled( + device: *mut libinput_device, + ) -> c::c_int; pub fn libinput_event_destroy(event: *mut libinput_event); pub fn libinput_event_get_type(event: *mut libinput_event) -> libinput_event_type; diff --git a/wire/jay_seat_events.txt b/wire/jay_seat_events.txt index 43c27c82..c1ed9c65 100644 --- a/wire/jay_seat_events.txt +++ b/wire/jay_seat_events.txt @@ -60,3 +60,8 @@ msg modifiers = 10 { modifiers: u32, group: u32, } + +msg axis_inverted = 11 { + inverted: u32, + axis: u32, +} diff --git a/wire/wl_pointer.txt b/wire/wl_pointer.txt index a9cdd155..f564c293 100644 --- a/wire/wl_pointer.txt +++ b/wire/wl_pointer.txt @@ -66,3 +66,8 @@ msg axis_value120 = 9 { axis: u32, value120: i32, } + +msg axis_relative_direction = 10 { + axis: u32, + direction: u32, +}