input: add basic touch support

This commit is contained in:
Christian Meissl
2024-02-24 18:32:13 +01:00
committed by Ivan Molodetskikh
parent 5ac350d51c
commit 719697179f
4 changed files with 168 additions and 6 deletions
+15
View File
@@ -62,6 +62,8 @@ pub struct Input {
pub trackpoint: Trackpoint,
#[knuffel(child, default)]
pub tablet: Tablet,
#[knuffel(child, default)]
pub touch: Touch,
#[knuffel(child)]
pub disable_power_key_handling: bool,
}
@@ -201,6 +203,12 @@ pub struct Tablet {
pub map_to_output: Option<String>,
}
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
pub struct Touch {
#[knuffel(child, unwrap(argument))]
pub map_to_output: Option<String>,
}
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
pub struct Output {
#[knuffel(child)]
@@ -1092,6 +1100,10 @@ mod tests {
map-to-output "eDP-1"
}
touch {
map-to-output "eDP-1"
}
disable-power-key-handling
}
@@ -1223,6 +1235,9 @@ mod tests {
tablet: Tablet {
map_to_output: Some("eDP-1".to_owned()),
},
touch: Touch {
map_to_output: Some("eDP-1".to_owned()),
},
disable_power_key_handling: true,
},
outputs: vec![Output {
+7
View File
@@ -54,6 +54,13 @@ input {
map-to-output "eDP-1"
}
touch {
// Set the name of the output (see below) which touch input will map to.
// If this is unset or the output doesn't exist, touch input maps to one of the
// existing outputs.
map-to-output "eDP-1"
}
// By default, niri will take over the power button to make it sleep
// instead of power off.
// Uncomment this if you would like to configure the power button elsewhere
+132 -6
View File
@@ -8,7 +8,7 @@ use smithay::backend::input::{
GestureBeginEvent, GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _,
InputBackend, InputEvent, KeyState, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
PointerMotionEvent, ProximityState, TabletToolButtonEvent, TabletToolEvent,
TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState,
TabletToolProximityEvent, TabletToolTipEvent, TabletToolTipState, TouchEvent,
};
use smithay::backend::libinput::LibinputInputBackend;
use smithay::input::keyboard::{keysyms, FilterResult, Keysym, ModifiersState};
@@ -17,6 +17,7 @@ use smithay::input::pointer::{
GesturePinchBeginEvent, GesturePinchEndEvent, GesturePinchUpdateEvent, GestureSwipeBeginEvent,
GestureSwipeEndEvent, GestureSwipeUpdateEvent, MotionEvent, RelativeMotionEvent,
};
use smithay::input::touch::{DownEvent, MotionEvent as TouchMotionEvent, UpEvent};
use smithay::utils::{Logical, Point, SERIAL_COUNTER};
use smithay::wayland::pointer_constraints::{with_pointer_constraint, PointerConstraint};
use smithay::wayland::tablet_manager::{TabletDescriptor, TabletSeatTrait};
@@ -101,11 +102,11 @@ impl State {
GesturePinchEnd { event } => self.on_gesture_pinch_end::<I>(event),
GestureHoldBegin { event } => self.on_gesture_hold_begin::<I>(event),
GestureHoldEnd { event } => self.on_gesture_hold_end::<I>(event),
TouchDown { .. } => (),
TouchMotion { .. } => (),
TouchUp { .. } => (),
TouchCancel { .. } => (),
TouchFrame { .. } => (),
TouchDown { event } => self.on_touch_down::<I>(event),
TouchMotion { event } => self.on_touch_motion::<I>(event),
TouchUp { event } => self.on_touch_up::<I>(event),
TouchCancel { event } => self.on_touch_cancel::<I>(event),
TouchFrame { event } => self.on_touch_frame::<I>(event),
SwitchToggle { .. } => (),
Special(_) => (),
}
@@ -154,9 +155,14 @@ impl State {
}
}
if device.has_capability(input::DeviceCapability::Touch) {
self.niri.touch.insert(device.clone());
}
apply_libinput_settings(&self.niri.config.borrow().input, device);
}
InputEvent::DeviceRemoved { device } => {
self.niri.touch.remove(device);
self.niri.tablets.remove(device);
self.niri.devices.remove(device);
}
@@ -171,6 +177,9 @@ impl State {
let desc = TabletDescriptor::from(&device);
tablet_seat.add_tablet::<Self>(&self.niri.display_handle, &desc);
}
if device.has_capability(DeviceCapability::Touch) && self.niri.seat.get_touch().is_none() {
self.niri.seat.add_touch();
}
}
fn on_device_removed(&mut self, device: impl Device) {
@@ -185,6 +194,9 @@ impl State {
tablet_seat.clear_tools();
}
}
if device.has_capability(DeviceCapability::Touch) && self.niri.touch.is_empty() {
self.niri.seat.remove_touch();
}
}
/// Computes the cursor position for the tablet event.
@@ -283,6 +295,10 @@ impl State {
return;
}
if let Some(touch) = self.niri.seat.get_touch() {
touch.cancel(self);
}
match action {
Action::Quit(skip_confirmation) => {
if !skip_confirmation {
@@ -1354,6 +1370,116 @@ impl State {
},
);
}
/// Computes the cursor position for the touch event.
///
/// This function handles the touch output mapping, as well as coordinate transform
fn compute_touch_location<I: InputBackend, E: AbsolutePositionEvent<I>>(
&self,
evt: &E,
) -> Option<Point<f64, Logical>> {
let output = self.niri.output_for_touch()?;
let output_geo = self.niri.global_space.output_geometry(output).unwrap();
let transform = output.current_transform();
let size = transform.invert().transform_size(output_geo.size);
Some(
transform.transform_point_in(evt.position_transformed(size), &size.to_f64())
+ output_geo.loc.to_f64(),
)
}
fn on_touch_down<I: InputBackend>(&mut self, evt: I::TouchDownEvent) {
let Some(handle) = self.niri.seat.get_touch() else {
return;
};
let Some(touch_location) = self.compute_touch_location(&evt) else {
return;
};
if !handle.is_grabbed() {
let output_under_touch = self
.niri
.global_space
.output_under(touch_location)
.next()
.cloned();
if let Some(window) = self.niri.window_under(touch_location) {
let window = window.clone();
self.niri.layout.activate_window(&window);
// FIXME: granular.
self.niri.queue_redraw_all();
} else if let Some(output) = output_under_touch {
self.niri.layout.activate_output(&output);
// FIXME: granular.
self.niri.queue_redraw_all();
};
};
let serial = SERIAL_COUNTER.next_serial();
let under = self
.niri
.surface_under_and_global_space(touch_location)
.map(|under| under.surface);
handle.down(
self,
under,
&DownEvent {
slot: evt.slot(),
location: touch_location,
serial,
time: evt.time_msec(),
},
);
}
fn on_touch_up<I: InputBackend>(&mut self, evt: I::TouchUpEvent) {
let Some(handle) = self.niri.seat.get_touch() else {
return;
};
let serial = SERIAL_COUNTER.next_serial();
handle.up(
self,
&UpEvent {
slot: evt.slot(),
serial,
time: evt.time_msec(),
},
)
}
fn on_touch_motion<I: InputBackend>(&mut self, evt: I::TouchMotionEvent) {
let Some(handle) = self.niri.seat.get_touch() else {
return;
};
let Some(touch_location) = self.compute_touch_location(&evt) else {
return;
};
let under = self
.niri
.surface_under_and_global_space(touch_location)
.map(|under| under.surface);
handle.motion(
self,
under,
&TouchMotionEvent {
slot: evt.slot(),
location: touch_location,
time: evt.time_msec(),
},
);
}
fn on_touch_frame<I: InputBackend>(&mut self, _evt: I::TouchFrameEvent) {
let Some(handle) = self.niri.seat.get_touch() else {
return;
};
handle.frame(self);
}
fn on_touch_cancel<I: InputBackend>(&mut self, _evt: I::TouchCancelEvent) {
let Some(handle) = self.niri.seat.get_touch() else {
return;
};
handle.cancel(self);
}
}
/// Check whether the key should be intercepted and mark intercepted
+14
View File
@@ -144,6 +144,7 @@ pub struct Niri {
pub devices: HashSet<input::Device>,
pub tablets: HashMap<input::Device, TabletData>,
pub touch: HashSet<input::Device>,
// Smithay state.
pub compositor_state: CompositorState,
@@ -702,6 +703,10 @@ impl State {
self.niri.reposition_outputs(None);
self.backend.on_output_config_changed(&mut self.niri);
if let Some(touch) = self.niri.seat.get_touch() {
touch.cancel(self);
}
}
// Can't really update xdg-decoration settings since we have to hide the globals for CSD
@@ -1002,6 +1007,7 @@ impl Niri {
devices: HashSet::new(),
tablets: HashMap::new(),
touch: HashSet::new(),
compositor_state,
xdg_shell_state,
@@ -1602,6 +1608,14 @@ impl Niri {
.or_else(|| self.global_space.outputs().next())
}
pub fn output_for_touch(&self) -> Option<&Output> {
let config = self.config.borrow();
let map_to_output = config.input.touch.map_to_output.as_ref();
map_to_output
.and_then(|name| self.output_by_name.get(name))
.or_else(|| self.global_space.outputs().next())
}
pub fn output_for_root(&self, root: &WlSurface) -> Option<&Output> {
// Check the main layout.
let win_out = self.layout.find_window_and_output(root);