Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux/x11 input handling #7811

Merged
merged 5 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion crates/gpui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ objc = "0.2"

[target.'cfg(target_os = "linux")'.dependencies]
flume = "0.11"
xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr"] }
# todo!(linux) - Technically do not use `randr`, but it doesn't compile otherwise
xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr", "xkb"] }
wayland-client= { version = "0.31.2" }
wayland-protocols = { version = "0.31.2", features = ["client"] }
wayland-backend = { version = "0.3.3", features = ["client_system"] }
Expand All @@ -106,3 +107,4 @@ blade-graphics = { git = "https://github.com/kvark/blade", rev = "c4f951a88b3457
blade-macros = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" }
bytemuck = "1"
cosmic-text = "0.10.0"
xkbcommon = { version = "0.7", features = ["x11"] }
3 changes: 0 additions & 3 deletions crates/gpui/src/platform/linux.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//todo!(linux): remove this
#![allow(unused_variables)]

mod blade_atlas;
mod blade_belt;
mod blade_renderer;
Expand Down
18 changes: 15 additions & 3 deletions crates/gpui/src/platform/linux/platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,21 @@ impl LinuxPlatform {
callbacks: Mutex<Callbacks>,
state: Mutex<LinuxPlatformState>,
) -> Self {
let (xcb_connection, x_root_index) =
xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Present], &[])
.unwrap();
let (xcb_connection, x_root_index) = xcb::Connection::connect_with_extensions(
None,
&[xcb::Extension::Present, xcb::Extension::Xkb],
&[],
)
.unwrap();

let xkb_ver = xcb_connection
.wait_for_reply(xcb_connection.send_request(&xcb::xkb::UseExtension {
wanted_major: xcb::xkb::MAJOR_VERSION as u16,
wanted_minor: xcb::xkb::MINOR_VERSION as u16,
}))
.unwrap();
assert!(xkb_ver.supported());

let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap();
let xcb_connection = Arc::new(xcb_connection);
let client_dispatcher: Arc<dyn ClientDispatcher + Send + Sync> =
Expand Down
4 changes: 3 additions & 1 deletion crates/gpui/src/platform/linux/x11.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod client;
mod client_dispatcher;
pub mod display;
mod display;
mod event;
mod window;

pub(crate) use client::*;
pub(crate) use client_dispatcher::*;
pub(crate) use display::*;
pub(crate) use event::*;
pub(crate) use window::*;
114 changes: 110 additions & 4 deletions crates/gpui/src/platform/linux/x11/client.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
use std::rc::Rc;
use std::sync::Arc;
use std::{rc::Rc, sync::Arc};

use parking_lot::Mutex;
use xcb::{x, Xid};
use xcb::{x, Xid as _};
use xkbcommon::xkb;

use collections::HashMap;

use crate::platform::linux::client::Client;
use crate::platform::{
LinuxPlatformInner, PlatformWindow, X11Display, X11Window, X11WindowState, XcbAtoms,
};
use crate::{AnyWindowHandle, Bounds, DisplayId, PlatformDisplay, Point, Size, WindowOptions};
use crate::{
AnyWindowHandle, Bounds, DisplayId, PlatformDisplay, PlatformInput, Point, Size, WindowOptions,
};

pub(crate) struct X11ClientState {
pub(crate) windows: HashMap<x::Window, Rc<X11WindowState>>,
xkb: xkbcommon::xkb::State,
}

