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 <mikayla@zed.dev>
This commit is contained in:
Owen Law 2024-05-06 16:19:28 -04:00 committed by GitHub
parent 5486c3dc93
commit 9a60c0a059
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 45 additions and 60 deletions

View file

@ -35,7 +35,7 @@ use super::{
super::{open_uri_internal, SCROLL_LINES}, super::{open_uri_internal, SCROLL_LINES},
X11Display, X11WindowStatePtr, XcbAtoms, 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::is_within_click_distance;
use crate::platform::linux::platform::DOUBLE_CLICK_INTERVAL; use crate::platform::linux::platform::DOUBLE_CLICK_INTERVAL;
@ -390,16 +390,16 @@ impl X11Client {
drop(state); drop(state);
window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke })); window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent { keystroke }));
} }
Event::ButtonPress(event) => { Event::XinputButtonPress(event) => {
let window = self.get_window(event.event)?; let window = self.get_window(event.event)?;
let mut state = self.0.borrow_mut(); 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( let position = point(
px(event.event_x as f32 / state.scale_factor), px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
px(event.event_y 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(); let click_elapsed = state.last_click.elapsed();
if click_elapsed < DOUBLE_CLICK_INTERVAL if click_elapsed < DOUBLE_CLICK_INTERVAL
@ -426,15 +426,15 @@ impl X11Client {
log::warn!("Unknown button press: {event:?}"); log::warn!("Unknown button press: {event:?}");
} }
} }
Event::ButtonRelease(event) => { Event::XinputButtonRelease(event) => {
let window = self.get_window(event.event)?; let window = self.get_window(event.event)?;
let state = self.0.borrow(); let state = self.0.borrow();
let modifiers = modifiers_from_state(event.state); let modifiers = modifiers_from_xinput_info(event.mods);
let position = point( let position = point(
px(event.event_x as f32 / state.scale_factor), px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
px(event.event_y 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; let click_count = state.current_count;
drop(state); drop(state);
window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent { window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
@ -448,6 +448,7 @@ impl X11Client {
Event::XinputMotion(event) => { Event::XinputMotion(event) => {
let window = self.get_window(event.event)?; let window = self.get_window(event.event)?;
let state = self.0.borrow(); let state = self.0.borrow();
let pressed_button = button_from_mask(event.button_mask[0]);
let position = point( let position = point(
px(event.event_x as f32 / u16::MAX 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), 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 { if event.valuator_mask[0] & 3 != 0 {
window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent { window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
position, position,
pressed_button: None, pressed_button,
modifiers, modifiers,
})); }));
} }
@ -524,32 +525,20 @@ impl X11Client {
valuator_idx += 1; valuator_idx += 1;
} }
} }
Event::MotionNotify(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 window = self.get_window(event.event)?;
let state = self.0.borrow(); 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( let position = point(
px(event.event_x as f32 / state.scale_factor), px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
px(event.event_y 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::MouseMove(crate::MouseMoveEvent {
pressed_button,
position,
modifiers,
}));
}
Event::LeaveNotify(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); drop(state);
window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent { window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
pressed_button, pressed_button,
position, position,

View file

@ -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<MouseButton> { pub(crate) fn button_from_mask(button_mask: u32) -> Option<MouseButton> {
Some(if state.contains(xproto::KeyButMask::BUTTON1) { Some(if button_mask & 2 == 2 {
MouseButton::Left MouseButton::Left
} else if state.contains(xproto::KeyButMask::BUTTON2) { } else if button_mask & 4 == 4 {
MouseButton::Middle MouseButton::Middle
} else if state.contains(xproto::KeyButMask::BUTTON3) { } else if button_mask & 8 == 8 {
MouseButton::Right MouseButton::Right
} else { } else {
return None; return None;

View file

@ -16,11 +16,11 @@ use x11rb::{
connection::{Connection as _, RequestConnection as _}, connection::{Connection as _, RequestConnection as _},
protocol::{ protocol::{
render::{self, ConnectionExt as _}, render::{self, ConnectionExt as _},
xinput, xinput::{self, ConnectionExt as _},
xproto::{self, ConnectionExt as _}, xproto::{self, ConnectionExt as _, CreateWindowAux},
}, },
resource_manager::Database, resource_manager::Database,
wrapper::ConnectionExt, wrapper::ConnectionExt as _,
xcb_ffi::XCBConnection, xcb_ffi::XCBConnection,
}; };
@ -262,14 +262,7 @@ impl X11WindowState {
| xproto::EventMask::LEAVE_WINDOW | xproto::EventMask::LEAVE_WINDOW
| xproto::EventMask::FOCUS_CHANGE | xproto::EventMask::FOCUS_CHANGE
| xproto::EventMask::KEY_PRESS | xproto::EventMask::KEY_PRESS
| xproto::EventMask::KEY_RELEASE | 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,
); );
xcb_connection xcb_connection
@ -290,18 +283,6 @@ impl X11WindowState {
.check() .check()
.unwrap(); .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(titlebar) = params.titlebar {
if let Some(title) = titlebar.title { if let Some(title) = titlebar.title {
xcb_connection xcb_connection
@ -326,6 +307,21 @@ impl X11WindowState {
) )
.unwrap(); .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.map_window(x_window).unwrap();
xcb_connection.flush().unwrap(); xcb_connection.flush().unwrap();