diff --git a/cosmic-comp-config/src/input.rs b/cosmic-comp-config/src/input.rs index 90f49c24..5ac4889e 100644 --- a/cosmic-comp-config/src/input.rs +++ b/cosmic-comp-config/src/input.rs @@ -27,6 +27,8 @@ pub struct InputConfig { pub scroll_config: Option, #[serde(skip_serializing_if = "Option::is_none", default)] pub tap_config: Option, + #[serde(skip_serializing_if = "Option::is_none", default)] + pub map_to_output: Option, } #[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)] diff --git a/src/config/input_config.rs b/src/config/input_config.rs index 063e80cf..6371a23b 100644 --- a/src/config/input_config.rs +++ b/src/config/input_config.rs @@ -79,6 +79,7 @@ pub fn for_device(device: &InputDevice) -> InputConfig { } else { None }, + map_to_output: None, } } diff --git a/src/config/mod.rs b/src/config/mod.rs index fff0340e..a81ae6fd 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -426,6 +426,16 @@ impl Config { .map_or(1.0, |x| x.0) } + pub fn map_to_output(&self, device: &InputDevice) -> Option<&str> { + let (device_config, default_config) = self.get_device_config(device); + Some( + input_config::get_config(device_config, default_config, |x| { + x.map_to_output.as_deref() + })? + .0, + ) + } + fn get_device_config(&self, device: &InputDevice) -> (Option<&InputConfig>, &InputConfig) { let default_config = if device.config_tap_finger_count() > 0 { &self.input_touchpad diff --git a/src/input/mod.rs b/src/input/mod.rs index 0764b8ce..6f94dcf6 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -2,7 +2,7 @@ use crate::{ backend::render::cursor::CursorState, - config::{xkb_config_to_wl, Action, Config, KeyPattern, KeyModifiers}, + config::{xkb_config_to_wl, Action, Config, KeyModifiers, KeyPattern}, shell::{ focus::{target::PointerFocusTarget, FocusDirection}, grabs::{ResizeEdge, SeatMenuGrabState, SeatMoveGrabState}, @@ -25,9 +25,9 @@ use cosmic_comp_config::workspace::WorkspaceLayout; use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; use smithay::{ backend::input::{ - Axis, AxisSource, Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, - GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, InputBackend, InputEvent, - KeyState, PointerAxisEvent, + AbsolutePositionEvent, Axis, AxisSource, Device, DeviceCapability, GestureBeginEvent, + GestureEndEvent, GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, InputBackend, + InputEvent, KeyState, PointerAxisEvent, TouchEvent, }, desktop::{layer_map_for_output, space::SpaceElement, WindowSurfaceType}, input::{ @@ -41,7 +41,12 @@ use smithay::{ Seat, SeatState, }, output::Output, - reexports::wayland_server::DisplayHandle, + reexports::{ + input::event::touch::{ + TouchDownEvent as LibinputTouchDownEvent, TouchMotionEvent as LibinputTouchMotionEvent, + }, + wayland_server::DisplayHandle, + }, utils::{Point, Serial, SERIAL_COUNTER}, wayland::{ keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, @@ -205,6 +210,7 @@ pub fn add_seat( .expect("Failed to load xkb configuration files"); } seat.add_pointer(); + seat.add_touch(); seat } @@ -216,6 +222,8 @@ impl State { needs_key_repetition: bool, ) where ::PointerAxisEvent: 'static, + ::TouchDownEvent: 'static, + ::TouchMotionEvent: 'static, { use smithay::backend::input::Event; match event { @@ -1171,7 +1179,127 @@ impl State { ); } } - _ => { /* TODO e.g. tablet or touch events */ } + InputEvent::TouchDown { event, .. } => { + if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + // TODO Is it possible to determine mapping for external touchscreen? + let map_to_output = if let Some(event) = + ::downcast_ref::(&event) + { + self.common + .config + .map_to_output(&event.device()) + .and_then(|name| { + self.common + .shell + .outputs() + .find(|output| output.name() == name) + }) + } else { + None + }; + let Some(output) = map_to_output.or_else(|| self.common.shell.builtin_output()).cloned() else { + return; + }; + + let geometry = output.geometry(); + + let position = geometry.loc.to_f64() + + event + .position_transformed(geometry.size.as_logical()) + .as_global(); + + let overview = self.common.shell.overview_mode(); + let workspace = self.common.shell.workspaces.active_mut(&output); + let under = State::surface_under( + position, + &output, + &self.common.shell.override_redirect_windows, + overview.0.clone(), + workspace, + self.common.shell.session_lock.as_ref(), + ) + .map(|(target, pos)| (target, pos.as_logical())); + + if let Some((target, pos)) = under { + if let Some(wl_surface) = target.wl_surface() { + let serial = SERIAL_COUNTER.next_serial(); + let touch = seat.get_touch().unwrap(); + touch.down( + serial, + event.time_msec(), + &wl_surface, + position.as_logical() - pos.to_f64(), + event.slot(), + ); + } + } + } + } + InputEvent::TouchMotion { event, .. } => { + if let Some(seat) = self.common.seat_with_device(&event.device()).cloned() { + let map_to_output = if let Some(event) = + ::downcast_ref::(&event) + { + self.common + .config + .map_to_output(&event.device()) + .and_then(|name| { + self.common + .shell + .outputs() + .find(|output| output.name() == name) + }) + } else { + None + }; + let Some(output) = map_to_output.or_else(|| self.common.shell.builtin_output()).cloned() else { + return; + }; + + let geometry = output.geometry(); + + let position = geometry.loc.to_f64() + + event + .position_transformed(geometry.size.as_logical()) + .as_global(); + + let overview = self.common.shell.overview_mode(); + let workspace = self.common.shell.workspaces.active_mut(&output); + let under = State::surface_under( + position, + &output, + &self.common.shell.override_redirect_windows, + overview.0.clone(), + workspace, + self.common.shell.session_lock.as_ref(), + ) + .map(|(target, pos)| (target, pos.as_logical())); + + if let Some((_target, pos)) = under { + let touch = seat.get_touch().unwrap(); + touch.motion( + event.time_msec(), + event.slot(), + position.as_logical() - pos.to_f64(), + ); + } + } + } + InputEvent::TouchUp { event, .. } => { + if let Some(seat) = self.common.seat_with_device(&event.device()) { + let serial = SERIAL_COUNTER.next_serial(); + let touch = seat.get_touch().unwrap(); + touch.up(serial, event.time_msec(), event.slot()); + } + } + InputEvent::TouchCancel { event, .. } => { + if let Some(seat) = self.common.seat_with_device(&event.device()) { + let touch = seat.get_touch().unwrap(); + touch.cancel(); + } + } + InputEvent::TouchFrame { event: _, .. } => {} + _ => { /* TODO e.g. tablet events */ } } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index a183a708..50e38c1e 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1326,6 +1326,13 @@ impl Shell { .map(|(o, _)| o) } + pub fn builtin_output(&self) -> Option<&Output> { + self.outputs().find(|output| { + let name = output.name(); + name.starts_with("eDP-") || name.starts_with("LVDS-") || name.starts_with("DSI-") + }) + } + pub fn global_space(&self) -> Rectangle { self.outputs() .fold(