Memory-map the keymap FD on Wayland to fix EOF error (#278)

wlroots based compositors reuse the same FD for keymap, so after a first
read is done, the file is seeked to the end and the next read fails,
causing the following error:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err`
value: Error { kind: UnexpectedEof, message: "failed to fill whole buffer" }'

The Wayland documentation says the FD "can be memory-mapped to provide
a keyboard mapping description" and then "From version 7 onwards, the
fd must be mapped with MAP_PRIVATE by the recipient, as MAP_SHARED may
fail."

Although it is not very clear, it probably means that the FD must be
memory-mapped.
This commit is contained in:
Greg Depoire--Ferrer 2022-02-18 13:35:00 +00:00 committed by GitHub
parent 4962afd3f5
commit 344a1997c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -30,9 +30,9 @@ use xkb::keymap::Keymap;
use std::cell::RefCell; use std::cell::RefCell;
use std::ffi::c_void; use std::ffi::c_void;
use std::fs::File; use std::fs::File;
use std::io::{self, Read, Seek, SeekFrom, Write}; use std::io::{self, Seek, SeekFrom, Write};
use std::mem; use std::mem;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc; use std::rc::Rc;
use std::slice; use std::slice;
use std::sync::mpsc; use std::sync::mpsc;
@ -1073,20 +1073,29 @@ impl Window {
match keymap { match keymap {
KeymapFormat::XkbV1 => { KeymapFormat::XkbV1 => {
unsafe { unsafe {
// Read fd content into Vec // The file descriptor must be memory-mapped (with MAP_PRIVATE).
let mut file = File::from_raw_fd(fd); let addr = libc::mmap(
let mut v = Vec::with_capacity(len as usize); std::ptr::null_mut(),
v.set_len(len as usize); len as usize,
file.read_exact(&mut v)?; libc::PROT_READ,
libc::MAP_PRIVATE,
fd,
0,
);
if addr == libc::MAP_FAILED {
return Err(std::io::Error::last_os_error());
}
let ctx = xkbcommon_sys::xkb_context_new(0); let ctx = xkbcommon_sys::xkb_context_new(0);
let kb_map_ptr = xkbcommon_sys::xkb_keymap_new_from_string( let kb_map_ptr = xkbcommon_sys::xkb_keymap_new_from_string(
ctx, ctx,
v.as_ptr() as *const _ as *const std::os::raw::c_char, addr as *const i8,
xkbcommon_sys::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1, xkbcommon_sys::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1,
0, 0,
); );
libc::munmap(addr, len as usize);
// Wrap keymap // Wrap keymap
Ok(Keymap::from_ptr(kb_map_ptr as *mut _ as *mut c_void)) Ok(Keymap::from_ptr(kb_map_ptr as *mut _ as *mut c_void))
} }