pub(crate) struct X11Client {
Expand All @@ -31,13 +34,25 @@ impl X11Client {
x_root_index: i32,
atoms: XcbAtoms,
) -> Self {
let xkb_context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS);
let xkb_device_id = xkb::x11::get_core_keyboard_device_id(&xcb_connection);
let xkb_keymap = xkb::x11::keymap_new_from_device(
&xkb_context,
&xcb_connection,
xkb_device_id,
xkb::KEYMAP_COMPILE_NO_FLAGS,
);
let xkb_state =
xkb::x11::state_new_from_device(&xkb_keymap, &xcb_connection, xkb_device_id);

Self {
platform_inner: inner,
xcb_connection,
x_root_index,
atoms,
state: Mutex::new(X11ClientState {
windows: HashMap::default(),
xkb: xkb_state,
}),
}
}
Expand Down Expand Up @@ -91,6 +106,97 @@ impl Client for X11Client {
window.request_refresh();
}
xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {}
xcb::Event::X(x::Event::KeyPress(ev)) => {
let window = self.get_window(ev.event());
let modifiers = super::modifiers_from_state(ev.state());
let key = {
let code = ev.detail().into();
let mut state = self.state.lock();
let key = state.xkb.key_get_utf8(code);
state.xkb.update_key(code, xkb::KeyDirection::Down);
key
};
window.handle_input(PlatformInput::KeyDown(crate::KeyDownEvent {
keystroke: crate::Keystroke {
modifiers,
key,
ime_key: None,
},
is_held: false,
}));
}
xcb::Event::X(x::Event::KeyRelease(ev)) => {
let window = self.get_window(ev.event());
let modifiers = super::modifiers_from_state(ev.state());
let key = {
let code = ev.detail().into();
let mut state = self.state.lock();
let key = state.xkb.key_get_utf8(code);
state.xkb.update_key(code, xkb::KeyDirection::Up);
key
};
window.handle_input(PlatformInput::KeyUp(crate::KeyUpEvent {
keystroke: crate::Keystroke {
modifiers,
key,
ime_key: None,
},
}));
}
xcb::Event::X(x::Event::ButtonPress(ev)) => {
let window = self.get_window(ev.event());
let modifiers = super::modifiers_from_state(ev.state());
let position =
Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
if let Some(button) = super::button_of_key(ev.detail()) {
window.handle_input(PlatformInput::MouseDown(crate::MouseDownEvent {
button,
position,
modifiers,
click_count: 1,
}));
} else {
log::warn!("Unknown button press: {ev:?}");
}
}
xcb::Event::X(x::Event::ButtonRelease(ev)) => {
let window = self.get_window(ev.event());
let modifiers = super::modifiers_from_state(ev.state());
let position =
Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
if let Some(button) = super::button_of_key(ev.detail()) {
window.handle_input(PlatformInput::MouseUp(crate::MouseUpEvent {
button,
position,
modifiers,
click_count: 1,
}));
}
}
xcb::Event::X(x::Event::MotionNotify(ev)) => {
let window = self.get_window(ev.event());
let pressed_button = super::button_from_state(ev.state());
let position =
Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
let modifiers = super::modifiers_from_state(ev.state());
window.handle_input(PlatformInput::MouseMove(crate::MouseMoveEvent {
pressed_button,
position,
modifiers,
}));
}
xcb::Event::X(x::Event::LeaveNotify(ev)) => {
let window = self.get_window(ev.event());
let pressed_button = super::button_from_state(ev.state());
let position =
Point::new((ev.event_x() as f32).into(), (ev.event_y() as f32).into());
let modifiers = super::modifiers_from_state(ev.state());
window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
pressed_button,
position,
modifiers,
}));
}
_ => {}
}

Expand Down
34 changes: 34 additions & 0 deletions crates/gpui/src/platform/linux/x11/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use xcb::x;

use crate::{Modifiers, MouseButton};

pub(crate) fn button_of_key(detail: x::Button) -> Option<MouseButton> {
Some(match detail {
1 => MouseButton::Left,
2 => MouseButton::Middle,
3 => MouseButton::Right,
_ => return None,
})
}

pub(crate) fn modifiers_from_state(state: x::KeyButMask) -> Modifiers {
Modifiers {
control: state.contains(x::KeyButMask::CONTROL),
alt: state.contains(x::KeyButMask::MOD1),
shift: state.contains(x::KeyButMask::SHIFT),
command: state.contains(x::KeyButMask::MOD4),
function: false,
}
}

