From cb1e5d6c192d58c33229c32e34628ecadf84c02f Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Tue, 5 Dec 2023 10:24:41 +0400 Subject: Track tablet pointer separately, don't sent wl_pointer events Tablets are not supposed to send wl_pointer events. This unbreaks GTK 4 clients for example. --- src/input.rs | 85 ++++++++++++++++++++++++++++-------------------------------- src/niri.rs | 44 ++++++++++++++++++++++++++----- 2 files changed, 76 insertions(+), 53 deletions(-) (limited to 'src') diff --git a/src/input.rs b/src/input.rs index a86045bd..cf2c59ba 100644 --- a/src/input.rs +++ b/src/input.rs @@ -439,6 +439,9 @@ impl State { pointer.frame(self); + // We moved the regular pointer, so show it now. + self.niri.tablet_cursor_location = None; + // Redraw to update the cursor position. // FIXME: redraw only outputs overlapping the cursor. self.niri.queue_redraw_all(); @@ -487,6 +490,9 @@ impl State { pointer.frame(self); + // We moved the regular pointer, so show it now. + self.niri.tablet_cursor_location = None; + // Redraw to update the cursor position. // FIXME: redraw only outputs overlapping the cursor. self.niri.queue_redraw_all(); @@ -596,25 +602,9 @@ impl State { return; }; - let serial = SERIAL_COUNTER.next_serial(); - - let pointer = self.niri.seat.get_pointer().unwrap(); - let under = self.niri.surface_under_and_global_space(pos); - self.niri.pointer_focus = under.clone(); let under = under.map(|u| u.surface); - pointer.motion( - self, - under.clone(), - &MotionEvent { - location: pos, - serial, - time: event.time_msec(), - }, - ); - pointer.frame(self); - let tablet_seat = self.niri.seat.tablet_seat(); let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device())); let tool = tablet_seat.get_tool(&event.tool()); @@ -645,6 +635,8 @@ impl State { SERIAL_COUNTER.next_serial(), event.time_msec(), ); + + self.niri.tablet_cursor_location = Some(pos); } // Redraw to update the cursor position. @@ -660,15 +652,21 @@ impl State { let serial = SERIAL_COUNTER.next_serial(); tool.tip_down(serial, event.time_msec()); - let pointer = self.niri.seat.get_pointer().unwrap(); - if !pointer.is_grabbed() { - if let Some(window) = self.niri.window_under_cursor() { + if let Some(pos) = self.niri.tablet_cursor_location { + if let Some(window) = self.niri.window_under(pos) { let window = window.clone(); self.niri.layout.activate_window(&window); - } else if let Some(output) = self.niri.output_under_cursor() { + + // FIXME: granular. + self.niri.queue_redraw_all(); + } else if let Some((output, _)) = self.niri.output_under(pos) { + let output = output.clone(); self.niri.layout.activate_output(&output); + + // FIXME: granular. + self.niri.queue_redraw_all(); } - }; + } } TabletToolTipState::Up => { tool.tip_up(event.time_msec()); @@ -681,39 +679,34 @@ impl State { return; }; - let serial = SERIAL_COUNTER.next_serial(); - - let pointer = self.niri.seat.get_pointer().unwrap(); - let under = self.niri.surface_under_and_global_space(pos); - self.niri.pointer_focus = under.clone(); let under = under.map(|u| u.surface); - pointer.motion( - self, - under.clone(), - &MotionEvent { - location: pos, - serial, - time: event.time_msec(), - }, - ); - pointer.frame(self); - let tablet_seat = self.niri.seat.tablet_seat(); let tool = tablet_seat.add_tool::(&self.niri.display_handle, &event.tool()); let tablet = tablet_seat.get_tablet(&TabletDescriptor::from(&event.device())); - if let (Some(under), Some(tablet)) = (under, tablet) { + if let Some(tablet) = tablet { match event.state() { - ProximityState::In => tool.proximity_in( - pos, - under, - &tablet, - SERIAL_COUNTER.next_serial(), - event.time_msec(), - ), - ProximityState::Out => tool.proximity_out(event.time_msec()), + ProximityState::In => { + if let Some(under) = under { + tool.proximity_in( + pos, + under, + &tablet, + SERIAL_COUNTER.next_serial(), + event.time_msec(), + ); + } + self.niri.tablet_cursor_location = Some(pos); + } + ProximityState::Out => { + tool.proximity_out(event.time_msec()); + self.niri.tablet_cursor_location = None; + } } + + // FIXME: granular. + self.niri.queue_redraw_all(); } } InputEvent::TabletToolButton { event, .. } => { diff --git a/src/niri.rs b/src/niri.rs index 544ae16d..45b1d4f4 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -74,7 +74,7 @@ use smithay::wayland::shell::xdg::decoration::XdgDecorationState; use smithay::wayland::shell::xdg::XdgShellState; use smithay::wayland::shm::ShmState; use smithay::wayland::socket::ListeningSocketSource; -use smithay::wayland::tablet_manager::TabletManagerState; +use smithay::wayland::tablet_manager::{TabletManagerState, TabletSeatTrait}; use smithay::wayland::text_input::TextInputManagerState; use smithay::wayland::virtual_keyboard::VirtualKeyboardManagerState; @@ -157,6 +157,7 @@ pub struct Niri { pub cursor_shape_manager_state: CursorShapeManagerState, pub dnd_icon: Option, pub pointer_focus: Option, + pub tablet_cursor_location: Option>, pub lock_state: LockState, @@ -645,6 +646,23 @@ impl Niri { let cursor_manager = CursorManager::new(&config_.cursor.xcursor_theme, config_.cursor.xcursor_size); + let (tx, rx) = calloop::channel::channel(); + event_loop + .insert_source(rx, move |event, _, state| { + if let calloop::channel::Event::Msg(image) = event { + state.niri.cursor_manager.set_cursor_image(image); + // FIXME: granular. + state.niri.queue_redraw_all(); + } + }) + .unwrap(); + seat.tablet_seat() + .on_cursor_surface(move |_tool, new_image| { + if let Err(err) = tx.send(new_image) { + warn!("error sending new tablet cursor image: {err:?}"); + }; + }); + let screenshot_ui = ScreenshotUi::new(); let socket_source = ListeningSocketSource::new_auto().unwrap(); @@ -724,6 +742,7 @@ impl Niri { cursor_shape_manager_state, dnd_icon: None, pointer_focus: None, + tablet_cursor_location: None, lock_state: LockState::Unlocked, @@ -980,17 +999,21 @@ impl Niri { Some((output, pos_within_output)) } - pub fn window_under_cursor(&self) -> Option<&Window> { + pub fn window_under(&self, pos: Point) -> Option<&Window> { if self.is_locked() || self.screenshot_ui.is_open() { return None; } - let pos = self.seat.get_pointer().unwrap().current_location(); let (output, pos_within_output) = self.output_under(pos)?; let (window, _loc) = self.layout.window_under(output, pos_within_output)?; Some(window) } + pub fn window_under_cursor(&self) -> Option<&Window> { + let pos = self.seat.get_pointer().unwrap().current_location(); + self.window_under(pos) + } + /// Returns the surface under cursor and its position in the global space. /// /// Pointer needs location in global space, and focused window location compatible with that @@ -1223,7 +1246,12 @@ impl Niri { let _span = tracy_client::span!("Niri::pointer_element"); let output_scale = output.current_scale(); let output_pos = self.global_space.output_geometry(output).unwrap().loc; - let pointer_pos = self.seat.get_pointer().unwrap().current_location() - output_pos.to_f64(); + + // Check whether we need to draw the tablet cursor or the regular cursor. + let pointer_pos = self + .tablet_cursor_location + .unwrap_or_else(|| self.seat.get_pointer().unwrap().current_location()); + let pointer_pos = pointer_pos - output_pos.to_f64(); // Get the render cursor to draw. let cursor_scale = output_scale.integer_scale(); @@ -1294,6 +1322,11 @@ impl Niri { pub fn refresh_pointer_outputs(&mut self) { let _span = tracy_client::span!("Niri::refresh_pointer_outputs"); + // Check whether we need to draw the tablet cursor or the regular cursor. + let pointer_pos = self + .tablet_cursor_location + .unwrap_or_else(|| self.seat.get_pointer().unwrap().current_location()); + match self.cursor_manager.cursor_image().clone() { CursorImageStatus::Surface(ref surface) => { let hotspot = with_states(surface, |states| { @@ -1306,7 +1339,6 @@ impl Niri { .hotspot }); - let pointer_pos = self.seat.get_pointer().unwrap().current_location(); let surface_pos = pointer_pos.to_i32_round() - hotspot; let bbox = bbox_from_surface_tree(surface, surface_pos); @@ -1364,8 +1396,6 @@ impl Niri { Default::default() }; - let pointer_pos = self.seat.get_pointer().unwrap().current_location(); - let mut dnd_scale = 1; for output in self.global_space.outputs() { let geo = self.global_space.output_geometry(output).unwrap(); -- cgit