Bump wayland-rs to 0.28, key repeat
This commit is contained in:
+168
-140
@@ -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
@@ -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
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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.")
|
||||
};
|
||||
);
|
||||
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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>,
|
||||
|
||||
@@ -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> {
|
||||
|
||||
Reference in New Issue
Block a user