pub(crate) fn button_from_state(state: x::KeyButMask) -> Option<MouseButton> {
Some(if state.contains(x::KeyButMask::BUTTON1) {
MouseButton::Left
} else if state.contains(x::KeyButMask::BUTTON2) {
MouseButton::Middle
} else if state.contains(x::KeyButMask::BUTTON3) {
MouseButton::Right
} else {
return None;
})
}
49 changes: 37 additions & 12 deletions crates/gpui/src/platform/linux/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,12 @@ use std::{
use blade_graphics as gpu;
use parking_lot::Mutex;
use raw_window_handle as rwh;
use xcb::{
x::{self, StackMode},
Xid as _,
};
use xcb::{x, Xid as _};

use crate::platform::linux::blade_renderer::BladeRenderer;
use crate::{
Bounds, GlobalPixels, Pixels, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
Size, WindowAppearance, WindowBounds, WindowOptions, X11Display,
Bounds, GlobalPixels, Pixels, PlatformDisplay, PlatformInput, PlatformInputHandler,
PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, X11Display,
};

#[derive(Default)]
Expand Down Expand Up @@ -52,6 +49,7 @@ struct LinuxWindowInner {
bounds: Bounds<i32>,
scale_factor: f32,
renderer: BladeRenderer,
input_handler: Option<PlatformInputHandler>,
}

impl LinuxWindowInner {
Expand Down Expand Up @@ -152,7 +150,19 @@ impl X11WindowState {
let xcb_values = [
x::Cw::BackPixel(screen.white_pixel()),
x::Cw::EventMask(
x::EventMask::EXPOSURE | x::EventMask::STRUCTURE_NOTIFY | x::EventMask::KEY_PRESS,
x::EventMask::EXPOSURE
| x::EventMask::STRUCTURE_NOTIFY
| x::EventMask::KEY_PRESS
| x::EventMask::KEY_RELEASE
| x::EventMask::BUTTON_PRESS
| x::EventMask::BUTTON_RELEASE
| x::EventMask::POINTER_MOTION
| x::EventMask::BUTTON1_MOTION
| x::EventMask::BUTTON2_MOTION
| x::EventMask::BUTTON3_MOTION
| x::EventMask::BUTTON4_MOTION
| x::EventMask::BUTTON5_MOTION
Comment on lines +160 to +164

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be uneducated, but what are these button1-5? If this is not commonplace knowledge maybe comments should be added to describe what each maps to

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mouse pointer motion while mouse button 1-5 is down.

| x::EventMask::BUTTON_MOTION,
),
];

Expand Down Expand Up @@ -250,6 +260,7 @@ impl X11WindowState {
bounds,
scale_factor: 1.0,
renderer: BladeRenderer::new(gpu, gpu_extent),
input_handler: None,
}),
}
}
Expand Down Expand Up @@ -313,6 +324,20 @@ impl X11WindowState {
})
.unwrap();
}

pub fn handle_input(&self, input: PlatformInput) {
if let Some(ref mut fun) = self.callbacks.lock().input {
if fun(input.clone()) {
return;
}
}
if let PlatformInput::KeyDown(event) = input {
let mut inner = self.inner.lock();
if let Some(ref mut input_handler) = inner.input_handler {
input_handler.replace_text_in_range(None, &event.keystroke.key);
}
}
}
}

impl PlatformWindow for X11Window {
Expand Down Expand Up @@ -356,12 +381,12 @@ impl PlatformWindow for X11Window {
self
}

//todo!(linux)
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {}
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
self.0.inner.lock().input_handler = Some(input_handler);
}

//todo!(linux)
fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
None
self.0.inner.lock().input_handler.take()
}

//todo!(linux)
Expand All @@ -378,7 +403,7 @@ impl PlatformWindow for X11Window {
fn activate(&self) {
self.0.xcb_connection.send_request(&x::ConfigureWindow {
window: self.0.x_window,
value_list: &[x::ConfigWindow::StackMode(StackMode::Above)],
value_list: &[x::ConfigWindow::StackMode(x::StackMode::Above)],
});
}

Expand Down
Loading
Loading