diff --git a/docs/features.md b/docs/features.md index 3d1feb0e..d692fde1 100644 --- a/docs/features.md +++ b/docs/features.md @@ -133,7 +133,7 @@ Jay supports the following wayland protocols: | ext_session_lock_manager_v1 | 1 | Yes | | ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes | | org_kde_kwin_server_decoration_manager | 1 | | -| wl_compositor | 6[^no_touch] | | +| wl_compositor | 6 | | | wl_data_device_manager | 3 | | | wl_drm | 2 | | | wl_output | 4 | | @@ -171,7 +171,6 @@ Jay supports the following wayland protocols: | zxdg_decoration_manager_v1 | 1 | | | zxdg_output_manager_v1 | 3 | | -[^no_touch]: Touch input is not supported. [^no_tearing]: Tearing screen updates are not supported. [^no_exclusive]: Exclusive zones are not supported. [^lsaccess]: Sandboxes can restrict access to this protocol. @@ -182,5 +181,4 @@ Jay supports the following wayland protocols: The following features are currently not supported but might get implemented in the future: - Fine-grained damage tracking. -- Touch support. - Tearing updates of fullscreen games. diff --git a/src/backend.rs b/src/backend.rs index 421fcffe..b8166c24 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -231,6 +231,23 @@ pub enum KeyState { Pressed, } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct TouchPosition { + pub x: Fixed, + pub y: Fixed, + pub x_transformed: Fixed, + pub y_transformed: Fixed, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum TouchEvent { + Down { pos: TouchPosition }, + Up, + Motion { pos: TouchPosition }, + Cancel, + Frame, +} + #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum ScrollAxis { Horizontal = HORIZONTAL_SCROLL as _, @@ -272,6 +289,12 @@ pub enum InputEvent { state: KeyState, }, + Touch { + seat_slot: i32, + time_usec: u64, + event: TouchEvent, + }, + AxisPx { dist: Fixed, axis: ScrollAxis, diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index c9788993..8ac01889 100644 --- a/src/backends/metal/input.rs +++ b/src/backends/metal/input.rs @@ -1,6 +1,6 @@ use { crate::{ - backend::{AxisSource, InputEvent, KeyState, ScrollAxis}, + backend::{AxisSource, InputEvent, KeyState, ScrollAxis, TouchEvent, TouchPosition}, backends::metal::MetalBackend, fixed::Fixed, ifs::wl_seat::tablet::{ @@ -121,6 +121,11 @@ impl MetalBackend { c::LIBINPUT_EVENT_TABLET_PAD_BUTTON => self.handle_tablet_pad_button(event), c::LIBINPUT_EVENT_TABLET_PAD_RING => self.handle_tablet_pad_ring(event), c::LIBINPUT_EVENT_TABLET_PAD_STRIP => self.handle_tablet_pad_strip(event), + c::LIBINPUT_EVENT_TOUCH_DOWN => self.handle_touch_down(event), + c::LIBINPUT_EVENT_TOUCH_UP => self.handle_touch_up(event), + c::LIBINPUT_EVENT_TOUCH_MOTION => self.handle_touch_motion(event), + c::LIBINPUT_EVENT_TOUCH_CANCEL => self.handle_touch_cancel(event), + c::LIBINPUT_EVENT_TOUCH_FRAME => self.handle_touch_frame(event), _ => {} } } @@ -545,4 +550,61 @@ impl MetalBackend { }, }); } + + fn handle_touch_down(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + let pos = TouchPosition { + x: Fixed::from_f64(event.x()), + y: Fixed::from_f64(event.y()), + x_transformed: Fixed::from_f64(event.x_transformed(1)), + y_transformed: Fixed::from_f64(event.y_transformed(1)), + }; + dev.event(InputEvent::Touch { + seat_slot: event.seat_slot(), + time_usec: event.time_usec(), + event: TouchEvent::Down { pos }, + }) + } + + fn handle_touch_up(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + dev.event(InputEvent::Touch { + seat_slot: event.seat_slot(), + time_usec: event.time_usec(), + event: TouchEvent::Up, + }) + } + + fn handle_touch_motion(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + let pos = TouchPosition { + x: Fixed::from_f64(event.x()), + y: Fixed::from_f64(event.y()), + x_transformed: Fixed::from_f64(event.x_transformed(1)), + y_transformed: Fixed::from_f64(event.y_transformed(1)), + }; + dev.event(InputEvent::Touch { + seat_slot: event.seat_slot(), + time_usec: event.time_usec(), + event: TouchEvent::Motion { pos }, + }) + } + + fn handle_touch_cancel(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + dev.event(InputEvent::Touch { + seat_slot: event.seat_slot(), + time_usec: event.time_usec(), + event: TouchEvent::Cancel, + }) + } + + fn handle_touch_frame(self: &Rc, event: LibInputEvent) { + let (event, dev) = unpack!(self, event, touch_event); + dev.event(InputEvent::Touch { + seat_slot: 0, + time_usec: event.time_usec(), + event: TouchEvent::Frame, + }) + } } diff --git a/src/cli/seat_test.rs b/src/cli/seat_test.rs index cdc8422e..024bc0d7 100644 --- a/src/cli/seat_test.rs +++ b/src/cli/seat_test.rs @@ -15,7 +15,8 @@ use { TabletPadStripSource, TabletPadStripStop, TabletToolButton, TabletToolDistance, TabletToolDown, TabletToolFrame, TabletToolMotion, TabletToolPressure, TabletToolProximityIn, TabletToolProximityOut, TabletToolRotation, - TabletToolSlider, TabletToolTilt, TabletToolUp, TabletToolWheel, + TabletToolSlider, TabletToolTilt, TabletToolUp, TabletToolWheel, TouchCancel, + TouchDown, TouchFrame, TouchMotion, TouchUp, }, }, }, @@ -583,6 +584,63 @@ async fn run(seat_test: Rc) { } println!(); }); + let st = seat_test.clone(); + TouchDown::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!( + "Time: {:.4}, Touch: {}, Down: {}x{}", + time(ev.time_usec), + ev.id, + ev.x, + ev.y + ); + } + }); + let st = seat_test.clone(); + TouchUp::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!("Time: {:.4}, Touch: {}, Up", time(ev.time_usec), ev.id); + } + }); + let st = seat_test.clone(); + TouchMotion::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!( + "Time: {:.4}, Touch: {} Motion: {}x{}", + time(ev.time_usec), + ev.id, + ev.x, + ev.y + ); + } + }); + let st = seat_test.clone(); + TouchFrame::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!("Time: {:.4}, Touch: {}, Frame", time(ev.time_usec), ev.id); + } + }); + let st = seat_test.clone(); + TouchCancel::handle(tc, se, (), move |_, ev| { + if all || ev.seat == seat { + if all { + print!("Seat: {}, ", st.name(ev.seat)); + } + println!("Time: {:.4}, Touch: {}, Cancel", time(ev.time_usec), ev.id); + } + }); pending::<()>().await; } diff --git a/src/compositor.rs b/src/compositor.rs index 065629a6..320398ec 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -156,6 +156,7 @@ fn start_compositor2( connector_ids: Default::default(), root: Rc::new(DisplayNode::new(node_ids.next())), workspaces: Default::default(), + builtin_output: Default::default(), dummy_output: Default::default(), node_ids, backend_events: AsyncQueue::new(), diff --git a/src/ifs/jay_seat_events.rs b/src/ifs/jay_seat_events.rs index 9df7d121..8c65fd45 100644 --- a/src/ifs/jay_seat_events.rs +++ b/src/ifs/jay_seat_events.rs @@ -1,6 +1,6 @@ use { crate::{ - backend::{InputDeviceId, KeyState}, + backend::{InputDeviceId, KeyState, TouchEvent}, client::Client, fixed::Fixed, ifs::wl_seat::{ @@ -468,6 +468,55 @@ impl JaySeatEvents { ring, }); } + + pub fn send_touch(&self, seat: SeatId, time_usec: u64, id: i32, event: TouchEvent) { + match event { + TouchEvent::Down { pos } => { + self.client.event(TouchDown { + self_id: self.id, + seat: seat.raw(), + time_usec, + id, + x: pos.x_transformed, + y: pos.y_transformed, + }); + } + TouchEvent::Up => { + self.client.event(TouchUp { + self_id: self.id, + seat: seat.raw(), + time_usec, + id, + }); + } + TouchEvent::Motion { pos } => { + self.client.event(TouchMotion { + self_id: self.id, + seat: seat.raw(), + time_usec, + id, + x: pos.x_transformed, + y: pos.y_transformed, + }); + } + TouchEvent::Frame => { + self.client.event(TouchFrame { + self_id: self.id, + seat: seat.raw(), + time_usec, + id, + }); + } + TouchEvent::Cancel => { + self.client.event(TouchCancel { + self_id: self.id, + seat: seat.raw(), + time_usec, + id, + }); + } + } + } } impl JaySeatEventsRequestHandler for JaySeatEvents { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index b3eb8cc7..748a8a2c 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -6,6 +6,7 @@ mod kb_owner; mod pointer_owner; pub mod tablet; pub mod text_input; +mod touch_owner; pub mod wl_keyboard; pub mod wl_pointer; pub mod wl_touch; @@ -22,6 +23,7 @@ pub mod zwp_virtual_keyboard_v1; use { crate::{ async_engine::SpawnedFuture, + backend::InputDeviceCapability, client::{Client, ClientError, ClientId}, cursor_user::{CursorUser, CursorUserGroup, CursorUserOwner}, fixed::Fixed, @@ -52,6 +54,7 @@ use { zwp_input_method_keyboard_grab_v2::ZwpInputMethodKeyboardGrabV2, zwp_input_method_v2::ZwpInputMethodV2, zwp_text_input_v3::ZwpTextInputV3, }, + touch_owner::TouchOwnerHolder, wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE}, wl_pointer::WlPointer, wl_touch::WlTouch, @@ -80,7 +83,7 @@ use { }, wire::{ wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId, - WlSeatId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, + WlSeatId, WlTouchId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, ZwpTextInputV3Id, }, xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState}, @@ -104,7 +107,6 @@ pub use { pub const POINTER: u32 = 1; const KEYBOARD: u32 = 2; -#[allow(dead_code)] const TOUCH: u32 = 4; #[allow(dead_code)] @@ -148,6 +150,7 @@ pub struct WlSeatGlobal { pointer_stack_modified: Cell, found_tree: RefCell>, keyboard_node: CloneCell>, + touch_found_tree: RefCell>, bindings: RefCell>>>, x_data_devices: SmallMap, 1>, data_devices: RefCell>>>, @@ -174,6 +177,7 @@ pub struct WlSeatGlobal { pointer_owner: PointerOwnerHolder, kb_owner: KbOwnerHolder, gesture_owner: GestureOwnerHolder, + touch_owner: TouchOwnerHolder, dropped_dnd: RefCell>, shortcuts: RefCell>>, queue_link: RefCell>>>, @@ -219,6 +223,7 @@ impl WlSeatGlobal { pointer_stack_modified: Cell::new(false), found_tree: RefCell::new(vec![]), keyboard_node: CloneCell::new(state.root.clone()), + touch_found_tree: RefCell::new(vec![]), bindings: Default::default(), x_data_devices: Default::default(), data_devices: RefCell::new(Default::default()), @@ -238,6 +243,7 @@ impl WlSeatGlobal { pointer_owner: Default::default(), kb_owner: Default::default(), gesture_owner: Default::default(), + touch_owner: Default::default(), dropped_dnd: RefCell::new(None), shortcuts: Default::default(), queue_link: Default::default(), @@ -839,6 +845,7 @@ impl WlSeatGlobal { mem::take(self.pointer_stack.borrow_mut().deref_mut()); mem::take(self.found_tree.borrow_mut().deref_mut()); self.keyboard_node.set(self.state.root.clone()); + mem::take(self.touch_found_tree.borrow_mut().deref_mut()); self.state .root .clone() @@ -854,6 +861,7 @@ impl WlSeatGlobal { self.primary_selection.set(None); self.pointer_owner.clear(); self.kb_owner.clear(); + self.touch_owner.clear(); *self.dropped_dnd.borrow_mut() = None; self.queue_link.take(); self.tree_changed_handler.set(None); @@ -883,13 +891,24 @@ impl WlSeatGlobal { client: &Rc, version: Version, ) -> Result<(), WlSeatError> { + let mut capabilities = POINTER | KEYBOARD; + let handlers = &self.state.input_device_handlers; + for (_, d) in handlers.borrow().iter() { + let dev = &d.data.device; + if dev.has_capability(InputDeviceCapability::Touch) { + capabilities |= TOUCH; + break; + } + } let obj = Rc::new(WlSeat { global: self.clone(), id, client: client.clone(), + capabilities, pointers: Default::default(), relative_pointers: Default::default(), keyboards: Default::default(), + touches: Default::default(), version, tracker: Default::default(), }); @@ -988,9 +1007,11 @@ pub struct WlSeat { pub global: Rc, pub id: WlSeatId, pub client: Rc, + capabilities: u32, pointers: CopyHashMap>, relative_pointers: CopyHashMap>, keyboards: CopyHashMap>, + touches: CopyHashMap>, version: Version, tracker: Tracker, } @@ -1001,7 +1022,7 @@ impl WlSeat { fn send_capabilities(self: &Rc) { self.client.event(Capabilities { self_id: self.id, - capabilities: POINTER | KEYBOARD, + capabilities: self.capabilities, }) } @@ -1053,10 +1074,23 @@ impl WlSeatRequestHandler for WlSeat { } fn get_touch(&self, req: GetTouch, slf: &Rc) -> Result<(), Self::Error> { - let p = Rc::new(WlTouch::new(req.id, slf)); - track!(self.client, p); - self.client.add_client_obj(&p)?; - Ok(()) + if self.capabilities & TOUCH == 0 { + self.client.protocol_error( + self, + MISSING_CAPABILITY, + &format!( + "wl_seat {} .get_touch called when no touch capability has existed", + self.id + ), + ); + Err(WlSeatError::MissingCapability("touch")) + } else { + let p = Rc::new(WlTouch::new(req.id, slf)); + track!(self.client, p); + self.client.add_client_obj(&p)?; + self.touches.set(req.id, p); + Ok(()) + } } fn release(&self, _req: Release, _slf: &Rc) -> Result<(), Self::Error> { @@ -1093,6 +1127,7 @@ impl Object for WlSeat { self.pointers.clear(); self.relative_pointers.clear(); self.keyboards.clear(); + self.touches.clear(); } } @@ -1106,6 +1141,8 @@ pub enum WlSeatError { IpcError(#[from] IpcError), #[error(transparent)] WlKeyboardError(Box), + #[error("Seat is missing `{0}` capability")] + MissingCapability(&'static str), #[error("Data source has a toplevel attached")] OfferHasDrag, } diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index be5cab5a..44d7c698 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -1,6 +1,6 @@ use { crate::{ - backend::{ConnectorId, InputDeviceId, InputEvent, KeyState, AXIS_120}, + backend::{ConnectorId, InputDeviceId, InputEvent, KeyState, TouchEvent, AXIS_120}, client::ClientId, config::InvokedShortcut, fixed::Fixed, @@ -24,6 +24,7 @@ use { AXIS_STOP_SINCE_VERSION, AXIS_VALUE120_SINCE_VERSION, IDENTICAL, INVERTED, POINTER_FRAME_SINCE_VERSION, WHEEL_TILT, WHEEL_TILT_SINCE_VERSION, }, + wl_touch::WlTouch, zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus}, zwp_relative_pointer_v1::ZwpRelativePointerV1, Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED, CHANGE_TREE, @@ -54,6 +55,7 @@ pub struct NodeSeatState { pointer_foci: SmallMap, 1>, kb_foci: SmallMap, 1>, gesture_foci: SmallMap, 1>, + touch_foci: SmallMap, 1>, pointer_grabs: SmallMap, 1>, dnd_targets: SmallMap, 1>, tablet_pad_foci: SmallMap, 1>, @@ -111,6 +113,14 @@ impl NodeSeatState { self.tablet_tool_foci.remove(&tool.id); } + pub(super) fn touch_begin(&self, seat: &Rc) { + self.touch_foci.insert(seat.id, seat.clone()); + } + + pub(super) fn touch_end(&self, seat: &WlSeatGlobal) { + self.touch_foci.remove(&seat.id); + } + pub(super) fn add_dnd_target(&self, seat: &Rc) { self.dnd_targets.insert(seat.id, seat.clone()); } @@ -188,6 +198,9 @@ impl NodeSeatState { while let Some((_, pad)) = self.tablet_pad_foci.pop() { pad.pad_owner.focus_root(&pad); } + while let Some((_, seat)) = self.touch_foci.pop() { + seat.touch_owner.clear(); + } self.release_kb_focus2(focus_last); } @@ -234,7 +247,8 @@ impl WlSeatGlobal { | InputEvent::TabletPadButton { time_usec, .. } | InputEvent::TabletPadModeSwitch { time_usec, .. } | InputEvent::TabletPadRing { time_usec, .. } - | InputEvent::TabletPadStrip { time_usec, .. } => { + | InputEvent::TabletPadStrip { time_usec, .. } + | InputEvent::Touch { time_usec, .. } => { self.last_input_usec.set(time_usec); if self.idle_notifications.is_not_empty() { for (_, notification) in self.idle_notifications.lock().drain() { @@ -278,6 +292,7 @@ impl WlSeatGlobal { InputEvent::TabletPadModeSwitch { .. } => {} InputEvent::TabletPadRing { .. } => {} InputEvent::TabletPadStrip { .. } => {} + InputEvent::Touch { .. } => {} } match event { InputEvent::Key { @@ -411,6 +426,11 @@ impl WlSeatGlobal { source, position, } => self.tablet_event_pad_strip(pad, strip, source, position, time_usec), + InputEvent::Touch { + seat_slot, + time_usec, + event, + } => self.touch_event(dev, seat_slot, time_usec, event), } } @@ -599,6 +619,52 @@ impl WlSeatGlobal { } } + fn touch_event( + self: &Rc, + dev: &DeviceHandlerData, + id: i32, + time_usec: u64, + event: TouchEvent, + ) { + self.state.for_each_seat_tester(|t| { + t.send_touch(self.id, time_usec, id, event); + }); + match event { + TouchEvent::Down { pos } => { + let mapped_node = dev + .output + .get() + .and_then(|out| out.node.get()) + .or_else(|| { + self.state + .builtin_output + .get() + .and_then(|con| self.state.root.outputs.get(&con)) + }) + .map(|o| o as Rc) + .unwrap_or_else(|| self.state.root.clone()); + let x = pos.x_transformed; + let y = pos.y_transformed; + self.touch_owner + .down(self, mapped_node, time_usec, id, x, y); + } + TouchEvent::Up => { + self.touch_owner.up(self, time_usec, id); + } + TouchEvent::Motion { pos } => { + let x = pos.x_transformed; + let y = pos.y_transformed; + self.touch_owner.motion(self, time_usec, id, x, y); + } + TouchEvent::Frame => { + self.touch_owner.frame(self); + } + TouchEvent::Cancel => { + self.touch_owner.cancel(self); + } + } + } + pub(super) fn key_event( self: &Rc, time_usec: u64, @@ -780,6 +846,18 @@ impl WlSeatGlobal { }) } + fn for_each_touch(&self, ver: Version, client: ClientId, mut f: C) + where + C: FnMut(&Rc), + { + self.for_each_seat(ver, client, |seat| { + let touches = seat.touches.lock(); + for touch in touches.values() { + f(touch); + } + }) + } + pub fn for_each_data_device(&self, ver: Version, client: ClientId, mut f: C) where C: FnMut(&Rc), @@ -855,6 +933,16 @@ impl WlSeatGlobal { // client.flush(); } + pub fn surface_touch_event(&self, ver: Version, surface: &WlSurface, mut f: F) + where + F: FnMut(&Rc), + { + let client = &surface.client; + self.for_each_touch(ver, client.id, |p| { + f(p); + }); + } + fn cursor_moved(self: &Rc, time_usec: u64) { self.pos_time_usec.set(time_usec); self.changes.or_assign(CHANGE_CURSOR_MOVED); @@ -1120,6 +1208,53 @@ impl WlSeatGlobal { } } +// Touch callbacks +impl WlSeatGlobal { + pub fn touch_down_surface( + self: &Rc, + surface: &WlSurface, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let serial = surface.client.next_serial(); + let time = (time_usec / 1000) as _; + self.surface_touch_event(Version::ALL, surface, |t| { + t.send_down(serial, time, surface.id, id, x, y) + }); + if let Some(node) = surface.get_focus_node(self.id) { + self.focus_node(node); + } + } + + pub fn touch_up_surface(&self, surface: &WlSurface, time_usec: u64, id: i32) { + let serial = surface.client.next_serial(); + let time = (time_usec / 1000) as _; + self.surface_touch_event(Version::ALL, surface, |t| t.send_up(serial, time, id)) + } + + pub fn touch_motion_surface( + &self, + surface: &WlSurface, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let time = (time_usec / 1000) as _; + self.surface_touch_event(Version::ALL, surface, |t| t.send_motion(time, id, x, y)); + } + + pub fn touch_frame(&self, surface: &WlSurface) { + self.surface_touch_event(Version::ALL, surface, |t| t.send_frame()) + } + + pub fn touch_cancel(&self, surface: &WlSurface) { + self.surface_touch_event(Version::ALL, surface, |t| t.send_cancel()) + } +} + // Dnd callbacks impl WlSeatGlobal { pub fn dnd_surface_leave(&self, surface: &WlSurface, dnd: &Dnd) { diff --git a/src/ifs/wl_seat/touch_owner.rs b/src/ifs/wl_seat/touch_owner.rs new file mode 100644 index 00000000..22040448 --- /dev/null +++ b/src/ifs/wl_seat/touch_owner.rs @@ -0,0 +1,197 @@ +use { + crate::{ + fixed::Fixed, + ifs::wl_seat::WlSeatGlobal, + rect::Rect, + tree::{FindTreeUsecase, FoundNode, Node}, + utils::clonecell::CloneCell, + }, + ahash::AHashSet, + std::{cell::RefCell, rc::Rc}, +}; + +pub struct TouchOwnerHolder { + default: Rc, + owner: CloneCell>, +} + +impl Default for TouchOwnerHolder { + fn default() -> Self { + Self { + default: Rc::new(DefaultTouchOwner), + owner: CloneCell::new(Rc::new(DefaultTouchOwner)), + } + } +} + +impl TouchOwnerHolder { + pub fn down( + &self, + seat: &Rc, + mapped_node: Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + self.owner + .get() + .down(seat, mapped_node, time_usec, id, x, y) + } + + pub fn up(&self, seat: &Rc, time_usec: u64, id: i32) { + self.owner.get().up(seat, time_usec, id) + } + + pub fn motion(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed) { + self.owner.get().motion(seat, time_usec, id, x, y) + } + + pub fn frame(&self, seat: &Rc) { + self.owner.get().frame(seat) + } + + pub fn cancel(&self, seat: &Rc) { + self.owner.get().cancel(seat) + } + + pub fn clear(&self) { + self.owner.set(self.default.clone()); + } +} + +fn transform_abs(x: Fixed, y: Fixed, pos: Rect) -> (Fixed, Fixed) { + let x = Fixed::from_f64(x.to_f64() * f64::from(pos.width())); + let y = Fixed::from_f64(y.to_f64() * f64::from(pos.height())); + (x, y) +} + +fn transform_rel(x: Fixed, y: Fixed, pos: Rect) -> (Fixed, Fixed) { + (x - pos.x1(), y - pos.y1()) +} + +struct DefaultTouchOwner; + +struct GrabTouchOwner { + pos: Rect, + node: Rc, + down_ids: RefCell>, +} + +trait TouchOwner { + fn down( + &self, + seat: &Rc, + mapped_node: Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ); + fn up(&self, seat: &Rc, time_usec: u64, id: i32); + fn motion(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed); + fn frame(&self, seat: &Rc); + fn cancel(&self, seat: &Rc); +} + +impl TouchOwner for DefaultTouchOwner { + fn down( + &self, + seat: &Rc, + mapped_node: Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + seat.cursor_group().deactivate(); + let pos = mapped_node.node_absolute_position(); + let (x, y) = transform_abs(x, y, pos); + let mut found_tree = seat.touch_found_tree.borrow_mut(); + let x_int = x.round_down(); + let y_int = y.round_down(); + found_tree.push(FoundNode { + node: mapped_node.clone(), + x: x_int, + y: y_int, + }); + mapped_node.node_find_tree_at(x_int, y_int, &mut found_tree, FindTreeUsecase::None); + if let Some(node) = found_tree.last() { + let node = node.node.clone(); + node.node_seat_state().touch_begin(seat); + let down_ids = RefCell::new(AHashSet::new()); + down_ids.borrow_mut().insert(id); + let (x_rel, y_rel) = transform_rel(x, y, node.node_absolute_position()); + seat.touch_owner.owner.set(Rc::new(GrabTouchOwner { + pos, + node: node.clone(), + down_ids, + })); + node.node_on_touch_down(seat, time_usec, id, x_rel, y_rel); + } + found_tree.clear(); + } + + fn up(&self, _seat: &Rc, _time_usec: u64, _id: i32) { + // nothing + } + + fn motion(&self, _seat: &Rc, _time_usec: u64, _id: i32, _x: Fixed, _y: Fixed) { + // nothing + } + + fn frame(&self, _seat: &Rc) { + // nothing + } + + fn cancel(&self, _seat: &Rc) { + // nothing + } +} + +impl TouchOwner for GrabTouchOwner { + fn down( + &self, + seat: &Rc, + _mapped_node: Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + self.down_ids.borrow_mut().insert(id); + let (x, y) = transform_abs(x, y, self.pos); + let (x_rel, y_rel) = transform_rel(x, y, self.node.node_absolute_position()); + self.node + .clone() + .node_on_touch_down(seat, time_usec, id, x_rel, y_rel); + } + + fn up(&self, seat: &Rc, time_usec: u64, id: i32) { + self.down_ids.borrow_mut().remove(&id); + self.node.clone().node_on_touch_up(seat, time_usec, id); + if self.down_ids.borrow().is_empty() { + self.node.node_seat_state().touch_end(seat); + seat.touch_owner.clear(); + } + } + + fn motion(&self, seat: &Rc, time_usec: u64, id: i32, x: Fixed, y: Fixed) { + self.down_ids.borrow_mut().insert(id); + let (x, y) = transform_abs(x, y, self.pos); + let (x_rel, y_rel) = transform_rel(x, y, self.node.node_absolute_position()); + self.node + .clone() + .node_on_touch_motion(seat, time_usec, id, x_rel, y_rel); + } + + fn frame(&self, seat: &Rc) { + self.node.node_on_touch_frame(seat); + } + + fn cancel(&self, seat: &Rc) { + self.node.node_on_touch_cancel(seat); + self.node.node_seat_state().touch_end(seat); + seat.touch_owner.clear(); + } +} diff --git a/src/ifs/wl_seat/wl_touch.rs b/src/ifs/wl_seat/wl_touch.rs index 6a7a5a8d..384f4543 100644 --- a/src/ifs/wl_seat/wl_touch.rs +++ b/src/ifs/wl_seat/wl_touch.rs @@ -1,29 +1,20 @@ use { crate::{ client::ClientError, + fixed::Fixed, ifs::wl_seat::WlSeat, leaks::Tracker, - object::Object, - wire::{wl_touch::*, WlTouchId}, + object::{Object, Version}, + wire::{wl_touch::*, WlSurfaceId, WlTouchId}, }, std::rc::Rc, thiserror::Error, }; #[allow(dead_code)] -const DOWN: u32 = 0; +pub const SHAPE_SINCE_VERSION: Version = Version(6); #[allow(dead_code)] -const UP: u32 = 1; -#[allow(dead_code)] -const MOTION: u32 = 2; -#[allow(dead_code)] -const FRAME: u32 = 3; -#[allow(dead_code)] -const CANCEL: u32 = 4; -#[allow(dead_code)] -const SHAPE: u32 = 5; -#[allow(dead_code)] -const ORIENTATION: u32 = 6; +pub const ORIENTATION_DIRECTION_SINCE_VERSION: Version = Version(6); pub struct WlTouch { id: WlTouchId, @@ -39,12 +30,79 @@ impl WlTouch { tracker: Default::default(), } } + + pub fn send_down( + &self, + serial: u32, + time: u32, + surface: WlSurfaceId, + id: i32, + x: Fixed, + y: Fixed, + ) { + self.seat.client.event(Down { + self_id: self.id, + serial, + time, + surface, + id, + x, + y, + }) + } + + pub fn send_up(&self, serial: u32, time: u32, id: i32) { + self.seat.client.event(Up { + self_id: self.id, + serial, + time, + id, + }) + } + + pub fn send_motion(&self, time: u32, id: i32, x: Fixed, y: Fixed) { + self.seat.client.event(Motion { + self_id: self.id, + time, + id, + x, + y, + }) + } + + pub fn send_frame(&self) { + self.seat.client.event(Frame { self_id: self.id }) + } + + pub fn send_cancel(&self) { + self.seat.client.event(Cancel { self_id: self.id }) + } + + #[allow(dead_code)] + pub fn send_shape(&self, id: i32, major: Fixed, minor: Fixed) { + self.seat.client.event(Shape { + self_id: self.id, + id, + major, + minor, + }) + } + + #[allow(dead_code)] + pub fn send_orientation(&self, id: i32, orientation: Fixed) { + self.seat.client.event(Orientation { + self_id: self.id, + id, + orientation, + }) + } } impl WlTouchRequestHandler for WlTouch { type Error = WlTouchError; fn release(&self, _req: Release, _slf: &Rc) -> Result<(), Self::Error> { + self.seat.touches.remove(&self.id); self.seat.client.remove_obj(self)?; Ok(()) } diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 82b10908..ac10b471 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -1436,6 +1436,40 @@ impl Node for WlSurface { seat.mods_surface(self, kb_state); } + fn node_on_touch_down( + self: Rc, + seat: &Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + seat.touch_down_surface(&self, time_usec, id, x, y) + } + + fn node_on_touch_up(self: Rc, seat: &Rc, time_usec: u64, id: i32) { + seat.touch_up_surface(&self, time_usec, id) + } + + fn node_on_touch_motion( + self: Rc, + seat: &WlSeatGlobal, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + seat.touch_motion_surface(&self, time_usec, id, x, y) + } + + fn node_on_touch_frame(&self, seat: &WlSeatGlobal) { + seat.touch_frame(&self) + } + + fn node_on_touch_cancel(&self, seat: &WlSeatGlobal) { + seat.touch_cancel(&self) + } + fn node_on_button( self: Rc, seat: &Rc, diff --git a/src/libinput/event.rs b/src/libinput/event.rs index 45885c48..10f9c4ca 100644 --- a/src/libinput/event.rs +++ b/src/libinput/event.rs @@ -16,17 +16,17 @@ use { libinput_event_get_gesture_event, libinput_event_get_keyboard_event, libinput_event_get_pointer_event, libinput_event_get_switch_event, libinput_event_get_tablet_pad_event, libinput_event_get_tablet_tool_event, - libinput_event_get_type, libinput_event_keyboard, libinput_event_keyboard_get_key, - libinput_event_keyboard_get_key_state, libinput_event_keyboard_get_time_usec, - libinput_event_pointer, libinput_event_pointer_get_button, - libinput_event_pointer_get_button_state, libinput_event_pointer_get_dx, - libinput_event_pointer_get_dx_unaccelerated, libinput_event_pointer_get_dy, - libinput_event_pointer_get_dy_unaccelerated, libinput_event_pointer_get_scroll_value, - libinput_event_pointer_get_scroll_value_v120, libinput_event_pointer_get_time_usec, - libinput_event_pointer_has_axis, libinput_event_switch, - libinput_event_switch_get_switch, libinput_event_switch_get_switch_state, - libinput_event_switch_get_time_usec, libinput_event_tablet_pad, - libinput_event_tablet_pad_get_button_number, + libinput_event_get_touch_event, libinput_event_get_type, libinput_event_keyboard, + libinput_event_keyboard_get_key, libinput_event_keyboard_get_key_state, + libinput_event_keyboard_get_time_usec, libinput_event_pointer, + libinput_event_pointer_get_button, libinput_event_pointer_get_button_state, + libinput_event_pointer_get_dx, libinput_event_pointer_get_dx_unaccelerated, + libinput_event_pointer_get_dy, libinput_event_pointer_get_dy_unaccelerated, + libinput_event_pointer_get_scroll_value, libinput_event_pointer_get_scroll_value_v120, + libinput_event_pointer_get_time_usec, libinput_event_pointer_has_axis, + libinput_event_switch, libinput_event_switch_get_switch, + libinput_event_switch_get_switch_state, libinput_event_switch_get_time_usec, + libinput_event_tablet_pad, libinput_event_tablet_pad_get_button_number, libinput_event_tablet_pad_get_button_state, libinput_event_tablet_pad_get_mode, libinput_event_tablet_pad_get_mode_group, libinput_event_tablet_pad_get_ring_number, libinput_event_tablet_pad_get_ring_position, libinput_event_tablet_pad_get_ring_source, @@ -40,10 +40,13 @@ use { libinput_event_tablet_tool_get_tool, libinput_event_tablet_tool_get_wheel_delta_discrete, libinput_event_tablet_tool_get_x_transformed, - libinput_event_tablet_tool_get_y_transformed, libinput_tablet_tool, - libinput_tablet_tool_get_serial, libinput_tablet_tool_get_tool_id, - libinput_tablet_tool_get_type, libinput_tablet_tool_get_user_data, - libinput_tablet_tool_set_user_data, + libinput_event_tablet_tool_get_y_transformed, libinput_event_touch, + libinput_event_touch_get_seat_slot, libinput_event_touch_get_time_usec, + libinput_event_touch_get_x, libinput_event_touch_get_x_transformed, + libinput_event_touch_get_y, libinput_event_touch_get_y_transformed, + libinput_tablet_tool, libinput_tablet_tool_get_serial, + libinput_tablet_tool_get_tool_id, libinput_tablet_tool_get_type, + libinput_tablet_tool_get_user_data, libinput_tablet_tool_set_user_data, }, }, std::marker::PhantomData, @@ -89,6 +92,11 @@ pub struct LibInputTabletTool<'a> { pub(super) _phantom: PhantomData<&'a ()>, } +pub struct LibInputEventTouch<'a> { + pub(super) event: *mut libinput_event_touch, + pub(super) _phantom: PhantomData<&'a ()>, +} + impl<'a> Drop for LibInputEvent<'a> { fn drop(&mut self) { unsafe { @@ -155,6 +163,11 @@ impl<'a> LibInputEvent<'a> { LibInputEventTabletPad, libinput_event_get_tablet_pad_event ); + converter!( + touch_event, + LibInputEventTouch, + libinput_event_get_touch_event + ); } impl<'a> LibInputEventKeyboard<'a> { @@ -467,3 +480,28 @@ impl<'a> LibInputEventTabletPad<'a> { } } } + +impl<'a> LibInputEventTouch<'a> { + pub fn seat_slot(&self) -> i32 { + unsafe { libinput_event_touch_get_seat_slot(self.event) } + } + pub fn x(&self) -> f64 { + unsafe { libinput_event_touch_get_x(self.event) } + } + + pub fn y(&self) -> f64 { + unsafe { libinput_event_touch_get_y(self.event) } + } + + pub fn x_transformed(&self, width: u32) -> f64 { + unsafe { libinput_event_touch_get_x_transformed(self.event, width) } + } + + pub fn y_transformed(&self, height: u32) -> f64 { + unsafe { libinput_event_touch_get_y_transformed(self.event, height) } + } + + pub fn time_usec(&self) -> u64 { + unsafe { libinput_event_touch_get_time_usec(self.event) } + } +} diff --git a/src/libinput/sys.rs b/src/libinput/sys.rs index c937abb3..114ffdd4 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -30,6 +30,8 @@ pub struct libinput_tablet_pad_mode_group(u8); pub struct libinput_tablet_tool(u8); // #[repr(transparent)] // pub struct libinput_tablet_pad(u8); +#[repr(transparent)] +pub struct libinput_event_touch(u8); #[link(name = "input")] extern "C" { @@ -357,6 +359,20 @@ extern "C" { // group: *mut libinput_tablet_pad_mode_group, // button: c::c_uint, // ) -> c::c_int; + + pub fn libinput_event_get_touch_event(event: *mut libinput_event) -> *mut libinput_event_touch; + pub fn libinput_event_touch_get_seat_slot(event: *mut libinput_event_touch) -> i32; + pub fn libinput_event_touch_get_time_usec(event: *mut libinput_event_touch) -> u64; + pub fn libinput_event_touch_get_x(event: *mut libinput_event_touch) -> f64; + pub fn libinput_event_touch_get_x_transformed( + event: *mut libinput_event_touch, + width: u32, + ) -> f64; + pub fn libinput_event_touch_get_y(event: *mut libinput_event_touch) -> f64; + pub fn libinput_event_touch_get_y_transformed( + event: *mut libinput_event_touch, + height: u32, + ) -> f64; } #[repr(C)] diff --git a/src/state.rs b/src/state.rs index 73abb7df..283e661d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -130,6 +130,7 @@ pub struct State { pub node_ids: NodeIds, pub root: Rc, pub workspaces: CopyHashMap>, + pub builtin_output: Cell>, pub dummy_output: CloneCell>>, pub backend_events: AsyncQueue, pub input_device_handlers: RefCell>, diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 2824010c..04156fd9 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -6,6 +6,7 @@ use { state::{ConnectorData, OutputData, State}, tree::{move_ws_to_output, OutputNode, OutputRenderData, WsMoveConfig}, utils::{asyncevent::AsyncEvent, clonecell::CloneCell}, + video::drm::ConnectorType, }, std::{ cell::{Cell, RefCell}, @@ -219,6 +220,20 @@ impl ConnectorHandler { }; move_ws_to_output(&ws, &on, config); } + let c_ty = self.data.connector.kernel_id().ty; + let mut builtin = false; + if c_ty == ConnectorType::eDP || c_ty == ConnectorType::LVDS || c_ty == ConnectorType::DSI { + match self.state.builtin_output.get() { + Some(_) => { + log::warn!("A built-in connector is already connected"); + } + None => { + builtin = true; + log::info!("Connector {} is built-in", self.data.connector.kernel_id()); + self.state.builtin_output.set(Some(self.id)) + } + } + }; if let Some(config) = self.state.config.get() { config.connector_connected(self.id); } @@ -240,6 +255,9 @@ impl ConnectorHandler { } self.data.async_event.triggered().await; } + if builtin { + self.state.builtin_output.set(None); + } if let Some(config) = self.state.config.get() { config.connector_disconnected(self.id); } diff --git a/src/tree.rs b/src/tree.rs index 58f679f0..797820c1 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -200,6 +200,50 @@ pub trait Node: 'static { let _ = kb_state; } + fn node_on_touch_down( + self: Rc, + seat: &Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let _ = seat; + let _ = time_usec; + let _ = id; + let _ = x; + let _ = y; + } + + fn node_on_touch_up(self: Rc, seat: &Rc, time_usec: u64, id: i32) { + let _ = seat; + let _ = time_usec; + let _ = id; + } + + fn node_on_touch_motion( + self: Rc, + seat: &WlSeatGlobal, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let _ = seat; + let _ = time_usec; + let _ = id; + let _ = x; + let _ = y; + } + + fn node_on_touch_frame(&self, seat: &WlSeatGlobal) { + let _ = seat; + } + + fn node_on_touch_cancel(&self, seat: &WlSeatGlobal) { + let _ = seat; + } + fn node_on_button( self: Rc, seat: &Rc, diff --git a/src/tree/container.rs b/src/tree/container.rs index 4ad79051..a14ebb33 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -148,6 +148,7 @@ pub struct ContainerChild { enum CursorType { Seat(SeatId), TabletTool(TabletToolId), + Touch(i32), } struct CursorState { @@ -1275,6 +1276,34 @@ impl Node for ContainerNode { Some(self) } + fn node_on_touch_down( + self: Rc, + seat: &Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let id = CursorType::Touch(id); + self.pointer_move(id, seat.pointer_cursor(), x, y, false); + self.button(id, seat, time_usec, true); + } + + fn node_on_touch_up(self: Rc, seat: &Rc, time_usec: u64, id: i32) { + self.button(CursorType::Touch(id), seat, time_usec, false); + } + + fn node_on_touch_motion( + self: Rc, + seat: &WlSeatGlobal, + _time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + self.pointer_move(CursorType::Touch(id), seat.pointer_cursor(), x, y, false); + } + fn node_on_button( self: Rc, seat: &Rc, diff --git a/src/tree/float.rs b/src/tree/float.rs index 571a99cf..b3ef34ab 100644 --- a/src/tree/float.rs +++ b/src/tree/float.rs @@ -56,6 +56,7 @@ pub struct FloatNode { enum CursorType { Seat(SeatId), TabletTool(TabletToolId), + Touch(i32), } struct CursorState { @@ -567,6 +568,41 @@ impl Node for FloatNode { renderer.render_floating(self, x, y) } + fn node_on_touch_down( + self: Rc, + seat: &Rc, + time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let id = CursorType::Touch(id); + let cursor = seat.pointer_cursor(); + self.pointer_move(id, cursor, x, y, false); + self.button(id, cursor, seat, time_usec, true); + } + + fn node_on_touch_up(self: Rc, seat: &Rc, time_usec: u64, id: i32) { + self.button( + CursorType::Touch(id), + seat.pointer_cursor(), + seat, + time_usec, + false, + ); + } + + fn node_on_touch_motion( + self: Rc, + seat: &WlSeatGlobal, + _time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + self.pointer_move(CursorType::Touch(id), seat.pointer_cursor(), x, y, false); + } + fn node_on_button( self: Rc, seat: &Rc, diff --git a/src/tree/output.rs b/src/tree/output.rs index 8c009de7..e03ba6cf 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -78,6 +78,7 @@ pub struct OutputNode { pub enum PointerType { Seat(SeatId), TabletTool(TabletToolId), + Touch(i32), } pub async fn output_render_data(state: Rc) { @@ -865,6 +866,30 @@ impl Node for OutputNode { renderer.render_output(self, x, y); } + fn node_on_touch_down( + self: Rc, + _seat: &Rc, + _time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + let id = PointerType::Touch(id); + self.pointer_move(id, x, y); + self.button(id); + } + + fn node_on_touch_motion( + self: Rc, + _seat: &WlSeatGlobal, + _time_usec: u64, + id: i32, + x: Fixed, + y: Fixed, + ) { + self.pointer_move(PointerType::Touch(id), x, y); + } + fn node_on_button( self: Rc, seat: &Rc, diff --git a/src/video/drm.rs b/src/video/drm.rs index 39194a49..8f1abd4f 100644 --- a/src/video/drm.rs +++ b/src/video/drm.rs @@ -910,7 +910,7 @@ impl Drop for Change { } #[allow(non_camel_case_types)] -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ConnectorType { Unknown(u32), VGA, diff --git a/wire/jay_seat_events.txt b/wire/jay_seat_events.txt index f7cb0163..5527148a 100644 --- a/wire/jay_seat_events.txt +++ b/wire/jay_seat_events.txt @@ -239,3 +239,37 @@ event tablet_pad_ring_frame { input_device: u32, ring: u32, } + +event touch_down { + seat: u32, + time_usec: pod(u64), + id: i32, + x: fixed, + y: fixed, +} + +event touch_up { + seat: u32, + time_usec: pod(u64), + id: i32, +} + +event touch_motion { + seat: u32, + time_usec: pod(u64), + id: i32, + x: fixed, + y: fixed, +} + +event touch_frame { + seat: u32, + time_usec: pod(u64), + id: i32, +} + +event touch_cancel { + seat: u32, + time_usec: pod(u64), + id: i32, +} \ No newline at end of file diff --git a/wire/wl_touch.txt b/wire/wl_touch.txt index 11ec77dd..a5a21c3f 100644 --- a/wire/wl_touch.txt +++ b/wire/wl_touch.txt @@ -23,7 +23,7 @@ event up { event motion { time: u32, - id: u32, + id: i32, x: fixed, y: fixed, }