From 9a60c0a0590616e40516cc9ddd25d883ddd98dbe Mon Sep 17 00:00:00 2001 From: Owen Law <81528246+someone13574@users.noreply.github.com> Date: Mon, 6 May 2024 16:19:28 -0400 Subject: [PATCH] Replace all X11 mouse events with XI2 equivalents (#11235) This PR replaces all pointer events on X11 with their XI2 equivalents, which fixes problems with scroll events not being reported when a mouse button is down. Additionally it closes #11206 by resetting the tracked global scroll valulator position with `None` on a leave event to prevent a large scroll delta if scrolling is done outside the window. Lastly, it resolves the bad window issue kvark was having. Release Notes: - Fixed X11 Scroll snapping (#11206 ). --------- Co-authored-by: Mikayla Maki --- crates/gpui/src/platform/linux/x11/client.rs | 55 ++++++++------------ crates/gpui/src/platform/linux/x11/event.rs | 8 +-- crates/gpui/src/platform/linux/x11/window.rs | 42 +++++++-------- 3 files changed, 45 insertions(+), 60 deletions(-) diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index b1d6917d7ac6e4036099478d19d77c1a36ef9d46..1c3a21c4319acdd0cf732c4d300f108fef49a6d5 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -35,7 +35,7 @@ use super::{ super::{open_uri_internal, SCROLL_LINES}, X11Display, X11WindowStatePtr, XcbAtoms, }; -use super::{button_of_key, modifiers_from_state}; +use super::{button_from_mask, button_of_key, modifiers_from_state}; use crate::platform::linux::is_within_click_distance; use crate::platform::linux::platform::DOUBLE_CLICK_INTERVAL; @@ -390,16 +390,16 @@ impl X11Client { drop(state); window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke })); } - Event::ButtonPress(event) => { + Event::XinputButtonPress(event) => { let window = self.get_window(event.event)?; let mut state = self.0.borrow_mut(); - let modifiers = modifiers_from_state(event.state); + let modifiers = modifiers_from_xinput_info(event.mods); let position = point( - px(event.event_x as f32 / state.scale_factor), - px(event.event_y as f32 / state.scale_factor), + px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), + px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), ); - if let Some(button) = button_of_key(event.detail) { + if let Some(button) = button_of_key(event.detail.try_into().unwrap()) { let click_elapsed = state.last_click.elapsed(); if click_elapsed < DOUBLE_CLICK_INTERVAL @@ -426,15 +426,15 @@ impl X11Client { log::warn!("Unknown button press: {event:?}"); } } - Event::ButtonRelease(event) => { + Event::XinputButtonRelease(event) => { let window = self.get_window(event.event)?; let state = self.0.borrow(); - let modifiers = modifiers_from_state(event.state); + let modifiers = modifiers_from_xinput_info(event.mods); let position = point( - px(event.event_x as f32 / state.scale_factor), - px(event.event_y as f32 / state.scale_factor), + px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), + px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), ); - if let Some(button) = button_of_key(event.detail) { + if let Some(button) = button_of_key(event.detail.try_into().unwrap()) { let click_count = state.current_count; drop(state); window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent { @@ -448,6 +448,7 @@ impl X11Client { Event::XinputMotion(event) => { let window = self.get_window(event.event)?; let state = self.0.borrow(); + let pressed_button = button_from_mask(event.button_mask[0]); let position = point( px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), @@ -464,7 +465,7 @@ impl X11Client { if event.valuator_mask[0] & 3 != 0 { window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent { position, - pressed_button: None, + pressed_button, modifiers, })); } @@ -524,32 +525,20 @@ impl X11Client { valuator_idx += 1; } } - Event::MotionNotify(event) => { - let window = self.get_window(event.event)?; - let state = self.0.borrow(); - let pressed_button = super::button_from_state(event.state); - let position = point( - px(event.event_x as f32 / state.scale_factor), - px(event.event_y as f32 / state.scale_factor), - ); - let modifiers = modifiers_from_state(event.state); - drop(state); - window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent { - pressed_button, - position, - modifiers, - })); - } - Event::LeaveNotify(event) => { + Event::XinputLeave(event) => { + self.0.borrow_mut().scroll_x = None; // Set last scroll to `None` so that a large delta isn't created if scrolling is done outside the window (the valuator is global) + self.0.borrow_mut().scroll_y = None; + let window = self.get_window(event.event)?; let state = self.0.borrow(); - let pressed_button = super::button_from_state(event.state); + let pressed_button = button_from_mask(event.buttons[0]); let position = point( - px(event.event_x as f32 / state.scale_factor), - px(event.event_y as f32 / state.scale_factor), + px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor), + px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor), ); - let modifiers = modifiers_from_state(event.state); + let modifiers = modifiers_from_xinput_info(event.mods); drop(state); + window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent { pressed_button, position, diff --git a/crates/gpui/src/platform/linux/x11/event.rs b/crates/gpui/src/platform/linux/x11/event.rs index 29427db022d7992cdae8fbc4753a484abdd94c57..fb16a854909b6bc0e30e5f3427c04d4c2ff5f844 100644 --- a/crates/gpui/src/platform/linux/x11/event.rs +++ b/crates/gpui/src/platform/linux/x11/event.rs @@ -37,12 +37,12 @@ pub(crate) fn modifiers_from_xinput_info(modifier_info: xinput::ModifierInfo) -> } } -pub(crate) fn button_from_state(state: xproto::KeyButMask) -> Option { - Some(if state.contains(xproto::KeyButMask::BUTTON1) { +pub(crate) fn button_from_mask(button_mask: u32) -> Option { + Some(if button_mask & 2 == 2 { MouseButton::Left - } else if state.contains(xproto::KeyButMask::BUTTON2) { + } else if button_mask & 4 == 4 { MouseButton::Middle - } else if state.contains(xproto::KeyButMask::BUTTON3) { + } else if button_mask & 8 == 8 { MouseButton::Right } else { return None; diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 8a235a39118f091dda11529e86371e0d484998f7..d5adb5fe91a9fe124aa349ec2f88108dcc27f35b 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -16,11 +16,11 @@ use x11rb::{ connection::{Connection as _, RequestConnection as _}, protocol::{ render::{self, ConnectionExt as _}, - xinput, - xproto::{self, ConnectionExt as _}, + xinput::{self, ConnectionExt as _}, + xproto::{self, ConnectionExt as _, CreateWindowAux}, }, resource_manager::Database, - wrapper::ConnectionExt, + wrapper::ConnectionExt as _, xcb_ffi::XCBConnection, }; @@ -262,14 +262,7 @@ impl X11WindowState { | xproto::EventMask::LEAVE_WINDOW | xproto::EventMask::FOCUS_CHANGE | xproto::EventMask::KEY_PRESS - | xproto::EventMask::KEY_RELEASE - | xproto::EventMask::BUTTON_PRESS - | xproto::EventMask::BUTTON_RELEASE - | xproto::EventMask::POINTER_MOTION - | xproto::EventMask::BUTTON1_MOTION - | xproto::EventMask::BUTTON2_MOTION - | xproto::EventMask::BUTTON3_MOTION - | xproto::EventMask::BUTTON_MOTION, + | xproto::EventMask::KEY_RELEASE, ); xcb_connection @@ -290,18 +283,6 @@ impl X11WindowState { .check() .unwrap(); - xinput::ConnectionExt::xinput_xi_select_events( - &xcb_connection, - x_window, - &[xinput::EventMask { - deviceid: 1, - mask: vec![xinput::XIEventMask::MOTION], - }], - ) - .unwrap() - .check() - .unwrap(); - if let Some(titlebar) = params.titlebar { if let Some(title) = titlebar.title { xcb_connection @@ -326,6 +307,21 @@ impl X11WindowState { ) .unwrap(); + xcb_connection + .xinput_xi_select_events( + x_window, + &[xinput::EventMask { + deviceid: 1, + mask: vec![ + xinput::XIEventMask::MOTION + | xinput::XIEventMask::BUTTON_PRESS + | xinput::XIEventMask::BUTTON_RELEASE + | xinput::XIEventMask::LEAVE, + ], + }], + ) + .unwrap(); + xcb_connection.map_window(x_window).unwrap(); xcb_connection.flush().unwrap();