diff --git a/docs/features.md b/docs/features.md index b49073b5..20dd42d0 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 | | @@ -170,7 +170,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. @@ -181,6 +180,5 @@ 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. - Tablet support. - Tearing updates of fullscreen games. diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index d00a274c..41c6e9da 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -873,6 +873,10 @@ impl Client { self.send(&ClientMessage::SetTransformMatrix { device, matrix }) } + pub fn set_mapped_output(&self, device: InputDevice, connector: (ConnectorType, u32)) { + self.send(&ClientMessage::SetMappedOutput { device, connector }) + } + pub fn set_px_per_wheel_scroll(&self, device: InputDevice, px: f64) { self.send(&ClientMessage::SetPxPerWheelScroll { device, px }) } diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 16e57be1..ce963a59 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -229,6 +229,10 @@ pub enum ClientMessage<'a> { device: InputDevice, matrix: [[f64; 2]; 2], }, + SetMappedOutput { + device: InputDevice, + connector: (ConnectorType, u32), + }, GetDeviceName { device: InputDevice, }, diff --git a/jay-config/src/input.rs b/jay-config/src/input.rs index 30edfb73..febecbca 100644 --- a/jay-config/src/input.rs +++ b/jay-config/src/input.rs @@ -9,7 +9,7 @@ use { keyboard::{mods::Modifiers, Keymap}, Axis, Direction, ModifiedKeySym, Workspace, _private::{ipc::WorkspaceSource, DEFAULT_SEAT_NAME}, - video::Connector, + video::{connector_type::ConnectorType, Connector}, }, serde::{Deserialize, Serialize}, std::time::Duration, @@ -80,6 +80,13 @@ impl InputDevice { get!().set_transform_matrix(self, matrix); } + /// Sets the output to which input from this device is mapped to. + /// + /// This only applies to touch devices. + pub fn set_mapped_output(self, connector: (ConnectorType, u32)) { + get!().set_mapped_output(self, connector); + } + /// Returns the name of the device. pub fn name(self) -> String { get!(String::new()).device_name(self) diff --git a/src/backend.rs b/src/backend.rs index f1ded7d8..d69c6886 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -110,6 +110,13 @@ pub enum ConnectorEvent { Available, } +#[derive(Copy, Clone, Debug)] +pub struct MappedOutput { + pub ty: ConnectorType, + pub idx: u32, + pub conn_id: Option, +} + pub trait HardwareCursor: Debug { fn set_enabled(&self, enabled: bool); fn get_buffer(&self) -> Rc; @@ -145,6 +152,10 @@ pub trait InputDevice { None } fn set_transform_matrix(&self, matrix: TransformMatrix); + fn mapped_output(&self) -> Option { + None + } + fn set_mapped_output(&self, output: MappedOutput); fn name(&self) -> Rc; fn dev_t(&self) -> Option { None @@ -212,6 +223,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 _, @@ -253,6 +281,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.rs b/src/backends/metal.rs index fb76db20..e53d6a2d 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -7,7 +7,7 @@ use { async_engine::SpawnedFuture, backend::{ Backend, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, - InputEvent, KeyState, TransformMatrix, + InputEvent, KeyState, MappedOutput, TransformMatrix, }, backends::metal::video::{MetalDrmDeviceData, MetalRenderContext, PendingDrmDevice}, dbus::{DbusError, SignalHandler}, @@ -298,6 +298,7 @@ struct MetalInputDevice { cb: CloneCell>>, name: CloneCell>, transform_matrix: Cell>, + mapped_output: Cell>, // state pressed_keys: SmallMap, @@ -504,6 +505,12 @@ impl InputDevice for MetalInputDevice { self.transform_matrix.set(Some(matrix)); } + fn set_mapped_output(&self, output: MappedOutput) { + if self.has_capability(InputDeviceCapability::Touch) { + self.mapped_output.set(Some(output)); + } + } + fn name(&self) -> Rc { self.name.get() } @@ -597,6 +604,10 @@ impl InputDevice for MetalInputDevice { fn natural_scrolling_enabled(&self) -> Option { self.effective.natural_scrolling_enabled.get() } + + fn mapped_output(&self) -> Option { + self.mapped_output.get() + } } impl MetalInputDevice { diff --git a/src/backends/metal/input.rs b/src/backends/metal/input.rs index c64f683c..02606531 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, libinput::{ @@ -103,6 +103,11 @@ impl MetalBackend { c::LIBINPUT_EVENT_GESTURE_HOLD_BEGIN => self.handle_gesture_hold_begin(event), c::LIBINPUT_EVENT_GESTURE_HOLD_END => self.handle_gesture_hold_end(event), c::LIBINPUT_EVENT_SWITCH_TOGGLE => self.handle_switch_toggle(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), _ => {} } } @@ -320,4 +325,61 @@ impl MetalBackend { event: switch_event, }); } + + 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/backends/metal/monitor.rs b/src/backends/metal/monitor.rs index acfa51e8..2ab91f53 100644 --- a/src/backends/metal/monitor.rs +++ b/src/backends/metal/monitor.rs @@ -320,6 +320,7 @@ impl MetalBackend { pressed_buttons: Default::default(), desired: Default::default(), transform_matrix: Default::default(), + mapped_output: Default::default(), effective: Default::default(), }); slots[slot] = Some(dev.clone()); diff --git a/src/backends/x.rs b/src/backends/x.rs index 3b8ed707..3398ef42 100644 --- a/src/backends/x.rs +++ b/src/backends/x.rs @@ -5,7 +5,7 @@ use { AxisSource, Backend, BackendDrmDevice, BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, DrmEvent, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, InputEvent, KeyState, - Mode, MonitorInfo, ScrollAxis, TransformMatrix, AXIS_120, + MappedOutput, Mode, MonitorInfo, ScrollAxis, TransformMatrix, AXIS_120, }, fixed::Fixed, format::XRGB8888, @@ -1203,6 +1203,10 @@ impl InputDevice for XSeatKeyboard { fn set_natural_scrolling_enabled(&self, enabled: bool) { let _ = enabled; } + + fn set_mapped_output(&self, output: MappedOutput) { + let _ = output; + } } impl InputDevice for XSeatMouse { @@ -1272,4 +1276,8 @@ impl InputDevice for XSeatMouse { fn set_natural_scrolling_enabled(&self, enabled: bool) { let _ = enabled; } + + fn set_mapped_output(&self, output: MappedOutput) { + let _ = output; + } } diff --git a/src/compositor.rs b/src/compositor.rs index eeebec41..a55e2f43 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/config/handler.rs b/src/config/handler.rs index a5e74c19..ba1665fe 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -3,7 +3,7 @@ use { async_engine::SpawnedFuture, backend::{ self, ConnectorId, DrmDeviceId, InputDeviceAccelProfile, InputDeviceCapability, - InputDeviceId, + InputDeviceId, MappedOutput, }, compositor::MAX_EXTENTS, config::ConfigProxy, @@ -47,7 +47,7 @@ use { logging::LogLevel, theme::{colors::Colorable, sized::Resizable}, timer::Timer as JayTimer, - video::{Connector, DrmDevice, GfxApi, Transform}, + video::{connector_type::ConnectorType, Connector, DrmDevice, GfxApi, Transform}, Axis, Direction, Workspace, }, libloading::Library, @@ -658,6 +658,21 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_set_mapped_output( + &self, + device: InputDevice, + connector: (ConnectorType, u32), + ) -> Result<(), CphError> { + let dev = self.get_device_handler_data(device)?; + let ty = crate::video::drm::ConnectorType::from_config(connector.0); + dev.device.set_mapped_output(MappedOutput { + ty, + idx: connector.1, + conn_id: None, + }); + Ok(()) + } + fn handle_get_workspace(&self, name: &str) { let name = Rc::new(name.to_owned()); let ws = match self.workspaces_by_name.get(&name) { @@ -1579,6 +1594,9 @@ impl ConfigProxyHandler { ClientMessage::SetTransformMatrix { device, matrix } => self .handle_set_transform_matrix(device, matrix) .wrn("set_transform_matrix")?, + ClientMessage::SetMappedOutput { device, connector } => self + .handle_set_mapped_output(device, connector) + .wrn("set_mapped_output")?, ClientMessage::GetDeviceName { device } => { self.handle_get_device_name(device).wrn("get_device_name")? } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 42591b8b..b4900470 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -4,6 +4,8 @@ pub mod ext_transient_seat_v1; mod gesture_owner; mod kb_owner; mod pointer_owner; +mod touch_owner; + pub mod text_input; pub mod wl_keyboard; pub mod wl_pointer; @@ -21,6 +23,7 @@ pub mod zwp_virtual_keyboard_v1; use { crate::{ async_engine::SpawnedFuture, + backend::InputDeviceCapability, client::{Client, ClientError, ClientId}, cursor::{Cursor, KnownCursor, DEFAULT_CURSOR_SIZE}, fixed::Fixed, @@ -49,6 +52,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, @@ -77,7 +81,7 @@ use { }, wire::{ wl_seat::*, ExtIdleNotificationV1Id, WlDataDeviceId, WlKeyboardId, WlPointerId, - WlSeatId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, + WlSeatId, WlTouchId, ZwlrDataControlDeviceV1Id, ZwpPrimarySelectionDeviceV1Id, ZwpRelativePointerV1Id, ZwpTextInputV3Id, }, xkbcommon::{DynKeyboardState, KeyboardState, KeymapId, XkbKeymap, XkbState}, @@ -101,7 +105,6 @@ pub use { pub const POINTER: u32 = 1; const KEYBOARD: u32 = 2; -#[allow(dead_code)] const TOUCH: u32 = 4; #[allow(dead_code)] @@ -149,6 +152,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 +178,7 @@ pub struct WlSeatGlobal { pointer_owner: PointerOwnerHolder, kb_owner: KbOwnerHolder, gesture_owner: GestureOwnerHolder, + touch_owner: TouchOwnerHolder, dropped_dnd: RefCell>, shortcuts: RefCell>>, queue_link: Cell>>>, @@ -229,6 +234,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()), @@ -247,6 +253,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: Cell::new(None), @@ -1074,6 +1081,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() @@ -1089,6 +1097,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.set(None); self.tree_changed_handler.set(None); @@ -1117,13 +1126,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(), }); @@ -1217,9 +1237,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, } @@ -1230,7 +1252,7 @@ impl WlSeat { fn send_capabilities(self: &Rc) { self.client.event(Capabilities { self_id: self.id, - capabilities: POINTER | KEYBOARD, + capabilities: self.capabilities, }) } @@ -1286,10 +1308,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> { @@ -1326,6 +1361,7 @@ impl Object for WlSeat { self.pointers.clear(); self.relative_pointers.clear(); self.keyboards.clear(); + self.touches.clear(); } } @@ -1339,6 +1375,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 0244838a..f08f15b6 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, @@ -23,6 +23,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, @@ -53,6 +54,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>, } @@ -84,6 +86,14 @@ impl NodeSeatState { self.gesture_foci.remove(&seat.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_pointer_grab(&self, seat: &Rc) { self.pointer_grabs.insert(seat.id, seat.clone()); } @@ -142,6 +152,9 @@ impl NodeSeatState { fn destroy_node2(&self, node: &dyn Node, focus_last: bool) { // NOTE: Also called by set_visible(false) + while let Some((_, seat)) = self.touch_foci.pop() { + seat.touch_owner.clear(); + } while let Some((_, seat)) = self.gesture_foci.pop() { seat.gesture_owner.revert_to_default(&seat); } @@ -195,6 +208,7 @@ impl WlSeatGlobal { | InputEvent::ConnectorPosition { time_usec, .. } | InputEvent::Motion { time_usec, .. } | InputEvent::Button { time_usec, .. } + | InputEvent::Touch { time_usec, .. } | InputEvent::AxisFrame { time_usec, .. } | InputEvent::SwipeBegin { time_usec, .. } | InputEvent::SwipeUpdate { time_usec, .. } @@ -241,6 +255,11 @@ impl WlSeatGlobal { button, state, } => self.button_event(time_usec, button, state), + InputEvent::Touch { + seat_slot, + time_usec, + event, + } => self.touch_event(dev, seat_slot, time_usec, event), InputEvent::AxisSource { source } => self.pointer_owner.axis_source(source), InputEvent::Axis120 { @@ -309,6 +328,69 @@ impl WlSeatGlobal { } } + fn touch_event( + self: &Rc, + dev: &DeviceHandlerData, + id: i32, + time_usec: u64, + event: TouchEvent, + ) { + match event { + TouchEvent::Down { pos } => { + let input_dev = &dev.device; + let mapped_node = input_dev + .mapped_output() + .and_then(|mut out| { + out.conn_id + .or_else(|| 'get_conn_id: { + for (&id, con) in self.state.connectors.lock().iter() { + let kernel_id = con.connector.kernel_id(); + if out.ty == kernel_id.ty && out.idx == kernel_id.idx { + out.conn_id = Some(id); + input_dev.set_mapped_output(out); + break 'get_conn_id Some(id); + } + } + None + }) + .and_then(|con| self.state.outputs.get(&con)) + .or_else(|| { + out.conn_id = None; + input_dev.set_mapped_output(out); + None + }) + }) + .or_else(|| { + self.state + .builtin_output + .get() + .and_then(|con| self.state.outputs.get(&con)) + }) + .or_else(|| self.state.outputs.lock().values().nth(0).cloned()) + .and_then(|o| o.node.clone()) + .unwrap_or_else(|| self.output.get()); + 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); + } + } + } + fn connector_position_event( self: &Rc, time_usec: u64, @@ -708,6 +790,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), @@ -783,6 +877,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 set_new_position(self: &Rc, time_usec: u64, x: Fixed, y: Fixed) { self.pos_time_usec.set(time_usec); self.pos.set((x, y)); @@ -1047,6 +1151,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..31b49196 --- /dev/null +++ b/src/ifs/wl_seat/touch_owner.rs @@ -0,0 +1,194 @@ +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, + ) { + 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()); + node.node_on_touch_down(seat, time_usec, id, x_rel, y_rel); + seat.touch_owner.owner.set(Rc::new(GrabTouchOwner { + pos, + node, + down_ids: RefCell::new(AHashSet::new()), + })); + } + 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 + .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.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 + .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 4311bf81..48479f8f 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -1433,6 +1433,40 @@ impl Node for WlSurface { seat.mods_surface(self, kb_state); } + fn node_on_touch_down( + &self, + 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, seat: &WlSeatGlobal, time_usec: u64, id: i32) { + seat.touch_up_surface(&self, time_usec, id) + } + + fn node_on_touch_motion( + &self, + 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/it/test_backend.rs b/src/it/test_backend.rs index 9691ce03..64699b78 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -4,8 +4,8 @@ use { backend::{ AxisSource, Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId, InputDevice, InputDeviceAccelProfile, - InputDeviceCapability, InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo, - ScrollAxis, TransformMatrix, + InputDeviceCapability, InputDeviceId, InputEvent, KeyState, MappedOutput, Mode, + MonitorInfo, ScrollAxis, TransformMatrix, }, compositor::TestFuture, drm_feedback::DrmFeedback, @@ -449,6 +449,10 @@ trait TestInputDevice: InputDevice { let _ = matrix; } + fn set_mapped_output(&self, output: MappedOutput) { + let _ = output; + } + fn set_tap_enabled(&self, enabled: bool) { let _ = enabled; } @@ -507,6 +511,10 @@ impl InputDevice for T { ::set_transform_matrix(self, matrix) } + fn set_mapped_output(&self, output: MappedOutput) { + ::set_mapped_output(self, output) + } + fn name(&self) -> Rc { self.common().name.clone() } diff --git a/src/libinput/event.rs b/src/libinput/event.rs index 60038fb6..6ed69d6e 100644 --- a/src/libinput/event.rs +++ b/src/libinput/event.rs @@ -11,16 +11,20 @@ use { libinput_event_gesture_get_time_usec, libinput_event_get_device, libinput_event_get_gesture_event, libinput_event_get_keyboard_event, libinput_event_get_pointer_event, libinput_event_get_switch_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_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_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, }, }, std::marker::PhantomData, @@ -51,6 +55,11 @@ pub struct LibInputEventSwitch<'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 { @@ -118,6 +127,18 @@ impl<'a> LibInputEvent<'a> { }) } } + + pub fn touch_event(&self) -> Option { + let res = unsafe { libinput_event_get_touch_event(self.event) }; + if res.is_null() { + None + } else { + Some(LibInputEventTouch { + event: res, + _phantom: Default::default(), + }) + } + } } impl<'a> LibInputEventKeyboard<'a> { @@ -228,3 +249,28 @@ impl<'a> LibInputEventSwitch<'a> { unsafe { SwitchState(libinput_event_switch_get_switch_state(self.event)) } } } + +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 31d860f2..4591823d 100644 --- a/src/libinput/sys.rs +++ b/src/libinput/sys.rs @@ -18,6 +18,8 @@ pub struct libinput_event_pointer(u8); pub struct libinput_event_gesture(u8); #[repr(transparent)] pub struct libinput_event_switch(u8); +#[repr(transparent)] +pub struct libinput_event_touch(u8); #[link(name = "input")] extern "C" { @@ -166,6 +168,20 @@ extern "C" { event: *mut libinput_event_switch, ) -> libinput_switch_state; pub fn libinput_event_switch_get_time_usec(event: *mut libinput_event_switch) -> u64; + + 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 48cc9e16..8cef9639 100644 --- a/src/state.rs +++ b/src/state.rs @@ -125,6 +125,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 bd61ea59..d3daa80f 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}, @@ -223,6 +224,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); } @@ -244,6 +259,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 33566120..bc9ca15e 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -192,6 +192,50 @@ pub trait Node: 'static { let _ = kb_state; } + fn node_on_touch_down( + &self, + 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, seat: &WlSeatGlobal, time_usec: u64, id: i32) { + let _ = seat; + let _ = time_usec; + let _ = id; + } + + fn node_on_touch_motion( + &self, + 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/video/drm.rs b/src/video/drm.rs index 39194a49..a407d8bd 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, @@ -991,6 +991,34 @@ impl ConnectorType { } } + pub fn from_config(c: jay_config::video::connector_type::ConnectorType) -> Self { + use jay_config::video::connector_type::*; + match c { + CON_VGA => Self::VGA, + CON_DVII => Self::DVII, + CON_DVID => Self::DVID, + CON_DVIA => Self::DVIA, + CON_COMPOSITE => Self::Composite, + CON_SVIDEO => Self::SVIDEO, + CON_LVDS => Self::LVDS, + CON_COMPONENT => Self::Component, + CON_9PIN_DIN => Self::_9PinDIN, + CON_DISPLAY_PORT => Self::DisplayPort, + CON_HDMIA => Self::HDMIA, + CON_HDMIB => Self::HDMIB, + CON_TV => Self::TV, + CON_EDP => Self::eDP, + CON_VIRTUAL => Self::VIRTUAL, + CON_DSI => Self::DSI, + CON_DPI => Self::DPI, + CON_WRITEBACK => Self::WRITEBACK, + CON_SPI => Self::SPI, + CON_USB => Self::USB, + CON_EMBEDDED_WINDOW => Self::EmbeddedWindow, + _ => Self::Unknown(0), + } + } + pub fn to_config(self) -> jay_config::video::connector_type::ConnectorType { use jay_config::video::connector_type::*; match self { diff --git a/toml-config/src/config.rs b/toml-config/src/config.rs index 44e7d9cb..7393fc1f 100644 --- a/toml-config/src/config.rs +++ b/toml-config/src/config.rs @@ -22,7 +22,7 @@ use { logging::LogLevel, status::MessageFormat, theme::Color, - video::{GfxApi, Transform}, + video::{connector_type::ConnectorType, GfxApi, Transform}, Axis, Direction, Workspace, }, std::{ @@ -244,6 +244,7 @@ pub struct Input { pub natural_scrolling: Option, pub px_per_wheel_scroll: Option, pub transform_matrix: Option<[[f64; 2]; 2]>, + pub mapped_output: Option<(ConnectorType, u32)>, pub keymap: Option, pub switch_actions: AHashMap, } diff --git a/toml-config/src/config/parsers/input.rs b/toml-config/src/config/parsers/input.rs index 83ee3bed..5b82bf72 100644 --- a/toml-config/src/config/parsers/input.rs +++ b/toml-config/src/config/parsers/input.rs @@ -18,9 +18,12 @@ use { }, ahash::AHashMap, indexmap::IndexMap, - jay_config::input::{ - acceleration::{ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT}, - SwitchEvent, + jay_config::{ + input::{ + acceleration::{ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT}, + SwitchEvent, + }, + video::ToConnectorId, }, thiserror::Error, }; @@ -72,6 +75,7 @@ impl<'a> Parser for InputParser<'a> { ), ( transform_matrix, + mapped_output, keymap, on_lid_opened_val, on_lid_closed_val, @@ -93,6 +97,7 @@ impl<'a> Parser for InputParser<'a> { ), ( recover(opt(val("transform-matrix"))), + recover(opt(str("mapped-output"))), opt(val("keymap")), opt(val("on-lid-opened")), opt(val("on-lid-closed")), @@ -121,6 +126,16 @@ impl<'a> Parser for InputParser<'a> { } }, }; + let mapped_output = match mapped_output { + None => None, + Some(c) => match c.value.to_connector_id() { + Ok(c) => Some(c), + Err(e) => { + log::warn!("Could not parse mapped-output: {:?}", e); + None + } + }, + }; if let Some(tag) = tag { if self.tag_ok { self.cx.used.borrow_mut().defined_inputs.insert(tag.into()); @@ -188,6 +203,7 @@ impl<'a> Parser for InputParser<'a> { natural_scrolling: natural_scrolling.despan(), px_per_wheel_scroll: px_per_wheel_scroll.despan(), transform_matrix, + mapped_output, keymap, switch_actions, }) diff --git a/toml-config/src/lib.rs b/toml-config/src/lib.rs index 73ce2329..dab52feb 100644 --- a/toml-config/src/lib.rs +++ b/toml-config/src/lib.rs @@ -417,6 +417,9 @@ impl Input { if let Some(v) = self.transform_matrix { c.set_transform_matrix(v); } + if let Some(v) = self.mapped_output { + c.set_mapped_output(v); + } if let Some(v) = &self.keymap { if let Some(km) = state.get_keymap(v) { c.set_keymap(km); diff --git a/toml-spec/spec/spec.generated.json b/toml-spec/spec/spec.generated.json index 86e68430..f9db4f5b 100644 --- a/toml-spec/spec/spec.generated.json +++ b/toml-spec/spec/spec.generated.json @@ -822,6 +822,10 @@ } } }, + "mapped-output": { + "type": "string", + "description": "The name of the connector to map input from this device to.\n\nYou can find out the name of the connector by running `jay randr`.\n\n- Example:\n\n ```toml\n [[inputs]]\n match.is-touch = true\n mapped-output = \"eDP-1\"\n ```\n" + }, "keymap": { "description": "The keymap to use for this device.\n\nThis overrides the global keymap. The keymap becomes active when a key is pressed.\n\n- Example:\n\n ```toml\n [[inputs]]\n match.name = \"ZSA Technology Labs Inc ErgoDox EZ\"\n keymap.name = \"external\"\n ```\n", "$ref": "#/$defs/Keymap" diff --git a/toml-spec/spec/spec.generated.md b/toml-spec/spec/spec.generated.md index d50e2c1b..6bc18f13 100644 --- a/toml-spec/spec/spec.generated.md +++ b/toml-spec/spec/spec.generated.md @@ -1619,6 +1619,22 @@ The table has the following fields: The value of this field should be an array of arrays of numbers. +- `mapped-output` (optional): + + The name of the connector to map input from this device to. + + You can find out the name of the connector by running `jay randr`. + + - Example: + + ```toml + [[inputs]] + match.is-touch = true + mapped-output = "eDP-1" + ``` + + The value of this field should be a string. + - `keymap` (optional): The keymap to use for this device. diff --git a/toml-spec/spec/spec.yaml b/toml-spec/spec/spec.yaml index 58456a23..06730abe 100644 --- a/toml-spec/spec/spec.yaml +++ b/toml-spec/spec/spec.yaml @@ -1224,6 +1224,21 @@ Input: match.is-pointer = true transform-matrix = [[0.35, 0], [0, 0.35]] ``` + mapped-output: + kind: string + required: false + description: | + The name of the connector to map input from this device to. + + You can find out the name of the connector by running `jay randr`. + + - Example: + + ```toml + [[inputs]] + match.is-touch = true + mapped-output = "eDP-1" + ``` keymap: ref: Keymap required: false 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, }