Skip to content

Commit

Permalink
Update wayland keymap handling for protocol 7+.
Browse files Browse the repository at this point in the history
Newer wayland protocol expects us to mmap the keymap file
descriptor (and do so with certain mmap flags).

The current code tries to handle both normal files and pipes,
but neither will work in some cases.

This updates the code to use xkbcommon's new_from_fd,
which is what the toolkit uses, and mmap's the file.

This fixes #5393, which was caused by hanging when
trying to read the file as if it was a pipe.
  • Loading branch information
dberlin authored and wez committed May 10, 2024
1 parent 91a16e5 commit 5b0b657
Showing 1 changed file with 28 additions and 29 deletions.
57 changes: 28 additions & 29 deletions window/src/os/wayland/keyboard.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::borrow::BorrowMut;
use std::io::Read;
use std::os::fd::{AsRawFd, FromRawFd};
use std::os::unix::fs::FileExt;

use wayland_client::protocol::wl_keyboard::{Event as WlKeyboardEvent, KeymapFormat, WlKeyboard};
use wayland_client::{Dispatch, Proxy};
use xkbcommon::xkb;
use xkbcommon::xkb::CONTEXT_NO_FLAGS;

use crate::x11::KeyboardWithFallback;

Expand Down Expand Up @@ -59,37 +58,37 @@ impl Dispatch<WlKeyboard, KeyboardData> for WaylandState {
*state.key_repeat_delay.borrow_mut() = *delay;
}
WlKeyboardEvent::Keymap { format, fd, size } => {
let mut file = unsafe { std::fs::File::from_raw_fd(fd.as_raw_fd()) };
match format.into_result().unwrap() {
KeymapFormat::XkbV1 => {
let mut data = vec![0u8; *size as usize];
// If we weren't passed a pipe, be sure to explicitly
// read from the start of the file
match file.read_exact_at(&mut data, 0) {
Ok(_) => {}
Err(err) => {
// ideally: we check for:
// err.kind() == std::io::ErrorKind::NotSeekable
// but that is not yet in stable rust
if err.raw_os_error() == Some(libc::ESPIPE) {
// It's a pipe, which cannot be seeked, so we
// just try reading from the current pipe position
file.read(&mut data).expect("read from Keymap fd/pipe");
} else {
return Err(err).expect("read_exact_at from Keymap fd");
// In later protocol versions, the fd must be privately mmap'd.
// We let xkb handle this and then turn it back into a string.
#[allow(unused_unsafe)] // Upstream release will change this
match unsafe {
let context = xkb::Context::new(CONTEXT_NO_FLAGS);
let cloned_fd = fd.try_clone().expect("Couldn't clone owned fd");
xkb::Keymap::new_from_fd(
&context,
cloned_fd,
*size as usize,
xkb::KEYMAP_FORMAT_TEXT_V1,
xkb::COMPILE_NO_FLAGS,
)
} {
Ok(Some(keymap)) => {
let s = keymap.get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1);
match KeyboardWithFallback::new_from_string(s) {
Ok(k) => {
state.keyboard_mapper.replace(k);
}
Err(err) => {
log::error!("Error processing keymap change: {:#}", err);
}
}
}
}
// Dance around CString panicing on the NUL terminator
// in the xkbcommon crate
while let Some(0) = data.last() {
data.pop();
}
let s = String::from_utf8(data).expect("Failed to read string from data");
match KeyboardWithFallback::new_from_string(s) {
Ok(k) => {
state.keyboard_mapper.replace(k);
Ok(None) => {
log::error!("invalid keymap");
}

Err(err) => {
log::error!("Error processing keymap change: {:#}", err);
}
Expand Down

0 comments on commit 5b0b657

Please sign in to comment.