Linux/x11 input handling (#7811)
Implements the basics of keyboard and mouse handling. Some keys will need special treatment, like Backspace/Delete. In this PR, all keys are treated as append-only. Leaving this for a follow-up. I used @gabydd 's branch as a reference (thank you!) as well as https://github.com/xkbcommon/libxkbcommon/blob/master/doc/quick-guide.md For future work, I'll also use https://github.com/xkbcommon/libxkbcommon/blob/master/tools/interactive-x11.c All commits are separately compileable and reviewable. Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
aa319ccfd0
commit
a41fb29e01
15 changed files with 253 additions and 53 deletions
19
Cargo.lock
generated
19
Cargo.lock
generated
|
@ -3528,6 +3528,7 @@ dependencies = [
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"xcb",
|
"xcb",
|
||||||
|
"xkbcommon",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -10840,6 +10841,24 @@ dependencies = [
|
||||||
"quick-xml 0.30.0",
|
"quick-xml 0.30.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xkbcommon"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
|
||||||
|
dependencies = [
|
||||||
|
"as-raw-xcb-connection",
|
||||||
|
"libc",
|
||||||
|
"memmap2 0.8.0",
|
||||||
|
"xkeysym",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xkeysym"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xmlparser"
|
name = "xmlparser"
|
||||||
version = "0.13.5"
|
version = "0.13.5"
|
||||||
|
|
|
@ -96,7 +96,8 @@ objc = "0.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
flume = "0.11"
|
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-client= { version = "0.31.2" }
|
||||||
wayland-protocols = { version = "0.31.2", features = ["client"] }
|
wayland-protocols = { version = "0.31.2", features = ["client"] }
|
||||||
wayland-backend = { version = "0.3.3", features = ["client_system"] }
|
wayland-backend = { version = "0.3.3", features = ["client_system"] }
|
||||||
|
@ -106,3 +107,4 @@ blade-graphics = { git = "https://github.com/kvark/blade", rev = "c4f951a88b3457
|
||||||
blade-macros = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" }
|
blade-macros = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" }
|
||||||
bytemuck = "1"
|
bytemuck = "1"
|
||||||
cosmic-text = "0.10.0"
|
cosmic-text = "0.10.0"
|
||||||
|
xkbcommon = { version = "0.7", features = ["x11"] }
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
//todo!(linux): remove this
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
|
|
||||||
mod blade_atlas;
|
mod blade_atlas;
|
||||||
mod blade_belt;
|
mod blade_belt;
|
||||||
mod blade_renderer;
|
mod blade_renderer;
|
||||||
|
|
|
@ -458,7 +458,7 @@ impl BladeRenderer {
|
||||||
sprites,
|
sprites,
|
||||||
} => {
|
} => {
|
||||||
let tex_info = self.atlas.get_texture_info(texture_id);
|
let tex_info = self.atlas.get_texture_info(texture_id);
|
||||||
let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
|
let instance_buf = self.instance_belt.alloc_data(sprites, &self.gpu);
|
||||||
let mut encoder = pass.with(&self.pipelines.mono_sprites);
|
let mut encoder = pass.with(&self.pipelines.mono_sprites);
|
||||||
encoder.bind(
|
encoder.bind(
|
||||||
0,
|
0,
|
||||||
|
@ -476,7 +476,7 @@ impl BladeRenderer {
|
||||||
sprites,
|
sprites,
|
||||||
} => {
|
} => {
|
||||||
let tex_info = self.atlas.get_texture_info(texture_id);
|
let tex_info = self.atlas.get_texture_info(texture_id);
|
||||||
let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
|
let instance_buf = self.instance_belt.alloc_data(sprites, &self.gpu);
|
||||||
let mut encoder = pass.with(&self.pipelines.poly_sprites);
|
let mut encoder = pass.with(&self.pipelines.poly_sprites);
|
||||||
encoder.bind(
|
encoder.bind(
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -49,8 +49,8 @@ pub(crate) struct LinuxPlatformInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct LinuxPlatform {
|
pub(crate) struct LinuxPlatform {
|
||||||
client: Arc<dyn Client>,
|
client: Rc<dyn Client>,
|
||||||
inner: Arc<LinuxPlatformInner>,
|
inner: Rc<LinuxPlatformInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct LinuxPlatformState {
|
pub(crate) struct LinuxPlatformState {
|
||||||
|
@ -93,7 +93,7 @@ impl LinuxPlatform {
|
||||||
let client_dispatcher: Arc<dyn ClientDispatcher + Send + Sync> =
|
let client_dispatcher: Arc<dyn ClientDispatcher + Send + Sync> =
|
||||||
Arc::new(WaylandClientDispatcher::new(&conn));
|
Arc::new(WaylandClientDispatcher::new(&conn));
|
||||||
let dispatcher = Arc::new(LinuxDispatcher::new(main_sender, &client_dispatcher));
|
let dispatcher = Arc::new(LinuxDispatcher::new(main_sender, &client_dispatcher));
|
||||||
let inner = Arc::new(LinuxPlatformInner {
|
let inner = Rc::new(LinuxPlatformInner {
|
||||||
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
||||||
foreground_executor: ForegroundExecutor::new(dispatcher.clone()),
|
foreground_executor: ForegroundExecutor::new(dispatcher.clone()),
|
||||||
main_receiver,
|
main_receiver,
|
||||||
|
@ -101,10 +101,10 @@ impl LinuxPlatform {
|
||||||
callbacks,
|
callbacks,
|
||||||
state,
|
state,
|
||||||
});
|
});
|
||||||
let client = Arc::new(WaylandClient::new(Arc::clone(&inner), Arc::clone(&conn)));
|
let client = Rc::new(WaylandClient::new(Rc::clone(&inner), Arc::clone(&conn)));
|
||||||
Self {
|
Self {
|
||||||
client,
|
client,
|
||||||
inner: Arc::clone(&inner),
|
inner: Rc::clone(&inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,15 +115,27 @@ impl LinuxPlatform {
|
||||||
callbacks: Mutex<Callbacks>,
|
callbacks: Mutex<Callbacks>,
|
||||||
state: Mutex<LinuxPlatformState>,
|
state: Mutex<LinuxPlatformState>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (xcb_connection, x_root_index) =
|
let (xcb_connection, x_root_index) = xcb::Connection::connect_with_extensions(
|
||||||
xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Present], &[])
|
None,
|
||||||
|
&[xcb::Extension::Present, xcb::Extension::Xkb],
|
||||||
|
&[],
|
||||||
|
)
|
||||||
.unwrap();
|
.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 atoms = XcbAtoms::intern_all(&xcb_connection).unwrap();
|
||||||
let xcb_connection = Arc::new(xcb_connection);
|
let xcb_connection = Arc::new(xcb_connection);
|
||||||
let client_dispatcher: Arc<dyn ClientDispatcher + Send + Sync> =
|
let client_dispatcher: Arc<dyn ClientDispatcher + Send + Sync> =
|
||||||
Arc::new(X11ClientDispatcher::new(&xcb_connection, x_root_index));
|
Arc::new(X11ClientDispatcher::new(&xcb_connection, x_root_index));
|
||||||
let dispatcher = Arc::new(LinuxDispatcher::new(main_sender, &client_dispatcher));
|
let dispatcher = Arc::new(LinuxDispatcher::new(main_sender, &client_dispatcher));
|
||||||
let inner = Arc::new(LinuxPlatformInner {
|
let inner = Rc::new(LinuxPlatformInner {
|
||||||
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
||||||
foreground_executor: ForegroundExecutor::new(dispatcher.clone()),
|
foreground_executor: ForegroundExecutor::new(dispatcher.clone()),
|
||||||
main_receiver,
|
main_receiver,
|
||||||
|
@ -131,15 +143,15 @@ impl LinuxPlatform {
|
||||||
callbacks,
|
callbacks,
|
||||||
state,
|
state,
|
||||||
});
|
});
|
||||||
let client = Arc::new(X11Client::new(
|
let client = Rc::new(X11Client::new(
|
||||||
Arc::clone(&inner),
|
Rc::clone(&inner),
|
||||||
xcb_connection,
|
xcb_connection,
|
||||||
x_root_index,
|
x_root_index,
|
||||||
atoms,
|
atoms,
|
||||||
));
|
));
|
||||||
Self {
|
Self {
|
||||||
client,
|
client,
|
||||||
inner: Arc::clone(&inner),
|
inner: Rc::clone(&inner),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//todo!(linux): remove this once the relevant functionality has been implemented
|
||||||
|
#![allow(unused_variables)]
|
||||||
|
|
||||||
pub(crate) use client::*;
|
pub(crate) use client::*;
|
||||||
pub(crate) use client_dispatcher::*;
|
pub(crate) use client_dispatcher::*;
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,12 @@ pub(crate) struct WaylandClientState {
|
||||||
compositor: Option<wl_compositor::WlCompositor>,
|
compositor: Option<wl_compositor::WlCompositor>,
|
||||||
buffer: Option<wl_buffer::WlBuffer>,
|
buffer: Option<wl_buffer::WlBuffer>,
|
||||||
wm_base: Option<xdg_wm_base::XdgWmBase>,
|
wm_base: Option<xdg_wm_base::XdgWmBase>,
|
||||||
windows: Vec<(xdg_surface::XdgSurface, Arc<WaylandWindowState>)>,
|
windows: Vec<(xdg_surface::XdgSurface, Rc<WaylandWindowState>)>,
|
||||||
platform_inner: Arc<LinuxPlatformInner>,
|
platform_inner: Rc<LinuxPlatformInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct WaylandClient {
|
pub(crate) struct WaylandClient {
|
||||||
platform_inner: Arc<LinuxPlatformInner>,
|
platform_inner: Rc<LinuxPlatformInner>,
|
||||||
conn: Arc<Connection>,
|
conn: Arc<Connection>,
|
||||||
state: Mutex<WaylandClientState>,
|
state: Mutex<WaylandClientState>,
|
||||||
event_queue: Mutex<EventQueue<WaylandClientState>>,
|
event_queue: Mutex<EventQueue<WaylandClientState>>,
|
||||||
|
@ -39,16 +39,13 @@ pub(crate) struct WaylandClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WaylandClient {
|
impl WaylandClient {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(linux_platform_inner: Rc<LinuxPlatformInner>, conn: Arc<Connection>) -> Self {
|
||||||
linux_platform_inner: Arc<LinuxPlatformInner>,
|
|
||||||
conn: Arc<Connection>,
|
|
||||||
) -> Self {
|
|
||||||
let state = WaylandClientState {
|
let state = WaylandClientState {
|
||||||
compositor: None,
|
compositor: None,
|
||||||
buffer: None,
|
buffer: None,
|
||||||
wm_base: None,
|
wm_base: None,
|
||||||
windows: Vec::new(),
|
windows: Vec::new(),
|
||||||
platform_inner: Arc::clone(&linux_platform_inner),
|
platform_inner: Rc::clone(&linux_platform_inner),
|
||||||
};
|
};
|
||||||
let event_queue: EventQueue<WaylandClientState> = conn.new_event_queue();
|
let event_queue: EventQueue<WaylandClientState> = conn.new_event_queue();
|
||||||
let qh = event_queue.handle();
|
let qh = event_queue.handle();
|
||||||
|
@ -109,14 +106,14 @@ impl Client for WaylandClient {
|
||||||
wl_surface.frame(&self.qh, wl_surface.clone());
|
wl_surface.frame(&self.qh, wl_surface.clone());
|
||||||
wl_surface.commit();
|
wl_surface.commit();
|
||||||
|
|
||||||
let window_state: Arc<WaylandWindowState> = Arc::new(WaylandWindowState::new(
|
let window_state = Rc::new(WaylandWindowState::new(
|
||||||
&self.conn,
|
&self.conn,
|
||||||
wl_surface.clone(),
|
wl_surface.clone(),
|
||||||
Arc::new(toplevel),
|
Arc::new(toplevel),
|
||||||
options,
|
options,
|
||||||
));
|
));
|
||||||
|
|
||||||
state.windows.push((xdg_surface, Arc::clone(&window_state)));
|
state.windows.push((xdg_surface, Rc::clone(&window_state)));
|
||||||
Box::new(WaylandWindow(window_state))
|
Box::new(WaylandWindow(window_state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ pub(crate) struct WaylandDisplay {}
|
||||||
impl PlatformDisplay for WaylandDisplay {
|
impl PlatformDisplay for WaylandDisplay {
|
||||||
// todo!(linux)
|
// todo!(linux)
|
||||||
fn id(&self) -> DisplayId {
|
fn id(&self) -> DisplayId {
|
||||||
return DisplayId(123); // return some fake data so it doesn't panic
|
DisplayId(123) // return some fake data so it doesn't panic
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo!(linux)
|
// todo!(linux)
|
||||||
fn uuid(&self) -> anyhow::Result<Uuid> {
|
fn uuid(&self) -> anyhow::Result<Uuid> {
|
||||||
return Ok(Uuid::from_bytes([0; 16])); // return some fake data so it doesn't panic
|
Ok(Uuid::from_bytes([0; 16])) // return some fake data so it doesn't panic
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo!(linux)
|
// todo!(linux)
|
||||||
|
|
|
@ -181,7 +181,7 @@ impl WaylandWindowState {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct WaylandWindow(pub(crate) Arc<WaylandWindowState>);
|
pub(crate) struct WaylandWindow(pub(crate) Rc<WaylandWindowState>);
|
||||||
|
|
||||||
impl HasWindowHandle for WaylandWindow {
|
impl HasWindowHandle for WaylandWindow {
|
||||||
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
|
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
|
||||||
|
@ -212,7 +212,7 @@ impl PlatformWindow for WaylandWindow {
|
||||||
|
|
||||||
// todo!(linux)
|
// todo!(linux)
|
||||||
fn scale_factor(&self) -> f32 {
|
fn scale_factor(&self) -> f32 {
|
||||||
return 1f32;
|
1f32
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo!(linux)
|
//todo!(linux)
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
mod client;
|
mod client;
|
||||||
mod client_dispatcher;
|
mod client_dispatcher;
|
||||||
pub mod display;
|
mod display;
|
||||||
|
mod event;
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
pub(crate) use client::*;
|
pub(crate) use client::*;
|
||||||
pub(crate) use client_dispatcher::*;
|
pub(crate) use client_dispatcher::*;
|
||||||
pub(crate) use display::*;
|
pub(crate) use display::*;
|
||||||
|
pub(crate) use event::*;
|
||||||
pub(crate) use window::*;
|
pub(crate) use window::*;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::rc::Rc;
|
use std::{rc::Rc, sync::Arc};
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use xcb::{x, Xid};
|
use xcb::{x, Xid as _};
|
||||||
|
use xkbcommon::xkb;
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
|
||||||
|
@ -10,14 +10,17 @@ use crate::platform::linux::client::Client;
|
||||||
use crate::platform::{
|
use crate::platform::{
|
||||||
LinuxPlatformInner, PlatformWindow, X11Display, X11Window, X11WindowState, XcbAtoms,
|
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) struct X11ClientState {
|
||||||
pub(crate) windows: HashMap<x::Window, Rc<X11WindowState>>,
|
pub(crate) windows: HashMap<x::Window, Rc<X11WindowState>>,
|
||||||
|
xkb: xkbcommon::xkb::State,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct X11Client {
|
pub(crate) struct X11Client {
|
||||||
platform_inner: Arc<LinuxPlatformInner>,
|
platform_inner: Rc<LinuxPlatformInner>,
|
||||||
xcb_connection: Arc<xcb::Connection>,
|
xcb_connection: Arc<xcb::Connection>,
|
||||||
x_root_index: i32,
|
x_root_index: i32,
|
||||||
atoms: XcbAtoms,
|
atoms: XcbAtoms,
|
||||||
|
@ -26,11 +29,22 @@ pub(crate) struct X11Client {
|
||||||
|
|
||||||
impl X11Client {
|
impl X11Client {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
inner: Arc<LinuxPlatformInner>,
|
inner: Rc<LinuxPlatformInner>,
|
||||||
xcb_connection: Arc<xcb::Connection>,
|
xcb_connection: Arc<xcb::Connection>,
|
||||||
x_root_index: i32,
|
x_root_index: i32,
|
||||||
atoms: XcbAtoms,
|
atoms: XcbAtoms,
|
||||||
) -> Self {
|
) -> 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 {
|
Self {
|
||||||
platform_inner: inner,
|
platform_inner: inner,
|
||||||
xcb_connection,
|
xcb_connection,
|
||||||
|
@ -38,6 +52,7 @@ impl X11Client {
|
||||||
atoms,
|
atoms,
|
||||||
state: Mutex::new(X11ClientState {
|
state: Mutex::new(X11ClientState {
|
||||||
windows: HashMap::default(),
|
windows: HashMap::default(),
|
||||||
|
xkb: xkb_state,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +106,97 @@ impl Client for X11Client {
|
||||||
window.request_refresh();
|
window.request_refresh();
|
||||||
}
|
}
|
||||||
xcb::Event::Present(xcb::present::Event::IdleNotify(_ev)) => {}
|
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,
|
||||||
|
}));
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
34
crates/gpui/src/platform/linux/x11/event.rs
Normal file
34
crates/gpui/src/platform/linux/x11/event.rs
Normal file
|
@ -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;
|
||||||
|
})
|
||||||
|
}
|
|
@ -13,15 +13,12 @@ use std::{
|
||||||
use blade_graphics as gpu;
|
use blade_graphics as gpu;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use raw_window_handle as rwh;
|
use raw_window_handle as rwh;
|
||||||
use xcb::{
|
use xcb::{x, Xid as _};
|
||||||
x::{self, StackMode},
|
|
||||||
Xid as _,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::platform::linux::blade_renderer::BladeRenderer;
|
use crate::platform::linux::blade_renderer::BladeRenderer;
|
||||||
use crate::{
|
use crate::{
|
||||||
Bounds, GlobalPixels, Pixels, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
|
Bounds, GlobalPixels, Pixels, PlatformDisplay, PlatformInput, PlatformInputHandler,
|
||||||
Size, WindowAppearance, WindowBounds, WindowOptions, X11Display,
|
PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, X11Display,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -52,6 +49,7 @@ struct LinuxWindowInner {
|
||||||
bounds: Bounds<i32>,
|
bounds: Bounds<i32>,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
renderer: BladeRenderer,
|
renderer: BladeRenderer,
|
||||||
|
input_handler: Option<PlatformInputHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinuxWindowInner {
|
impl LinuxWindowInner {
|
||||||
|
@ -152,7 +150,19 @@ impl X11WindowState {
|
||||||
let xcb_values = [
|
let xcb_values = [
|
||||||
x::Cw::BackPixel(screen.white_pixel()),
|
x::Cw::BackPixel(screen.white_pixel()),
|
||||||
x::Cw::EventMask(
|
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
|
||||||
|
| x::EventMask::BUTTON_MOTION,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -238,7 +248,7 @@ impl X11WindowState {
|
||||||
|
|
||||||
// Note: this has to be done after the GPU init, or otherwise
|
// Note: this has to be done after the GPU init, or otherwise
|
||||||
// the sizes are immediately invalidated.
|
// the sizes are immediately invalidated.
|
||||||
let gpu_extent = query_render_extent(&xcb_connection, x_window);
|
let gpu_extent = query_render_extent(xcb_connection, x_window);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
xcb_connection: Arc::clone(xcb_connection),
|
xcb_connection: Arc::clone(xcb_connection),
|
||||||
|
@ -250,6 +260,7 @@ impl X11WindowState {
|
||||||
bounds,
|
bounds,
|
||||||
scale_factor: 1.0,
|
scale_factor: 1.0,
|
||||||
renderer: BladeRenderer::new(gpu, gpu_extent),
|
renderer: BladeRenderer::new(gpu, gpu_extent),
|
||||||
|
input_handler: None,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -313,6 +324,20 @@ impl X11WindowState {
|
||||||
})
|
})
|
||||||
.unwrap();
|
.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 {
|
impl PlatformWindow for X11Window {
|
||||||
|
@ -356,12 +381,12 @@ impl PlatformWindow for X11Window {
|
||||||
self
|
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> {
|
fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
|
||||||
None
|
self.0.inner.lock().input_handler.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo!(linux)
|
//todo!(linux)
|
||||||
|
@ -378,7 +403,7 @@ impl PlatformWindow for X11Window {
|
||||||
fn activate(&self) {
|
fn activate(&self) {
|
||||||
self.0.xcb_connection.send_request(&x::ConfigureWindow {
|
self.0.xcb_connection.send_request(&x::ConfigureWindow {
|
||||||
window: self.0.x_window,
|
window: self.0.x_window,
|
||||||
value_list: &[x::ConfigWindow::StackMode(StackMode::Above)],
|
value_list: &[x::ConfigWindow::StackMode(x::StackMode::Above)],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,4 +6,4 @@ set -euxo pipefail
|
||||||
# so specify those here, and disable the rest until Zed's workspace
|
# so specify those here, and disable the rest until Zed's workspace
|
||||||
# will have more fixes & suppression for the standard lint set
|
# will have more fixes & suppression for the standard lint set
|
||||||
cargo clippy --release --workspace --all-features --all-targets -- -A clippy::all -D clippy::dbg_macro -D clippy::todo
|
cargo clippy --release --workspace --all-features --all-targets -- -A clippy::all -D clippy::dbg_macro -D clippy::todo
|
||||||
cargo clippy -p gpui
|
cargo clippy -p gpui -- -D warnings
|
||||||
|
|
|
@ -12,6 +12,7 @@ if [[ -n $apt ]]; then
|
||||||
libfontconfig-dev
|
libfontconfig-dev
|
||||||
vulkan-validationlayers*
|
vulkan-validationlayers*
|
||||||
libwayland-dev
|
libwayland-dev
|
||||||
|
libxkbcommon-x11-dev
|
||||||
)
|
)
|
||||||
$maysudo "$apt" install -y "${deps[@]}"
|
$maysudo "$apt" install -y "${deps[@]}"
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -26,6 +27,7 @@ if [[ -n $dnf ]]; then
|
||||||
fontconfig-devel
|
fontconfig-devel
|
||||||
vulkan-validation-layers
|
vulkan-validation-layers
|
||||||
wayland-devel
|
wayland-devel
|
||||||
|
libxkbcommon-x11-devel
|
||||||
)
|
)
|
||||||
$maysudo "$dnf" install -y "${deps[@]}"
|
$maysudo "$dnf" install -y "${deps[@]}"
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -40,6 +42,7 @@ if [[ -n $pacman ]]; then
|
||||||
fontconfig
|
fontconfig
|
||||||
vulkan-validation-layers
|
vulkan-validation-layers
|
||||||
wayland
|
wayland
|
||||||
|
libxkbcommon-x11
|
||||||
)
|
)
|
||||||
$maysudo "$pacman" -S --needed --noconfirm "${deps[@]}"
|
$maysudo "$pacman" -S --needed --noconfirm "${deps[@]}"
|
||||||
exit 0
|
exit 0
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue