mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 15:04:36 +03:00
window: wayland: fix routing of keyboard events
The seat is a global thing, so we need to track the active surface and route events to the appropriate window.
This commit is contained in:
parent
2d6d54bfab
commit
29a6c62b6c
@ -109,6 +109,7 @@ fn spawn_window() -> Fallible<()> {
|
||||
}
|
||||
|
||||
fn main() -> Fallible<()> {
|
||||
pretty_env_logger::init();
|
||||
let conn = Connection::init()?;
|
||||
spawn_window()?;
|
||||
conn.run_message_loop()
|
||||
|
@ -1,4 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
use super::keyboard::KeyboardDispatcher;
|
||||
use super::window::*;
|
||||
use crate::connection::ConnectionOps;
|
||||
use crate::spawn::*;
|
||||
@ -15,6 +16,7 @@ use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::time::{Duration, Instant};
|
||||
use toolkit::reexports::client::protocol::wl_seat::{Event as SeatEvent, WlSeat};
|
||||
use toolkit::reexports::client::{Display, EventQueue};
|
||||
use toolkit::Environment;
|
||||
|
||||
@ -27,6 +29,8 @@ pub struct WaylandConnection {
|
||||
pub(crate) tasks: Tasks,
|
||||
pub(crate) next_window_id: AtomicUsize,
|
||||
pub(crate) windows: RefCell<HashMap<usize, Rc<RefCell<WaylandWindowInner>>>>,
|
||||
pub(crate) seat: WlSeat,
|
||||
pub(crate) keyboard: KeyboardDispatcher,
|
||||
}
|
||||
|
||||
impl Evented for WaylandConnection {
|
||||
@ -60,6 +64,22 @@ impl WaylandConnection {
|
||||
pub fn create_new() -> Fallible<Self> {
|
||||
let (display, mut event_q) = Display::connect_to_env()?;
|
||||
let environment = Environment::from_display(&*display, &mut event_q)?;
|
||||
|
||||
let seat = environment
|
||||
.manager
|
||||
.instantiate_range(1, 6, move |seat| {
|
||||
seat.implement_closure(
|
||||
move |event, _seat| {
|
||||
if let SeatEvent::Name { name } = event {
|
||||
log::error!("seat name is {}", name);
|
||||
}
|
||||
},
|
||||
(),
|
||||
)
|
||||
})
|
||||
.map_err(|_| failure::format_err!("Failed to create seat"))?;
|
||||
let keyboard = KeyboardDispatcher::register(&seat)?;
|
||||
|
||||
Ok(Self {
|
||||
display: RefCell::new(display),
|
||||
event_q: RefCell::new(event_q),
|
||||
@ -69,6 +89,8 @@ impl WaylandConnection {
|
||||
tasks: Default::default(),
|
||||
next_window_id: AtomicUsize::new(1),
|
||||
windows: RefCell::new(HashMap::new()),
|
||||
seat,
|
||||
keyboard,
|
||||
})
|
||||
}
|
||||
|
||||
|
155
window/src/os/wayland/keyboard.rs
Normal file
155
window/src/os/wayland/keyboard.rs
Normal file
@ -0,0 +1,155 @@
|
||||
use crate::input::*;
|
||||
use crate::os::wayland::connection::WaylandConnection;
|
||||
use failure::Fallible;
|
||||
use smithay_client_toolkit as toolkit;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use toolkit::keyboard::{
|
||||
map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind, KeyState,
|
||||
ModifiersState,
|
||||
};
|
||||
use wayland_client::protocol::wl_seat::WlSeat;
|
||||
use wayland_client::protocol::wl_surface::WlSurface;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Inner {
|
||||
active_surface_id: u32,
|
||||
surface_to_window_id: HashMap<u32, usize>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn handle_event(&mut self, evt: KbEvent) {
|
||||
if let KbEvent::Enter { surface, .. } = &evt {
|
||||
self.active_surface_id = surface.as_ref().id();
|
||||
}
|
||||
|
||||
if let Some(event) = KeyboardEvent::from_event(evt) {
|
||||
self.dispatch_to_window(event);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_repeat(&mut self, rawkey: u32, keysym: u32, utf8: Option<String>) {
|
||||
self.dispatch_to_window(KeyboardEvent::Key {
|
||||
serial: 0,
|
||||
rawkey,
|
||||
keysym,
|
||||
is_down: true,
|
||||
utf8,
|
||||
});
|
||||
}
|
||||
|
||||
fn dispatch_to_window(&mut self, evt: KeyboardEvent) {
|
||||
if let Some(window_id) = self.surface_to_window_id.get(&self.active_surface_id) {
|
||||
let mut evt = Some(evt);
|
||||
WaylandConnection::with_window_inner(*window_id, move |inner| {
|
||||
inner.handle_keyboard_event(evt.take().unwrap());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KeyboardDispatcher {
|
||||
inner: Arc<Mutex<Inner>>,
|
||||
}
|
||||
|
||||
impl KeyboardDispatcher {
|
||||
pub fn register(seat: &WlSeat) -> Fallible<Self> {
|
||||
let inner = Arc::new(Mutex::new(Inner::default()));
|
||||
|
||||
map_keyboard_auto_with_repeat(
|
||||
&seat,
|
||||
KeyRepeatKind::System,
|
||||
{
|
||||
let inner = Arc::clone(&inner);
|
||||
move |evt: KbEvent, _| {
|
||||
inner.lock().unwrap().handle_event(evt);
|
||||
}
|
||||
},
|
||||
{
|
||||
let inner = Arc::clone(&inner);
|
||||
move |evt: KeyRepeatEvent, _| {
|
||||
inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.handle_repeat(evt.rawkey, evt.keysym, evt.utf8);
|
||||
}
|
||||
},
|
||||
)
|
||||
.map_err(|_| failure::format_err!("Failed to configure keyboard callback"))?;
|
||||
|
||||
Ok(Self { inner })
|
||||
}
|
||||
|
||||
pub fn add_window(&self, window_id: usize, surface: &WlSurface) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner
|
||||
.surface_to_window_id
|
||||
.insert(surface.as_ref().id(), window_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum KeyboardEvent {
|
||||
Enter {
|
||||
serial: u32,
|
||||
},
|
||||
Leave {
|
||||
serial: u32,
|
||||
},
|
||||
Key {
|
||||
rawkey: u32,
|
||||
keysym: u32,
|
||||
is_down: bool,
|
||||
serial: u32,
|
||||
utf8: Option<String>,
|
||||
},
|
||||
Modifiers {
|
||||
modifiers: Modifiers,
|
||||
},
|
||||
}
|
||||
|
||||
impl KeyboardEvent {
|
||||
fn from_event(evt: KbEvent) -> Option<Self> {
|
||||
Some(match evt {
|
||||
KbEvent::Enter { serial, .. } => KeyboardEvent::Enter { serial },
|
||||
KbEvent::Leave { serial, .. } => KeyboardEvent::Leave { serial },
|
||||
KbEvent::Key {
|
||||
rawkey,
|
||||
keysym,
|
||||
state,
|
||||
serial,
|
||||
utf8,
|
||||
..
|
||||
} => KeyboardEvent::Key {
|
||||
rawkey,
|
||||
keysym,
|
||||
is_down: state == KeyState::Pressed,
|
||||
serial,
|
||||
utf8,
|
||||
},
|
||||
KbEvent::Modifiers { modifiers } => KeyboardEvent::Modifiers {
|
||||
modifiers: modifier_keys(modifiers),
|
||||
},
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn modifier_keys(state: ModifiersState) -> Modifiers {
|
||||
let mut mods = Modifiers::NONE;
|
||||
if state.ctrl {
|
||||
mods |= Modifiers::CTRL;
|
||||
}
|
||||
if state.alt {
|
||||
mods |= Modifiers::ALT;
|
||||
}
|
||||
if state.shift {
|
||||
mods |= Modifiers::SHIFT;
|
||||
}
|
||||
if state.logo {
|
||||
mods |= Modifiers::SUPER;
|
||||
}
|
||||
mods
|
||||
}
|
@ -4,3 +4,4 @@ pub mod connection;
|
||||
pub mod window;
|
||||
pub use connection::*;
|
||||
pub use window::*;
|
||||
mod keyboard;
|
||||
|
@ -1,3 +1,4 @@
|
||||
use super::keyboard::KeyboardEvent;
|
||||
use crate::bitmaps::BitmapImage;
|
||||
use crate::color::Color;
|
||||
use crate::connection::ConnectionOps;
|
||||
@ -18,10 +19,6 @@ use std::io::{Read, Write};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use toolkit::keyboard::{
|
||||
map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind, KeyState,
|
||||
ModifiersState,
|
||||
};
|
||||
use toolkit::reexports::client::protocol::wl_data_device::{
|
||||
Event as DataDeviceEvent, WlDataDevice,
|
||||
};
|
||||
@ -32,29 +29,12 @@ use toolkit::reexports::client::protocol::wl_data_source::{
|
||||
use toolkit::reexports::client::protocol::wl_pointer::{
|
||||
self, Axis, AxisSource, Event as PointerEvent,
|
||||
};
|
||||
use toolkit::reexports::client::protocol::wl_seat::{Event as SeatEvent, WlSeat};
|
||||
use toolkit::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use toolkit::utils::MemPool;
|
||||
use toolkit::window::Event;
|
||||
#[cfg(feature = "opengl")]
|
||||
use wayland_client::egl::{is_available as egl_is_available, WlEglSurface};
|
||||
|
||||
fn modifier_keys(state: ModifiersState) -> Modifiers {
|
||||
let mut mods = Modifiers::NONE;
|
||||
if state.ctrl {
|
||||
mods |= Modifiers::CTRL;
|
||||
}
|
||||
if state.alt {
|
||||
mods |= Modifiers::ALT;
|
||||
}
|
||||
if state.shift {
|
||||
mods |= Modifiers::SHIFT;
|
||||
}
|
||||
if state.logo {
|
||||
mods |= Modifiers::SUPER;
|
||||
}
|
||||
mods
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||
enum DebuggableButtonState {
|
||||
Released,
|
||||
@ -274,8 +254,6 @@ pub struct WaylandWindowInner {
|
||||
window_id: usize,
|
||||
callbacks: Box<dyn WindowCallbacks>,
|
||||
surface: WlSurface,
|
||||
#[allow(dead_code)]
|
||||
seat: WlSeat,
|
||||
copy_and_paste: Arc<Mutex<CopyAndPaste>>,
|
||||
window: Option<toolkit::window::Window<toolkit::window::ConceptFrame>>,
|
||||
pool: MemPool,
|
||||
@ -458,9 +436,18 @@ impl WaylandWindow {
|
||||
let surface = conn
|
||||
.environment
|
||||
.borrow_mut()
|
||||
.create_surface(|dpi, _surface| {
|
||||
println!("surface dpi changed to {}", dpi);
|
||||
.create_surface(|dpi, surface| {
|
||||
log::error!(
|
||||
"surface id={} dpi changed to {}",
|
||||
surface.as_ref().id(),
|
||||
dpi
|
||||
);
|
||||
});
|
||||
log::error!(
|
||||
"window_id {} made new surface id={}",
|
||||
window_id,
|
||||
surface.as_ref().id()
|
||||
);
|
||||
|
||||
let dimensions = (width as u32, height as u32);
|
||||
let pending_event = Arc::new(Mutex::new(PendingEvent::default()));
|
||||
@ -490,23 +477,8 @@ impl WaylandWindow {
|
||||
|
||||
let pool = MemPool::new(&conn.environment.borrow().shm, || {})?;
|
||||
|
||||
let seat = conn
|
||||
.environment
|
||||
.borrow()
|
||||
.manager
|
||||
.instantiate_range(1, 6, move |seat| {
|
||||
seat.implement_closure(
|
||||
move |event, _seat| {
|
||||
if let SeatEvent::Name { name } = event {
|
||||
log::error!("seat name is {}", name);
|
||||
}
|
||||
},
|
||||
(),
|
||||
)
|
||||
})
|
||||
.map_err(|_| failure::format_err!("Failed to create seat"))?;
|
||||
|
||||
window.new_seat(&seat);
|
||||
window.new_seat(&conn.seat);
|
||||
conn.keyboard.add_window(window_id, &surface);
|
||||
|
||||
let copy_and_paste = Arc::new(Mutex::new(CopyAndPaste {
|
||||
data_offer: None,
|
||||
@ -525,7 +497,7 @@ impl WaylandWindow {
|
||||
.environment
|
||||
.borrow()
|
||||
.data_device_manager
|
||||
.get_data_device(&seat, {
|
||||
.get_data_device(&conn.seat, {
|
||||
let copy_and_paste = Arc::clone(©_and_paste);
|
||||
move |device| {
|
||||
device.implement_closure(
|
||||
@ -571,81 +543,41 @@ impl WaylandWindow {
|
||||
.data_device
|
||||
.replace(data_device);
|
||||
|
||||
seat.get_pointer({
|
||||
let pending_mouse = Arc::clone(&pending_mouse);
|
||||
move |ptr| {
|
||||
ptr.implement_closure(
|
||||
{
|
||||
let pending_mouse = Arc::clone(&pending_mouse);
|
||||
move |evt, _| {
|
||||
let evt: SendablePointerEvent = evt.into();
|
||||
if pending_mouse.lock().unwrap().queue(evt) {
|
||||
WaylandConnection::with_window_inner(window_id, move |inner| {
|
||||
inner.dispatch_pending_mouse();
|
||||
Ok(())
|
||||
});
|
||||
conn.seat
|
||||
.get_pointer({
|
||||
let pending_mouse = Arc::clone(&pending_mouse);
|
||||
move |ptr| {
|
||||
ptr.implement_closure(
|
||||
{
|
||||
let pending_mouse = Arc::clone(&pending_mouse);
|
||||
move |evt, _| {
|
||||
if let PointerEvent::Enter { surface, .. } = &evt {
|
||||
log::error!(
|
||||
"window_id {} Pointer entering surface id {}",
|
||||
window_id,
|
||||
surface.as_ref().id()
|
||||
);
|
||||
}
|
||||
let evt: SendablePointerEvent = evt.into();
|
||||
if pending_mouse.lock().unwrap().queue(evt) {
|
||||
WaylandConnection::with_window_inner(window_id, move |inner| {
|
||||
inner.dispatch_pending_mouse();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(),
|
||||
)
|
||||
}
|
||||
})
|
||||
.map_err(|_| failure::format_err!("Failed to configure pointer callback"))?;
|
||||
|
||||
map_keyboard_auto_with_repeat(
|
||||
&seat,
|
||||
KeyRepeatKind::System,
|
||||
{
|
||||
let copy_and_paste = Arc::clone(©_and_paste);
|
||||
move |event: KbEvent, _| match event {
|
||||
KbEvent::Enter { serial, .. } => {
|
||||
copy_and_paste.lock().unwrap().update_last_serial(serial);
|
||||
}
|
||||
KbEvent::Key {
|
||||
rawkey,
|
||||
keysym,
|
||||
state,
|
||||
utf8,
|
||||
serial,
|
||||
..
|
||||
} => {
|
||||
WaylandConnection::with_window_inner(window_id, move |inner| {
|
||||
inner.handle_key(
|
||||
serial,
|
||||
state == KeyState::Pressed,
|
||||
rawkey,
|
||||
keysym,
|
||||
utf8.clone(),
|
||||
);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
KbEvent::Modifiers { modifiers } => {
|
||||
let mods = modifier_keys(modifiers);
|
||||
WaylandConnection::with_window_inner(window_id, move |inner| {
|
||||
inner.handle_modifiers(mods);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
(),
|
||||
)
|
||||
}
|
||||
},
|
||||
move |event: KeyRepeatEvent, _| {
|
||||
WaylandConnection::with_window_inner(window_id, move |inner| {
|
||||
inner.handle_key(0, true, event.rawkey, event.keysym, event.utf8.clone());
|
||||
Ok(())
|
||||
});
|
||||
},
|
||||
)
|
||||
.map_err(|_| failure::format_err!("Failed to configure keyboard callback"))?;
|
||||
})
|
||||
.map_err(|_| failure::format_err!("Failed to configure pointer callback"))?;
|
||||
|
||||
let inner = Rc::new(RefCell::new(WaylandWindowInner {
|
||||
copy_and_paste,
|
||||
window_id,
|
||||
callbacks,
|
||||
surface,
|
||||
seat,
|
||||
window: Some(window),
|
||||
pool,
|
||||
dimensions,
|
||||
@ -672,52 +604,55 @@ impl WaylandWindow {
|
||||
}
|
||||
|
||||
impl WaylandWindowInner {
|
||||
fn handle_key(
|
||||
&mut self,
|
||||
serial: u32,
|
||||
key_is_down: bool,
|
||||
rawkey: u32,
|
||||
keysym: u32,
|
||||
utf8: Option<String>,
|
||||
) {
|
||||
self.copy_and_paste
|
||||
.lock()
|
||||
.unwrap()
|
||||
.update_last_serial(serial);
|
||||
let raw_key = keysym_to_keycode(keysym);
|
||||
let (key, raw_key) = match utf8 {
|
||||
Some(text) if text.chars().count() == 1 => {
|
||||
(KeyCode::Char(text.chars().nth(0).unwrap()), raw_key)
|
||||
}
|
||||
Some(text) => (KeyCode::Composed(text), raw_key),
|
||||
None => match raw_key {
|
||||
Some(key) => (key, None),
|
||||
None => {
|
||||
println!("no mapping for keysym {:x} and rawkey {:x}", keysym, rawkey);
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
// Avoid redundant key == raw_key
|
||||
let (key, raw_key) = match (key, raw_key) {
|
||||
// Avoid eg: \x01 when we can use CTRL-A
|
||||
(KeyCode::Char(c), Some(raw)) if c.is_ascii_control() => (raw.clone(), None),
|
||||
(key, Some(raw)) if key == raw => (key, None),
|
||||
pair => pair,
|
||||
};
|
||||
let key_event = KeyEvent {
|
||||
key_is_down,
|
||||
key,
|
||||
raw_key,
|
||||
modifiers: self.modifiers,
|
||||
repeat_count: 1,
|
||||
};
|
||||
self.callbacks
|
||||
.key_event(&key_event, &Window::Wayland(WaylandWindow(self.window_id)));
|
||||
}
|
||||
pub(crate) fn handle_keyboard_event(&mut self, evt: KeyboardEvent) {
|
||||
log::error!("window: {} key: {:?}", self.window_id, evt);
|
||||
|
||||
fn handle_modifiers(&mut self, modifiers: Modifiers) {
|
||||
self.modifiers = modifiers;
|
||||
match evt {
|
||||
KeyboardEvent::Key {
|
||||
rawkey,
|
||||
keysym,
|
||||
is_down,
|
||||
utf8,
|
||||
serial,
|
||||
} => {
|
||||
self.copy_and_paste
|
||||
.lock()
|
||||
.unwrap()
|
||||
.update_last_serial(serial);
|
||||
let raw_key = keysym_to_keycode(keysym);
|
||||
let (key, raw_key) = match utf8 {
|
||||
Some(text) if text.chars().count() == 1 => {
|
||||
(KeyCode::Char(text.chars().nth(0).unwrap()), raw_key)
|
||||
}
|
||||
Some(text) => (KeyCode::Composed(text), raw_key),
|
||||
None => match raw_key {
|
||||
Some(key) => (key, None),
|
||||
None => {
|
||||
println!("no mapping for keysym {:x} and rawkey {:x}", keysym, rawkey);
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
// Avoid redundant key == raw_key
|
||||
let (key, raw_key) = match (key, raw_key) {
|
||||
// Avoid eg: \x01 when we can use CTRL-A
|
||||
(KeyCode::Char(c), Some(raw)) if c.is_ascii_control() => (raw.clone(), None),
|
||||
(key, Some(raw)) if key == raw => (key, None),
|
||||
pair => pair,
|
||||
};
|
||||
let key_event = KeyEvent {
|
||||
key_is_down: is_down,
|
||||
key,
|
||||
raw_key,
|
||||
modifiers: self.modifiers,
|
||||
repeat_count: 1,
|
||||
};
|
||||
self.callbacks
|
||||
.key_event(&key_event, &Window::Wayland(WaylandWindow(self.window_id)));
|
||||
}
|
||||
KeyboardEvent::Modifiers { modifiers } => self.modifiers = modifiers,
|
||||
KeyboardEvent::Enter { .. } | KeyboardEvent::Leave { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_pending_mouse(&mut self) {
|
||||
|
Loading…
Reference in New Issue
Block a user