Bump wayland-rs to 0.28, key repeat

This commit is contained in:
Kenny Levinsen
2021-02-14 16:12:03 +01:00
parent ee2646f091
commit 07d8066d90
12 changed files with 1615 additions and 450 deletions
+168 -140
View File
@@ -3,21 +3,21 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use chrono::Local;
use chrono::{Duration, Local, NaiveDateTime};
use smithay_client_toolkit::keyboard::{
keysyms, map_keyboard_auto, Event as KbEvent, KeyState, ModifiersState,
use crate::keyboard::{
keysyms, map_keyboard, Event as KbEvent, KeyState, ModifiersState,
};
use wayland_client::protocol::{wl_compositor, wl_output, wl_pointer, wl_shm, wl_surface};
use wayland_client::{Display, EventQueue, GlobalEvent, GlobalManager, NewProxy};
use wayland_client::protocol::{wl_compositor, wl_output, wl_pointer, wl_shm, wl_surface, wl_seat};
use wayland_client::{Display, EventQueue, GlobalEvent, GlobalManager, Main};
use wayland_protocols::wlr::unstable::layer_shell::v1::client::{
zwlr_layer_shell_v1, zwlr_layer_surface_v1,
};
use crate::buffer::Buffer;
use crate::color::Color;
use crate::widget::{DrawContext, Widget};
use crate::widget::{WaitContext, DrawContext, Widget};
use crate::cmd::Cmd;
use crate::doublemempool::DoubleMemPool;
@@ -29,12 +29,12 @@ pub enum OutputMode {
}
struct AppInner {
compositor: Option<wl_compositor::WlCompositor>,
surfaces: Vec<wl_surface::WlSurface>,
shell_surfaces: Vec<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>,
compositor: Option<Main<wl_compositor::WlCompositor>>,
surfaces: Vec<Main<wl_surface::WlSurface>>,
shell_surfaces: Vec<Main<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>>,
configured_surfaces: Arc<Mutex<usize>>,
outputs: Vec<(u32, wl_output::WlOutput)>,
shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
outputs: Vec<(u32, Main<wl_output::WlOutput>)>,
shell: Option<Main<zwlr_layer_shell_v1::ZwlrLayerShellV1>>,
draw_tx: Sender<Cmd>,
output_mode: OutputMode,
visible: bool,
@@ -65,12 +65,11 @@ impl AppInner {
tx: Sender<Cmd>,
output: Option<&wl_output::WlOutput>,
) -> (
wl_surface::WlSurface,
zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
Main<wl_surface::WlSurface>,
Main<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>,
) {
let surface = compositor
.create_surface(NewProxy::implement_dummy)
.unwrap();
.create_surface();
let this_is_stupid = AtomicBool::new(false);
@@ -79,24 +78,18 @@ impl AppInner {
&surface,
output,
zwlr_layer_shell_v1::Layer::Overlay,
"".to_string(),
move |layer| {
layer.implement_closure(
move |evt, layer| match evt {
zwlr_layer_surface_v1::Event::Configure { serial, .. } => {
if !this_is_stupid.compare_and_swap(false, true, Ordering::SeqCst) {
*(configured_surfaces.lock().unwrap()) += 1;
layer.ack_configure(serial);
tx.send(Cmd::ForceDraw).unwrap();
}
}
_ => unreachable!(),
},
(),
)
},
)
.unwrap();
"".to_string());
shell_surface.quick_assign(move |layer, event, _| match event {
zwlr_layer_surface_v1::Event::Configure { serial, .. } => {
if !this_is_stupid.compare_and_swap(false, true, Ordering::SeqCst) {
*(configured_surfaces.lock().unwrap()) += 1;
layer.ack_configure(serial);
tx.send(Cmd::ForceDraw).unwrap();
}
}
_ => unreachable!(),
},
);
shell_surface.set_keyboard_interactivity(1);
shell_surface.set_size(1, 1);
@@ -169,7 +162,7 @@ impl AppInner {
self.draw_tx.send(Cmd::ForceDraw).unwrap();
}
fn add_output(&mut self, id: u32, output: wl_output::WlOutput) {
fn add_output(&mut self, id: u32, output: Main<wl_output::WlOutput>) {
self.outputs.push((id, output));
self.outputs_changed();
}
@@ -191,15 +184,22 @@ impl AppInner {
}
}
fn set_compositor(&mut self, compositor: Option<wl_compositor::WlCompositor>) {
fn set_compositor(&mut self, compositor: Option<Main<wl_compositor::WlCompositor>>) {
self.compositor = compositor
}
fn set_shell(&mut self, shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>) {
fn set_shell(&mut self, shell: Option<Main<zwlr_layer_shell_v1::ZwlrLayerShellV1>>) {
self.shell = shell
}
}
struct AppKeyboard {
current: Option<Cmd>,
delay: i32,
rate: i32,
next: Option<NaiveDateTime>,
}
pub struct App<'a> {
pools: DoubleMemPool,
display: Display,
@@ -210,6 +210,7 @@ pub struct App<'a> {
inner: Arc<Mutex<AppInner>>,
last_damage: Option<Vec<(i32, i32, i32, i32)>>,
last_dim: (u32, u32),
keyboard: Arc<Mutex<AppKeyboard>>,
}
impl<'a> App<'a> {
@@ -355,6 +356,26 @@ impl<'a> App<'a> {
self.redraw(true)
}
pub fn set_keyboard_repeat(&mut self, ctx: &mut WaitContext) {
let kbd = self.keyboard.lock().unwrap();
if let Some(t) = kbd.next {
ctx.set_time(t);
}
}
pub fn key_repeat(&mut self) -> Option<Cmd> {
let time = Local::now().naive_local();
let mut kbd = self.keyboard.lock().unwrap();
if let Some(target) = kbd.next {
if time >= target {
let cmd = kbd.current.as_ref().unwrap().clone();
kbd.next = Some(time + Duration::milliseconds(kbd.rate.into()));
return Some(cmd);
}
}
None
}
pub fn new(tx: Sender<Cmd>, output_mode: OutputMode, bg: Color, scale: u32) -> App<'a> {
let inner = Arc::new(Mutex::new(AppInner::new(tx, output_mode, scale)));
@@ -364,19 +385,16 @@ impl<'a> App<'a> {
let cmd_queue = Arc::new(Mutex::new(VecDeque::new()));
let (display, mut event_queue) = Display::connect_to_env().unwrap();
let display_wrapper = display
.as_ref()
.make_wrapper(&event_queue.get_token())
.unwrap();
let display = Display::connect_to_env().unwrap();
let mut event_queue = display.create_event_queue();
let display_wrapper = (*display).clone().attach(event_queue.token());
//
// Set up global manager
//
let inner_global = inner.clone();
let manager =
GlobalManager::new_with_cb(&display_wrapper, move |event, registry| match event {
GlobalManager::new_with_cb(&display_wrapper, move |event, registry, _| match event {
GlobalEvent::New {
id,
ref interface,
@@ -384,10 +402,7 @@ impl<'a> App<'a> {
} => {
if let "wl_output" = &interface[..] {
let output = registry
.bind(version, id, move |output| {
output.implement_closure(move |_, _| {}, ())
})
.unwrap();
.bind(version, id);
inner_global.lock().unwrap().add_output(id, output);
}
}
@@ -400,39 +415,30 @@ impl<'a> App<'a> {
// double sync to retrieve the global list
// and the globals metadata
event_queue.sync_roundtrip().unwrap();
event_queue.sync_roundtrip().unwrap();
event_queue.sync_roundtrip(&mut(), |_, _, _|{}).unwrap();
event_queue.sync_roundtrip(&mut(), |_, _, _|{}).unwrap();
// wl_compositor
let compositor: wl_compositor::WlCompositor = manager
.instantiate_range(1, 4, NewProxy::implement_dummy)
let compositor = manager
.instantiate_range(1, 4)
.expect("server didn't advertise `wl_compositor`");
inner.lock().unwrap().set_compositor(Some(compositor));
// wl_shm
let shm_formats = Arc::new(Mutex::new(Vec::new()));
let shm = manager
.instantiate_range(1, 1, |shm| {
shm.implement_closure(
move |evt, _| {
if let wl_shm::Event::Format { format } = evt {
shm_formats.lock().unwrap().push(format);
}
},
(),
)
})
let shm: Main<wl_shm::WlShm> = manager
.instantiate_range(1, 1)
.expect("server didn't advertise `wl_shm`");
let pools = DoubleMemPool::new(&shm).expect("Failed to create a memory pool !");
let pools = DoubleMemPool::new(shm).expect("Failed to create a memory pool !");
//
// Get our seat
//
let seat = manager
.instantiate_range(1, 6, NewProxy::implement_dummy)
let seat: Main<wl_seat::WlSeat> = manager
.instantiate_range(1, 6)
.unwrap();
event_queue.sync_roundtrip(&mut(), |_, _, _|{}).unwrap();
//
// Keyboard processing
@@ -446,7 +452,17 @@ impl<'a> App<'a> {
logo: false,
num_lock: false,
}));
map_keyboard_auto(&seat, move |event: KbEvent, _| match event {
let keyboard = Arc::new(Mutex::new(AppKeyboard{
current: None,
delay: 0,
rate: 0,
next: None,
}));
let kb2 = keyboard.clone();
map_keyboard(&seat, None, move |event: KbEvent, _, _| match event {
KbEvent::Key {
keysym,
utf8,
@@ -459,101 +475,112 @@ impl<'a> App<'a> {
keysyms::XKB_KEY_c if modifiers_state.lock().unwrap().ctrl => {
kbd_clone.lock().unwrap().push_back(Cmd::Exit)
}
v => kbd_clone.lock().unwrap().push_back(Cmd::Keyboard {
key: v,
key_state: state,
modifiers_state: *modifiers_state.lock().unwrap(),
interpreted: utf8,
}),
v => {
let ev = Cmd::Keyboard {
key: v,
key_state: state,
modifiers_state: *modifiers_state.lock().unwrap(),
interpreted: utf8,
};
let mut kbd = kb2.lock().unwrap();
kbd.current = Some(ev.clone());
kbd.next = if kbd.delay > 0 {
Some(Local::now().naive_local() + Duration::milliseconds(kbd.delay.into()))
} else {
None
};
drop(kbd);
kbd_clone.lock().unwrap().push_back(ev);
}
}
} else {
kb2.lock().unwrap().next = None;
}
},
KbEvent::Leave { .. } => {
kb2.lock().unwrap().next = None;
},
KbEvent::RepeatInfo { delay, rate } => {
let mut kbd = kb2.lock().unwrap();
kbd.delay = delay;
kbd.rate = rate;
}
KbEvent::Modifiers { modifiers } => *modifiers_state.lock().unwrap() = modifiers,
_ => (),
})
.expect("Failed to map keyboard");
.expect("could not map keyboard");
//
// Prepare shell so that we can create our shell surface
//
inner.lock().unwrap().set_shell(Some(
if let Ok(layer) = manager.instantiate_exact(
1,
|layer: NewProxy<zwlr_layer_shell_v1::ZwlrLayerShellV1>| {
layer.implement_closure(|_, _| {}, ())
},
) {
if let Ok(layer) = manager.instantiate_exact(1) {
layer
} else {
panic!("server didn't advertise `zwlr_layer_shell_v1`");
},
));
event_queue.sync_roundtrip().unwrap();
event_queue.sync_roundtrip(&mut(), |_, _, _|{}).unwrap();
//
// Cursor processing
//
let pointer_clone = cmd_queue.clone();
seat.get_pointer(move |ptr| {
let mut pos: (u32, u32) = (0, 0);
let mut vert_scroll: f64 = 0.0;
let mut horiz_scroll: f64 = 0.0;
let mut btn: u32 = 0;
let mut btn_clicked = false;
ptr.implement_closure(
move |evt, _| match evt {
wl_pointer::Event::Enter {
surface_x,
surface_y,
..
} => {
pos = (surface_x as u32, surface_y as u32);
}
wl_pointer::Event::Leave { .. } => {
pos = (0, 0);
}
wl_pointer::Event::Motion {
surface_x,
surface_y,
..
} => {
pos = (surface_x as u32 * scale, surface_y as u32 * scale);
}
wl_pointer::Event::Axis { axis, value, .. } => {
if axis == wl_pointer::Axis::VerticalScroll {
vert_scroll += value;
}
}
wl_pointer::Event::Button { button, state, .. } => {
if let wl_pointer::ButtonState::Released = state {
btn = button;
btn_clicked = true;
}
}
wl_pointer::Event::Frame => {
if vert_scroll != 0.0 || horiz_scroll != 0.0 {
pointer_clone.lock().unwrap().push_back(Cmd::MouseScroll {
scroll: (horiz_scroll, vert_scroll),
pos,
});
vert_scroll = 0.0;
horiz_scroll = 0.0;
}
if btn_clicked {
pointer_clone
.lock()
.unwrap()
.push_back(Cmd::MouseClick { btn, pos });
btn_clicked = false;
}
}
_ => {}
},
(),
)
})
.unwrap();
let pointer = seat.get_pointer();
let mut pos: (u32, u32) = (0, 0);
let mut vert_scroll: f64 = 0.0;
let mut horiz_scroll: f64 = 0.0;
let mut btn: u32 = 0;
let mut btn_clicked = false;
pointer.quick_assign(move |_, event, _| match event {
wl_pointer::Event::Enter {
surface_x,
surface_y,
..
} => {
pos = (surface_x as u32, surface_y as u32);
}
wl_pointer::Event::Leave { .. } => {
pos = (0, 0);
}
wl_pointer::Event::Motion {
surface_x,
surface_y,
..
} => {
pos = (surface_x as u32 * scale, surface_y as u32 * scale);
}
wl_pointer::Event::Axis { axis, value, .. } => {
if axis == wl_pointer::Axis::VerticalScroll {
vert_scroll += value;
}
}
wl_pointer::Event::Button { button, state, .. } => {
if let wl_pointer::ButtonState::Released = state {
btn = button;
btn_clicked = true;
}
}
wl_pointer::Event::Frame => {
if vert_scroll != 0.0 || horiz_scroll != 0.0 {
pointer_clone.lock().unwrap().push_back(Cmd::MouseScroll {
scroll: (horiz_scroll, vert_scroll),
pos,
});
vert_scroll = 0.0;
horiz_scroll = 0.0;
}
if btn_clicked {
pointer_clone
.lock()
.unwrap()
.push_back(Cmd::MouseClick { btn, pos });
btn_clicked = false;
}
}
_ => {}
});
display.flush().unwrap();
@@ -567,6 +594,7 @@ impl<'a> App<'a> {
inner,
last_damage: None,
last_dim: (0, 0),
keyboard,
}
}
}
+3 -1
View File
@@ -1,5 +1,6 @@
use smithay_client_toolkit::keyboard::{KeyState, ModifiersState};
use crate::keyboard::{KeyState, ModifiersState};
#[derive(Clone)]
pub enum Cmd {
Exit,
Draw,
@@ -13,6 +14,7 @@ pub enum Cmd {
scroll: (f64, f64),
pos: (u32, u32),
},
KeyboardTest,
Keyboard {
key: u32,
key_state: KeyState,
+233 -5
View File
@@ -1,5 +1,233 @@
use smithay_client_toolkit::utils::MemPool;
use wayland_client::protocol::wl_shm;
use std::{
cell::RefCell,
ffi::CStr,
fs::File,
io,
os::unix::io::{FromRawFd, RawFd},
rc::Rc,
time::SystemTime,
time::UNIX_EPOCH,
};
#[cfg(target_os = "linux")]
use nix::sys::memfd;
use nix::{
errno::Errno,
fcntl,
sys::{mman, stat},
unistd,
};
use memmap::MmapMut;
use wayland_client::{
protocol::{wl_buffer, wl_shm, wl_shm_pool},
Main,
};
/// A wrapper handling an SHM memory pool backed by a shared memory file
///
/// This wrapper handles for you the creation of the shared memory file and its synchronization
/// with the protocol.
///
/// Mempool internally tracks the release of the buffers by the compositor. As such, creating a buffer
/// that is not commited to a surface (and then never released by the server) would cause the Mempool
/// to be stuck believing it is still in use.
///
/// Mempool will also handle the destruction of buffers and as such the `destroy()` method should not
/// be used on buffers created from Mempool.
///
/// Overwriting the contents of the memory pool before it is completely freed may cause graphical
/// glitches due to the possible corruption of data while the compositor is reading it.
///
/// Mempool requires a callback that will be called when the pool becomes free, this
/// happens when all the pools buffers are released by the server.
pub struct MemPool {
file: File,
len: usize,
pool: Main<wl_shm_pool::WlShmPool>,
buffer_count: Rc<RefCell<u32>>,
mmap: MmapMut,
callback: Rc<RefCell<dyn FnMut(wayland_client::DispatchData)>>,
}
impl MemPool {
/// Create a new memory pool associated with given shm
pub fn new<F>(shm: Main<wl_shm::WlShm>, callback: F) -> io::Result<MemPool>
where
F: FnMut(wayland_client::DispatchData) + 'static,
{
let mem_fd = create_shm_fd()?;
let mem_file = unsafe { File::from_raw_fd(mem_fd) };
mem_file.set_len(128)?;
let pool = shm.create_pool(mem_fd, 128);
let mmap = unsafe { MmapMut::map_mut(&mem_file).unwrap() };
Ok(MemPool {
file: mem_file,
len: 128,
pool,
buffer_count: Rc::new(RefCell::new(0)),
mmap,
callback: Rc::new(RefCell::new(callback)),
})
}
/// Resize the memory pool
///
/// This affect the size as it is seen by the wayland server. Even
/// if you extend the temporary file size by writing to it, you need to
/// call this method otherwise the server won't see the new size.
///
/// Memory pools can only be extented, as such this method will do nothing
/// if the requested new size is smaller than the current size.
///
/// This method allows you to ensure the underlying pool is large enough to
/// hold what you want to write to it.
pub fn resize(&mut self, newsize: usize) -> io::Result<()> {
if newsize > self.len {
self.file.set_len(newsize as u64)?;
self.pool.resize(newsize as i32);
self.len = newsize;
self.mmap = unsafe { MmapMut::map_mut(&self.file).unwrap() };
}
Ok(())
}
/// Create a new buffer to this pool
///
/// The parameters are:
///
/// - `offset`: the offset (in bytes) from the beginning of the pool at which this
/// buffer starts
/// - `width`: the width of this buffer (in pixels)
/// - `height`: the height of this buffer (in pixels)
/// - `stride`: distance (in bytes) between the beginning of a row and the next one
/// - `format`: the encoding format of the pixels. Using a format that was not
/// advertised to the `wl_shm` global by the server is a protocol error and will
/// terminate your connection
pub fn buffer(
&self,
offset: i32,
width: i32,
height: i32,
stride: i32,
format: wl_shm::Format,
) -> wl_buffer::WlBuffer {
*self.buffer_count.borrow_mut() += 1;
let my_buffer_count = self.buffer_count.clone();
let my_callback = self.callback.clone();
let buffer = self
.pool
.create_buffer(offset, width, height, stride, format);
buffer.quick_assign(move |buffer, event, dispatch_data| match event {
wl_buffer::Event::Release => {
buffer.destroy();
let new_count = {
// borrow the buffer_count for as short as possible, in case
// the user wants to create a new buffer from the callback
let mut my_buffer_count = my_buffer_count.borrow_mut();
*my_buffer_count -= 1;
*my_buffer_count
};
if new_count == 0 {
(&mut *my_callback.borrow_mut())(dispatch_data);
}
}
_ => unreachable!(),
});
(*buffer).clone().detach()
}
/// Uses the memmap crate to map the underlying shared memory file
pub fn mmap(&mut self) -> &mut MmapMut {
&mut self.mmap
}
/// Returns true if the pool contains buffers that are currently in use by the server
pub fn is_used(&self) -> bool {
*self.buffer_count.borrow() != 0
}
}
impl Drop for MemPool {
fn drop(&mut self) {
self.pool.destroy();
}
}
impl io::Write for MemPool {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
io::Write::write(&mut self.file, buf)
}
fn flush(&mut self) -> io::Result<()> {
io::Write::flush(&mut self.file)
}
}
impl io::Seek for MemPool {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
io::Seek::seek(&mut self.file, pos)
}
}
fn create_shm_fd() -> io::Result<RawFd> {
// Only try memfd on linux
#[cfg(target_os = "linux")]
loop {
match memfd::memfd_create(
CStr::from_bytes_with_nul(b"smithay-client-toolkit\0").unwrap(),
memfd::MemFdCreateFlag::MFD_CLOEXEC,
) {
Ok(fd) => return Ok(fd),
Err(nix::Error::Sys(Errno::EINTR)) => continue,
Err(nix::Error::Sys(Errno::ENOSYS)) => break,
Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)),
Err(err) => unreachable!(err),
}
}
// Fallback to using shm_open
let sys_time = SystemTime::now();
let mut mem_file_handle = format!(
"/smithay-client-toolkit-{}",
sys_time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
);
loop {
match mman::shm_open(
mem_file_handle.as_str(),
fcntl::OFlag::O_CREAT
| fcntl::OFlag::O_EXCL
| fcntl::OFlag::O_RDWR
| fcntl::OFlag::O_CLOEXEC,
stat::Mode::S_IRUSR | stat::Mode::S_IWUSR,
) {
Ok(fd) => match mman::shm_unlink(mem_file_handle.as_str()) {
Ok(_) => return Ok(fd),
Err(nix::Error::Sys(errno)) => match unistd::close(fd) {
Ok(_) => return Err(io::Error::from(errno)),
Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)),
Err(err) => panic!(err),
},
Err(err) => panic!(err),
},
Err(nix::Error::Sys(Errno::EEXIST)) => {
// If a file with that handle exists then change the handle
mem_file_handle = format!(
"/smithay-client-toolkit-{}",
sys_time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
);
continue;
}
Err(nix::Error::Sys(Errno::EINTR)) => continue,
Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)),
Err(err) => unreachable!(err),
}
}
}
pub struct DoubleMemPool {
pool1: MemPool,
@@ -8,10 +236,10 @@ pub struct DoubleMemPool {
}
impl DoubleMemPool {
pub fn new(shm: &wl_shm::WlShm) -> ::std::io::Result<DoubleMemPool> {
pub fn new(shm: Main<wl_shm::WlShm>) -> ::std::io::Result<DoubleMemPool> {
Ok(DoubleMemPool {
pool1: MemPool::new(shm, move || {})?,
pool2: MemPool::new(shm, move || {})?,
pool1: MemPool::new(shm.clone(), move |_| {})?,
pool2: MemPool::new(shm.clone(), move |_| {})?,
switch: false,
})
}
+267
View File
@@ -0,0 +1,267 @@
#![allow(dead_code, non_camel_case_types, clippy::identity_op)]
#![cfg_attr(rustfmt, rustfmt_skip)]
use std::os::raw::{c_char, c_int, c_void, c_uint};
pub const XKB_MOD_NAME_SHIFT : &[u8] = b"Shift\0";
pub const XKB_MOD_NAME_CAPS : &[u8] = b"Lock\0";
pub const XKB_MOD_NAME_CTRL : &[u8] = b"Control\0";
pub const XKB_MOD_NAME_ALT : &[u8] = b"Mod1\0";
pub const XKB_MOD_NAME_NUM : &[u8] = b"Mod2\0";
pub const XKB_MOD_NAME_LOGO : &[u8] = b"Mod4\0";
pub const XKB_LED_NAME_CAPS : &[u8] = b"Caps Lock\0";
pub const XKB_LED_NAME_NUM : &[u8] = b"Num Lock\0";
pub const XKB_LED_NAME_SCROLL : &[u8] = b"Scroll Lock\0";
pub struct xkb_context;
pub struct xkb_keymap;
pub struct xkb_state;
pub struct xkb_compose_table;
pub struct xkb_compose_state;
pub type xkb_keycode_t = u32;
pub type xkb_keysym_t = u32;
pub type xkb_layout_index_t = u32;
pub type xkb_layout_mask_t = u32;
pub type xkb_level_index_t = u32;
pub type xkb_mod_index_t = u32;
pub type xkb_mod_mask_t = u32;
pub type xkb_led_index_t = u32;
pub type xkb_led_mask_t = u32;
pub const XKB_KEYCODE_INVALID :u32 = 0xffff_ffff;
pub const XKB_LAYOUT_INVALID :u32 = 0xffff_ffff;
pub const XKB_LEVEL_INVALID :u32 = 0xffff_ffff;
pub const XKB_MOD_INVALID :u32 = 0xffff_ffff;
pub const XKB_LED_INVALID :u32 = 0xffff_ffff;
pub const XKB_KEYCODE_MAX :u32 = 0xffff_fffe;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct xkb_rule_names {
pub rules: *const c_char,
pub model: *const c_char ,
pub layout: *const c_char,
pub variant: *const c_char,
pub options: *const c_char,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_keysym_flags {
/** Do not apply any flags. */
XKB_KEYSYM_NO_FLAGS = 0,
/** Find keysym by case-insensitive search. */
XKB_KEYSYM_CASE_INSENSITIVE = (1 << 0)
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_context_flags {
/** Do not apply any context flags. */
XKB_CONTEXT_NO_FLAGS = 0,
/** Create this context with an empty include path. */
XKB_CONTEXT_NO_DEFAULT_INCLUDES = (1 << 0),
/**
* Don't take RMLVO names from the environment.
* @since 0.3.0
*/
XKB_CONTEXT_NO_ENVIRONMENT_NAMES = (1 << 1)
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_log_level {
/** Log critical internal errors only. */
XKB_LOG_LEVEL_CRITICAL = 10,
/** Log all errors. */
XKB_LOG_LEVEL_ERROR = 20,
/** Log warnings and errors. */
XKB_LOG_LEVEL_WARNING = 30,
/** Log information, warnings, and errors. */
XKB_LOG_LEVEL_INFO = 40,
/** Log everything. */
XKB_LOG_LEVEL_DEBUG = 50
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_keymap_compile_flags {
/** Do not apply any flags. */
XKB_KEYMAP_COMPILE_NO_FLAGS = 0,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_keymap_format {
/** Cannot be used for creation */
XKB_KEYMAP_USE_ORIGINAL_FORMAT = 0,
/** The current/classic XKB text format, as generated by xkbcomp -xkb. */
XKB_KEYMAP_FORMAT_TEXT_V1 = 1,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_key_direction {
/** The key was released. */
XKB_KEY_UP,
/** The key was pressed. */
XKB_KEY_DOWN
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_compose_compile_flags {
XKB_COMPOSE_COMPILE_NO_FLAGS = 0
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_compose_format {
XKB_COMPOSE_FORMAT_TEXT_V1 = 1
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_compose_state_flags {
XKB_COMPOSE_STATE_NO_FLAGS = 0
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_compose_status {
XKB_COMPOSE_NOTHING,
XKB_COMPOSE_COMPOSING,
XKB_COMPOSE_COMPOSED,
XKB_COMPOSE_CANCELLED
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum xkb_compose_feed_result {
XKB_COMPOSE_FEED_IGNORED,
XKB_COMPOSE_FEED_ACCEPTED
}
bitflags::bitflags!(
pub struct xkb_state_component: u32 {
/** Depressed modifiers, i.e. a key is physically holding them. */
const XKB_STATE_MODS_DEPRESSED = (1 << 0);
/** Latched modifiers, i.e. will be unset after the next non-modifier
* key press. */
const XKB_STATE_MODS_LATCHED = (1 << 1);
/** Locked modifiers, i.e. will be unset after the key provoking the
* lock has been pressed again. */
const XKB_STATE_MODS_LOCKED = (1 << 2);
/** Effective modifiers, i.e. currently active and affect key
* processing (derived from the other state components).
* Use this unless you explictly care how the state came about. */
const XKB_STATE_MODS_EFFECTIVE = (1 << 3);
/** Depressed layout, i.e. a key is physically holding it. */
const XKB_STATE_LAYOUT_DEPRESSED = (1 << 4);
/** Latched layout, i.e. will be unset after the next non-modifier
* key press. */
const XKB_STATE_LAYOUT_LATCHED = (1 << 5);
/** Locked layout, i.e. will be unset after the key provoking the lock
* has been pressed again. */
const XKB_STATE_LAYOUT_LOCKED = (1 << 6);
/** Effective layout, i.e. currently active and affects key processing
* (derived from the other state components).
* Use this unless you explictly care how the state came about. */
const XKB_STATE_LAYOUT_EFFECTIVE = (1 << 7);
/** LEDs (derived from the other state components). */
const XKB_STATE_LEDS = (1 << 8);
}
);
dlopen_external_library!(XkbCommon,
functions:
fn xkb_keysym_get_name(xkb_keysym_t, *mut c_char, usize) -> c_int,
fn xkb_keysym_from_name(*const c_char, xkb_keysym_flags) -> xkb_keysym_t,
fn xkb_keysym_to_utf8(xkb_keysym_t, *mut c_char, usize) -> c_int,
fn xkb_keysym_to_utf32(xkb_keysym_t) -> u32,
fn xkb_context_new(xkb_context_flags) -> *mut xkb_context,
fn xkb_context_ref(*mut xkb_context) -> *mut xkb_context,
fn xkb_context_unref(*mut xkb_context) -> (),
fn xkb_context_set_user_data(*mut xkb_context, *mut c_void) -> (),
fn xkb_context_get_user_data(*mut xkb_context) -> *mut c_void,
fn xkb_context_include_path_append(*mut xkb_context, *const c_char) -> c_int,
fn xkb_context_include_path_append_default(*mut xkb_context) -> c_int,
fn xkb_context_include_path_reset_defaults(*mut xkb_context) -> c_int,
fn xkb_context_include_path_clear(*mut xkb_context) -> (),
fn xkb_context_num_include_paths(*mut xkb_context) -> c_uint,
fn xkb_context_include_path_get(*mut xkb_context, c_uint) -> *const c_char,
fn xkb_context_set_log_level(*mut xkb_context, xkb_log_level) -> (),
fn xkb_context_get_log_level(*mut xkb_context) -> xkb_log_level,
fn xkb_context_set_log_verbosity(*mut xkb_context, c_int) -> (),
fn xkb_context_get_log_verbosity(*mut xkb_context) -> c_int,
fn xkb_keymap_new_from_names(*mut xkb_context,
*const xkb_rule_names,
xkb_keymap_compile_flags
) -> *mut xkb_keymap,
fn xkb_keymap_new_from_string(*mut xkb_context,
*const c_char,
xkb_keymap_format,
xkb_keymap_compile_flags
) -> *mut xkb_keymap,
fn xkb_keymap_new_from_buffer(*mut xkb_context,
*const c_char,
usize,
xkb_keymap_format,
xkb_keymap_compile_flags
) -> *mut xkb_keymap,
fn xkb_keymap_ref(*mut xkb_keymap) -> *mut xkb_keymap,
fn xkb_keymap_unref(*mut xkb_keymap) -> (),
fn xkb_keymap_get_as_string(*mut xkb_keymap, xkb_keymap_format) -> *const c_char,
fn xkb_keymap_key_repeats(*mut xkb_keymap, xkb_keycode_t) -> c_int,
fn xkb_state_new(*mut xkb_keymap) -> *mut xkb_state,
fn xkb_state_ref(*mut xkb_state) -> *mut xkb_state,
fn xkb_state_unref(*mut xkb_state) -> (),
fn xkb_state_update_mask(*mut xkb_state,
xkb_mod_mask_t,
xkb_mod_mask_t,
xkb_mod_mask_t,
xkb_layout_index_t,
xkb_layout_index_t,
xkb_layout_index_t
) -> xkb_state_component,
fn xkb_state_update_key(*mut xkb_state,
xkb_keycode_t,
xkb_key_direction
) -> xkb_state_component,
fn xkb_state_key_get_syms(*mut xkb_state,
xkb_keycode_t,
*const *mut xkb_keysym_t
) -> c_int,
fn xkb_state_key_get_utf8(*mut xkb_state,
xkb_keycode_t,
*mut c_char,
usize
) -> c_int,
fn xkb_state_key_get_utf32(*mut xkb_state, xkb_keycode_t) -> u32,
fn xkb_state_key_get_one_sym(*mut xkb_state, xkb_keycode_t) -> xkb_keysym_t,
fn xkb_state_mod_name_is_active(*mut xkb_state, *const c_char, xkb_state_component) -> c_int,
fn xkb_compose_table_new_from_locale(*mut xkb_context, *const c_char, xkb_compose_compile_flags) -> *mut xkb_compose_table,
fn xkb_compose_table_unref(*mut xkb_compose_table) -> (),
fn xkb_compose_state_new(*mut xkb_compose_table, xkb_compose_state_flags) -> *mut xkb_compose_state,
fn xkb_compose_state_unref(*mut xkb_compose_state) -> (),
fn xkb_compose_state_feed(*mut xkb_compose_state, xkb_keysym_t) -> xkb_compose_feed_result,
fn xkb_compose_state_reset(*mut xkb_compose_state) -> (),
fn xkb_compose_state_get_status(*mut xkb_compose_state) -> xkb_compose_status,
fn xkb_compose_state_get_utf8(*mut xkb_compose_state, *mut c_char, usize) -> c_int,
fn xkb_compose_state_get_one_sym(*mut xkb_compose_state) -> xkb_keysym_t,
);
lazy_static::lazy_static!(
pub static ref XKBCOMMON_OPTION: Option<XkbCommon> = {
XkbCommon::open("libxkbcommon.so.0")
.or_else(|_| XkbCommon::open("libxkbcommon.so"))
.ok()
};
pub static ref XKBCOMMON_HANDLE: &'static XkbCommon = {
XKBCOMMON_OPTION.as_ref().expect("Library libxkbcommon.so could not be loaded.")
};
);
+17
View File
@@ -0,0 +1,17 @@
#![allow(missing_docs, non_upper_case_globals, unused_parens, clippy::all)]
pub const XKB_KEY_BackSpace :u32 = 0xff08; /* Back space, back char */
pub const XKB_KEY_Tab :u32 = 0xff09;
pub const XKB_KEY_Return :u32 = 0xff0d; /* Return, enter */
pub const XKB_KEY_Escape :u32 = 0xff1b;
pub const XKB_KEY_Delete :u32 = 0xffff; /* Delete, rubout */
pub const XKB_KEY_Home :u32 = 0xff50;
pub const XKB_KEY_Left :u32 = 0xff51; /* Move left, left arrow */
pub const XKB_KEY_Right :u32 = 0xff53; /* Move right, right arrow */
pub const XKB_KEY_End :u32 = 0xff57; /* EOL */
pub const XKB_KEY_ISO_Left_Tab :u32 = 0xfe20;
pub const XKB_KEY_e :u32 = 0x0065; /* U+0065 LATIN SMALL LETTER E */
pub const XKB_KEY_a :u32 = 0x0061; /* U+0061 LATIN SMALL LETTER A */
pub const XKB_KEY_c :u32 = 0x0063; /* U+0063 LATIN SMALL LETTER C */
pub const XKB_KEY_u :u32 = 0x0075; /* U+0075 LATIN SMALL LETTER U */
+338
View File
@@ -0,0 +1,338 @@
//! Utilities for keymap interpretation of keyboard input
//!
//! This module provides an implementation for `wl_keyboard`
//! objects using `libxkbcommon` to interpret the keyboard input
//! given the user keymap.
//!
//! The entry point of this module is the [`map_keyboard`](fn.map_keyboard.html)
//! function which, given a `wl_seat` and a callback, setup keymap interpretation
//! and key repetition for the `wl_keyboard` of this seat.
//!
//! Key repetition relies on an event source, that needs to be inserted in your
//! calloop event loop. Not doing so will prevent key repetition to work
//! (but the rest of the functionnality will not be affected).
use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc};
use byteorder::{ByteOrder, NativeEndian};
pub use wayland_client::protocol::wl_keyboard::KeyState;
use wayland_client::{
protocol::{wl_keyboard, wl_seat, wl_surface},
Attached,
};
mod ffi;
mod state;
pub mod keysyms;
use self::state::KbState;
pub use self::state::{ModifiersState, RMLVO};
#[derive(Debug)]
/// An error that occurred while trying to initialize a mapped keyboard
pub enum Error {
/// libxkbcommon is not available
XKBNotFound,
/// Provided RMLVO specified a keymap that would not be loaded
BadNames,
}
/// Events received from a mapped keyboard
pub enum Event<'a> {
/// The keyboard focus has entered a surface
Enter {
/// serial number of the event
serial: u32,
/// surface that was entered
surface: wl_surface::WlSurface,
/// raw values of the currently pressed keys
rawkeys: &'a [u32],
/// interpreted symbols of the currently pressed keys
keysyms: &'a [u32],
},
/// The keyboard focus has left a surface
Leave {
/// serial number of the event
serial: u32,
/// surface that was left
surface: wl_surface::WlSurface,
},
/// The key modifiers have changed state
Modifiers {
/// current state of the modifiers
modifiers: ModifiersState,
},
/// A key event occurred
Key {
/// serial number of the event
serial: u32,
/// time at which the keypress occurred
time: u32,
/// raw value of the key
rawkey: u32,
/// interpreted symbol of the key
keysym: u32,
/// new state of the key
state: KeyState,
/// utf8 interpretation of the entered text
///
/// will always be `None` on key release events
utf8: Option<String>,
repeats: bool,
},
RepeatInfo {
rate: i32,
delay: i32,
},
}
/// Implement a keyboard for keymap translation with key repetition
///
/// This requires you to provide a callback to receive the events after they
/// have been interpreted with the keymap.
///
/// The keymap will be loaded from the provided RMLVO rules, or from the compositor
/// provided keymap if `None`.
///
/// Returns an error if xkbcommon could not be initialized, the RMLVO specification
/// contained invalid values, or if the provided seat does not have keyboard capability.
///
/// **Note:** This adapter does not handle key repetition. See `map_keyboard_repeat` for that.
pub fn map_keyboard<F>(
seat: &Attached<wl_seat::WlSeat>,
rmlvo: Option<RMLVO>,
callback: F,
) -> Result<wl_keyboard::WlKeyboard, Error>
where
F: FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>) + 'static,
{
let keyboard = seat.get_keyboard();
let state = Rc::new(RefCell::new(
rmlvo
.map(KbState::from_rmlvo)
.unwrap_or_else(KbState::new)?,
));
let callback = Rc::new(RefCell::new(callback));
// prepare the handler
let mut kbd_handler = KbdHandler {
callback,
state,
};
keyboard.quick_assign(move |keyboard, event, data| {
kbd_handler.event(keyboard.detach(), event, data)
});
Ok(keyboard.detach())
}
/*
* Classic handling
*/
type KbdCallback = dyn FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>);
struct KbdHandler {
state: Rc<RefCell<KbState>>,
callback: Rc<RefCell<KbdCallback>>,
}
impl KbdHandler {
fn event(
&mut self,
kbd: wl_keyboard::WlKeyboard,
event: wl_keyboard::Event,
dispatch_data: wayland_client::DispatchData,
) {
use wl_keyboard::Event;
match event {
Event::Keymap { format, fd, size } => self.keymap(kbd, format, fd, size),
Event::Enter {
serial,
surface,
keys,
} => self.enter(kbd, serial, surface, keys, dispatch_data),
Event::Leave { serial, surface } => self.leave(kbd, serial, surface, dispatch_data),
Event::Key {
serial,
time,
key,
state,
} => self.key(kbd, serial, time, key, state, dispatch_data),
Event::Modifiers {
mods_depressed,
mods_latched,
mods_locked,
group,
..
} => self.modifiers(
kbd,
mods_depressed,
mods_latched,
mods_locked,
group,
dispatch_data,
),
Event::RepeatInfo { rate, delay } => self.repeat_info(kbd, rate, delay, dispatch_data),
_ => {}
}
}
fn keymap(
&mut self,
_: wl_keyboard::WlKeyboard,
format: wl_keyboard::KeymapFormat,
fd: RawFd,
size: u32,
) {
let mut state = self.state.borrow_mut();
if state.locked() {
// state is locked, ignore keymap updates
return;
}
if state.ready() {
// new keymap, we first deinit to free resources
unsafe {
state.de_init();
}
}
match format {
wl_keyboard::KeymapFormat::XkbV1 => unsafe {
state.init_with_fd(fd, size as usize);
},
wl_keyboard::KeymapFormat::NoKeymap => {
// TODO: how to handle this (hopefully never occuring) case?
}
_ => unreachable!(),
}
}
fn enter(
&mut self,
object: wl_keyboard::WlKeyboard,
serial: u32,
surface: wl_surface::WlSurface,
keys: Vec<u8>,
dispatch_data: wayland_client::DispatchData,
) {
let mut state = self.state.borrow_mut();
let rawkeys = keys
.chunks_exact(4)
.map(NativeEndian::read_u32)
.collect::<Vec<_>>();
let keys: Vec<u32> = rawkeys.iter().map(|k| state.get_one_sym_raw(*k)).collect();
(&mut *self.callback.borrow_mut())(
Event::Enter {
serial,
surface,
rawkeys: &rawkeys,
keysyms: &keys,
},
object,
dispatch_data,
);
}
fn leave(
&mut self,
object: wl_keyboard::WlKeyboard,
serial: u32,
surface: wl_surface::WlSurface,
dispatch_data: wayland_client::DispatchData,
) {
(&mut *self.callback.borrow_mut())(Event::Leave { serial, surface }, object, dispatch_data);
}
fn key(
&mut self,
object: wl_keyboard::WlKeyboard,
serial: u32,
time: u32,
key: u32,
key_state: wl_keyboard::KeyState,
dispatch_data: wayland_client::DispatchData,
) {
let (sym, utf8, repeats) = {
let mut state = self.state.borrow_mut();
// Get the values to generate a key event
let sym = state.get_one_sym_raw(key);
let utf8 = if key_state == wl_keyboard::KeyState::Pressed {
match state.compose_feed(sym) {
Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => {
if let Some(status) = state.compose_status() {
match status {
ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => {
state.compose_get_utf8()
}
ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => {
state.get_utf8_raw(key)
}
_ => None,
}
} else {
state.get_utf8_raw(key)
}
}
Some(_) => {
// XKB_COMPOSE_FEED_IGNORED
None
}
None => {
// XKB COMPOSE is not initialized
state.get_utf8_raw(key)
}
}
} else {
None
};
let repeats = unsafe { state.key_repeats(key + 8) };
(sym, utf8, repeats)
};
(&mut *self.callback.borrow_mut())(
Event::Key {
serial,
time,
rawkey: key,
keysym: sym,
state: key_state,
utf8,
repeats,
},
object,
dispatch_data,
);
}
fn modifiers(
&mut self,
object: wl_keyboard::WlKeyboard,
mods_depressed: u32,
mods_latched: u32,
mods_locked: u32,
group: u32,
dispatch_data: wayland_client::DispatchData,
) {
{
let mut state = self.state.borrow_mut();
state.update_modifiers(mods_depressed, mods_latched, mods_locked, group);
(&mut *self.callback.borrow_mut())(
Event::Modifiers {
modifiers: state.mods_state(),
},
object,
dispatch_data,
);
}
}
fn repeat_info(&mut self, object: wl_keyboard::WlKeyboard, rate: i32, delay: i32, dispatch_data: wayland_client::DispatchData) {
(&mut *self.callback.borrow_mut())(Event::RepeatInfo { rate, delay }, object, dispatch_data);
}
}
+401
View File
@@ -0,0 +1,401 @@
use std::{
env,
ffi::CString,
fs::File,
os::raw::c_char,
os::unix::{
ffi::OsStringExt,
io::{FromRawFd, RawFd},
},
ptr,
};
use memmap::MmapOptions;
use super::ffi::{self, xkb_state_component, XKBCOMMON_HANDLE as XKBH};
use super::Error;
pub struct KbState {
xkb_context: *mut ffi::xkb_context,
xkb_keymap: *mut ffi::xkb_keymap,
xkb_state: *mut ffi::xkb_state,
xkb_compose_table: *mut ffi::xkb_compose_table,
xkb_compose_state: *mut ffi::xkb_compose_state,
mods_state: ModifiersState,
locked: bool,
}
/// The RMLVO description of a keymap
///
/// All fields are optional, and the system default
/// will be used if set to `None`.
pub struct RMLVO {
/// The rules file to use
pub rules: Option<String>,
/// The keyboard model by which to interpret keycodes and LEDs
pub model: Option<String>,
/// A comma separated list of layouts (languages) to include in the keymap
pub layout: Option<String>,
/// A comma separated list of variants, one per layout, which may modify or
/// augment the respective layout in various ways
pub variant: Option<String>,
/// A comma separated list of options, through which the user specifies
/// non-layout related preferences, like which key combinations are
/// used for switching layouts, or which key is the Compose key.
pub options: Option<String>,
}
/// Represents the current state of the keyboard modifiers
///
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
///
/// For some modifiers, this means that the key is currently pressed, others are toggled
/// (like caps lock).
#[derive(Copy, Clone, Debug, Default)]
pub struct ModifiersState {
/// The "control" key
pub ctrl: bool,
/// The "alt" key
pub alt: bool,
/// The "shift" key
pub shift: bool,
/// The "Caps lock" key
pub caps_lock: bool,
/// The "logo" key
///
/// Also known as the "windows" key on most keyboards
pub logo: bool,
/// The "Num lock" key
pub num_lock: bool,
}
impl ModifiersState {
fn new() -> ModifiersState {
ModifiersState::default()
}
fn update_with(&mut self, state: *mut ffi::xkb_state) {
self.ctrl = unsafe {
(XKBH.xkb_state_mod_name_is_active)(
state,
ffi::XKB_MOD_NAME_CTRL.as_ptr() as *const c_char,
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
) > 0
};
self.alt = unsafe {
(XKBH.xkb_state_mod_name_is_active)(
state,
ffi::XKB_MOD_NAME_ALT.as_ptr() as *const c_char,
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
) > 0
};
self.shift = unsafe {
(XKBH.xkb_state_mod_name_is_active)(
state,
ffi::XKB_MOD_NAME_SHIFT.as_ptr() as *const c_char,
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
) > 0
};
self.caps_lock = unsafe {
(XKBH.xkb_state_mod_name_is_active)(
state,
ffi::XKB_MOD_NAME_CAPS.as_ptr() as *const c_char,
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
) > 0
};
self.logo = unsafe {
(XKBH.xkb_state_mod_name_is_active)(
state,
ffi::XKB_MOD_NAME_LOGO.as_ptr() as *const c_char,
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
) > 0
};
self.num_lock = unsafe {
(XKBH.xkb_state_mod_name_is_active)(
state,
ffi::XKB_MOD_NAME_NUM.as_ptr() as *const c_char,
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
) > 0
};
}
}
impl KbState {
pub(crate) fn update_modifiers(
&mut self,
mods_depressed: u32,
mods_latched: u32,
mods_locked: u32,
group: u32,
) {
if !self.ready() {
return;
}
let mask = unsafe {
(XKBH.xkb_state_update_mask)(
self.xkb_state,
mods_depressed,
mods_latched,
mods_locked,
0,
0,
group,
)
};
if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) {
// effective value of mods have changed, we need to update our state
self.mods_state.update_with(self.xkb_state);
}
}
pub(crate) fn get_one_sym_raw(&mut self, keycode: u32) -> u32 {
if !self.ready() {
return 0;
}
unsafe { (XKBH.xkb_state_key_get_one_sym)(self.xkb_state, keycode + 8) }
}
pub(crate) fn get_utf8_raw(&mut self, keycode: u32) -> Option<String> {
if !self.ready() {
return None;
}
let size = unsafe {
(XKBH.xkb_state_key_get_utf8)(self.xkb_state, keycode + 8, ptr::null_mut(), 0)
} + 1;
if size <= 1 {
return None;
};
let mut buffer = Vec::with_capacity(size as usize);
unsafe {
buffer.set_len(size as usize);
(XKBH.xkb_state_key_get_utf8)(
self.xkb_state,
keycode + 8,
buffer.as_mut_ptr() as *mut _,
size as usize,
);
};
// remove the final `\0`
buffer.pop();
// libxkbcommon will always provide valid UTF8
Some(unsafe { String::from_utf8_unchecked(buffer) })
}
pub(crate) fn compose_feed(&mut self, keysym: u32) -> Option<ffi::xkb_compose_feed_result> {
if !self.ready() || self.xkb_compose_state.is_null() {
return None;
}
Some(unsafe { (XKBH.xkb_compose_state_feed)(self.xkb_compose_state, keysym) })
}
pub(crate) fn compose_status(&mut self) -> Option<ffi::xkb_compose_status> {
if !self.ready() || self.xkb_compose_state.is_null() {
return None;
}
Some(unsafe { (XKBH.xkb_compose_state_get_status)(self.xkb_compose_state) })
}
pub(crate) fn compose_get_utf8(&mut self) -> Option<String> {
if !self.ready() || self.xkb_compose_state.is_null() {
return None;
}
let size = unsafe {
(XKBH.xkb_compose_state_get_utf8)(self.xkb_compose_state, ptr::null_mut(), 0)
} + 1;
if size <= 1 {
return None;
};
let mut buffer = Vec::with_capacity(size as usize);
unsafe {
buffer.set_len(size as usize);
(XKBH.xkb_compose_state_get_utf8)(
self.xkb_compose_state,
buffer.as_mut_ptr() as *mut _,
size as usize,
);
};
// remove the final `\0`
buffer.pop();
// libxkbcommon will always provide valid UTF8
Some(unsafe { String::from_utf8_unchecked(buffer) })
}
pub(crate) fn new() -> Result<KbState, Error> {
let xkbh = match ffi::XKBCOMMON_OPTION.as_ref() {
Some(h) => h,
None => return Err(Error::XKBNotFound),
};
let xkb_context =
unsafe { (xkbh.xkb_context_new)(ffi::xkb_context_flags::XKB_CONTEXT_NO_FLAGS) };
if xkb_context.is_null() {
return Err(Error::XKBNotFound);
}
let mut me = KbState {
xkb_context,
xkb_keymap: ptr::null_mut(),
xkb_state: ptr::null_mut(),
xkb_compose_table: ptr::null_mut(),
xkb_compose_state: ptr::null_mut(),
mods_state: ModifiersState::new(),
locked: false,
};
unsafe {
me.init_compose();
}
Ok(me)
}
pub(crate) fn from_rmlvo(rmlvo: RMLVO) -> Result<KbState, Error> {
fn to_cstring(s: Option<String>) -> Result<Option<CString>, Error> {
s.map_or(Ok(None), |s| CString::new(s).map(Option::Some))
.map_err(|_| Error::BadNames)
}
let mut state = KbState::new()?;
let rules = to_cstring(rmlvo.rules)?;
let model = to_cstring(rmlvo.model)?;
let layout = to_cstring(rmlvo.layout)?;
let variant = to_cstring(rmlvo.variant)?;
let options = to_cstring(rmlvo.options)?;
let xkb_names = ffi::xkb_rule_names {
rules: rules.map_or(ptr::null(), |s| s.as_ptr()),
model: model.map_or(ptr::null(), |s| s.as_ptr()),
layout: layout.map_or(ptr::null(), |s| s.as_ptr()),
variant: variant.map_or(ptr::null(), |s| s.as_ptr()),
options: options.map_or(ptr::null(), |s| s.as_ptr()),
};
unsafe {
state.init_with_rmlvo(xkb_names)?;
}
state.locked = true;
Ok(state)
}
pub(crate) unsafe fn init_compose(&mut self) {
let locale = env::var_os("LC_ALL")
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.or_else(|| env::var_os("LC_CTYPE"))
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.or_else(|| env::var_os("LANG"))
.and_then(|v| if v.is_empty() { None } else { Some(v) })
.unwrap_or_else(|| "C".into());
let locale = CString::new(locale.into_vec()).unwrap();
let compose_table = (XKBH.xkb_compose_table_new_from_locale)(
self.xkb_context,
locale.as_ptr(),
ffi::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
);
if compose_table.is_null() {
// init of compose table failed, continue without compose
return;
}
let compose_state = (XKBH.xkb_compose_state_new)(
compose_table,
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
);
if compose_state.is_null() {
// init of compose state failed, continue without compose
(XKBH.xkb_compose_table_unref)(compose_table);
return;
}
self.xkb_compose_table = compose_table;
self.xkb_compose_state = compose_state;
}
pub(crate) unsafe fn post_init(&mut self, xkb_keymap: *mut ffi::xkb_keymap) {
let xkb_state = (XKBH.xkb_state_new)(xkb_keymap);
self.xkb_keymap = xkb_keymap;
self.xkb_state = xkb_state;
self.mods_state.update_with(xkb_state);
}
pub(crate) unsafe fn de_init(&mut self) {
(XKBH.xkb_state_unref)(self.xkb_state);
self.xkb_state = ptr::null_mut();
(XKBH.xkb_keymap_unref)(self.xkb_keymap);
self.xkb_keymap = ptr::null_mut();
}
pub(crate) unsafe fn init_with_fd(&mut self, fd: RawFd, size: usize) {
let map = MmapOptions::new()
.len(size)
.map(&File::from_raw_fd(fd))
.unwrap();
let xkb_keymap = (XKBH.xkb_keymap_new_from_string)(
self.xkb_context,
map.as_ptr() as *const _,
ffi::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1,
ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
);
if xkb_keymap.is_null() {
panic!("Received invalid keymap from compositor.");
}
self.post_init(xkb_keymap);
}
pub(crate) unsafe fn init_with_rmlvo(
&mut self,
names: ffi::xkb_rule_names,
) -> Result<(), Error> {
let xkb_keymap = (XKBH.xkb_keymap_new_from_names)(
self.xkb_context,
&names,
ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
);
if xkb_keymap.is_null() {
return Err(Error::BadNames);
}
self.post_init(xkb_keymap);
Ok(())
}
pub(crate) unsafe fn key_repeats(&mut self, xkb_keycode_t: ffi::xkb_keycode_t) -> bool {
(XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, xkb_keycode_t) == 1
}
#[inline]
pub(crate) fn ready(&self) -> bool {
!self.xkb_state.is_null()
}
#[inline]
pub(crate) fn locked(&self) -> bool {
self.locked
}
#[inline]
pub(crate) fn mods_state(&self) -> ModifiersState {
self.mods_state
}
}
impl Drop for KbState {
fn drop(&mut self) {
unsafe {
(XKBH.xkb_compose_state_unref)(self.xkb_compose_state);
(XKBH.xkb_compose_table_unref)(self.xkb_compose_table);
(XKBH.xkb_state_unref)(self.xkb_state);
(XKBH.xkb_keymap_unref)(self.xkb_keymap);
(XKBH.xkb_context_unref)(self.xkb_context);
}
}
}
+15 -3
View File
@@ -12,6 +12,7 @@ use timerfd::{SetTimeFlags, TimerFd, TimerState};
mod app;
mod buffer;
mod keyboard;
mod cmd;
mod color;
mod config;
@@ -23,6 +24,9 @@ mod fonts;
mod widget;
mod widgets;
#[macro_use]
extern crate dlib;
use app::{App, OutputMode};
use cmd::Cmd;
use config::Config;
@@ -202,7 +206,7 @@ fn main() {
});
let mut timer = TimerFd::new().unwrap();
let ev_fd = PollFd::new(app.event_queue().get_connection_fd(), PollFlags::POLLIN);
let ev_fd = PollFd::new(app.event_queue().display().get_connection_fd(), PollFlags::POLLIN);
let rx_fd = PollFd::new(rx_pipe.as_raw_fd(), PollFlags::POLLIN);
let tm_fd = PollFd::new(timer.as_raw_fd(), PollFlags::POLLIN);
let ipc_fd = PollFd::new(listener.as_raw_fd(), PollFlags::POLLIN);
@@ -224,6 +228,11 @@ fn main() {
app.redraw(false).expect("Failed to draw");
app.flush_display();
}
Cmd::KeyboardTest => {
if let Some(cmd) = app.key_repeat() {
q.lock().unwrap().push_back(cmd);
}
}
Cmd::ForceDraw => {
app.redraw(true).expect("Failed to draw");
app.flush_display();
@@ -279,6 +288,7 @@ fn main() {
wait_ctx.target_time = None;
app.get_widget().wait(&mut wait_ctx);
app.set_keyboard_repeat(&mut wait_ctx);
if let Some(target_time) = wait_ctx.target_time {
let n = Local::now().naive_local();
@@ -313,7 +323,7 @@ fn main() {
}
app.event_queue()
.dispatch_pending()
.dispatch_pending(&mut (), |_, _, _|{})
.expect("Failed to dispatch all messages.");
}
@@ -365,7 +375,9 @@ fn main() {
.contains(PollFlags::POLLIN)
{
timer.read();
q.lock().unwrap().push_back(Cmd::Draw);
let mut qq = q.lock().unwrap();
qq.push_back(Cmd::KeyboardTest);
qq.push_back(Cmd::Draw);
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ use crate::buffer::Buffer;
use crate::color::Color;
use chrono::NaiveDateTime;
use nix::poll::PollFd;
pub use smithay_client_toolkit::keyboard::{KeyState, ModifiersState};
pub use crate::keyboard::{KeyState, ModifiersState};
pub struct DrawContext<'a> {
pub buf: &'a mut Buffer<'a>,
+1 -1
View File
@@ -15,7 +15,7 @@ use std::process::Command;
use std::sync::mpsc::Sender;
use fuzzy_matcher::skim::{fuzzy_indices, fuzzy_match};
use smithay_client_toolkit::keyboard::keysyms;
use crate::keyboard::keysyms;
use unicode_segmentation::UnicodeSegmentation;
pub struct Launcher<'a> {