1
1
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:
Wez Furlong 2019-11-30 10:00:34 -08:00
parent 2d6d54bfab
commit 29a6c62b6c
5 changed files with 270 additions and 156 deletions

View File

@ -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()

View File

@ -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,
})
}

View 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
}

View File

@ -4,3 +4,4 @@ pub mod connection;
pub mod window;
pub use connection::*;
pub use window::*;
mod keyboard;

View File

@ -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(&copy_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(&copy_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) {