diff --git a/iced_layershell/Cargo.toml b/iced_layershell/Cargo.toml index b2c584b..25cec98 100644 --- a/iced_layershell/Cargo.toml +++ b/iced_layershell/Cargo.toml @@ -22,3 +22,7 @@ tracing = "0.1.40" thiserror = "1.0.56" layershellev.workspace = true futures = "0.3.30" + +memmap2 = "0.9.4" +once_cell = "1.19.0" +xkbcommon-dl = "0.4.2" diff --git a/layershellev/Cargo.toml b/layershellev/Cargo.toml index bdfc81e..4122dd9 100644 --- a/layershellev/Cargo.toml +++ b/layershellev/Cargo.toml @@ -31,3 +31,7 @@ rwh_06.workspace = true bitflags.workspace = true sctk.workspace = true + +memmap2 = "0.9.4" +once_cell = "1.19.0" +xkbcommon-dl = "0.4.2" diff --git a/layershellev/src/keyboard.rs b/layershellev/src/keyboard.rs new file mode 100644 index 0000000..083b4c0 --- /dev/null +++ b/layershellev/src/keyboard.rs @@ -0,0 +1,125 @@ +use std::{ffi::c_char, ops::Deref, os::fd::OwnedFd, ptr::NonNull}; + +use memmap2::MmapOptions; +use once_cell::sync::Lazy; + +use xkbcommon_dl::{self as xkb, xkbcommon_handle, XkbCommon}; + +use xkb::{ + xkb_context, xkb_context_flags, xkb_keymap, xkb_keymap_compile_flags, xkb_state, + xkb_state_component, +}; + +static XKBH: Lazy<&'static XkbCommon> = Lazy::new(xkbcommon_handle); + +#[derive(Debug)] +pub struct XkbKeymap { + keymap: NonNull, +} + +impl XkbKeymap { + pub fn from_fd(context: &XkbContext, fd: OwnedFd, size: usize) -> Option { + let map = MmapOptions::new().len(size).map_raw_read_only(&fd).ok()?; + let keymap = unsafe { + let keymap = (XKBH.xkb_keymap_new_from_string)( + (*context).as_ptr(), + map.as_ptr() as *const _, + xkb::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1, + xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, + ); + + NonNull::new(keymap)? + }; + Some(Self { keymap }) + } +} + +impl Drop for XkbKeymap { + fn drop(&mut self) { + unsafe { (XKBH.xkb_keymap_unref)(self.keymap.as_ptr()) } + } +} + +impl Deref for XkbKeymap { + type Target = NonNull; + fn deref(&self) -> &Self::Target { + &self.keymap + } +} + +#[derive(Debug)] +pub struct XkbContext { + context: NonNull, +} + +impl Drop for XkbContext { + fn drop(&mut self) { + unsafe { (XKBH.xkb_context_unref)(self.context.as_ptr()) } + } +} + +impl Deref for XkbContext { + type Target = NonNull; + fn deref(&self) -> &Self::Target { + &self.context + } +} + +impl XkbContext { + pub fn new() -> Self { + let context = unsafe { (XKBH.xkb_context_new)(xkb_context_flags::XKB_CONTEXT_NO_FLAGS) }; + let context = NonNull::new(context).unwrap(); + Self { context } + } +} + +#[derive(Debug)] +pub struct XkbState { + state: NonNull, + modifiers: ModifiersState, +} + +impl XkbState { + pub fn new_wayland(keymap: &XkbKeymap) -> Option { + let state = NonNull::new(unsafe { (XKBH.xkb_state_new)(keymap.as_ptr()) })?; + Some(Self::new_inner(state)) + } + + fn new_inner(state: NonNull) -> Self { + let modifiers = ModifiersState::default(); + let mut this = Self { state, modifiers }; + this.reload_modifiers(); + this + } + // NOTE: read here + /// Check if the modifier is active within xkb. + fn mod_name_is_active(&mut self, name: &[u8]) -> bool { + unsafe { + (XKBH.xkb_state_mod_name_is_active)( + self.state.as_ptr(), + name.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + } + } + fn reload_modifiers(&mut self) { + self.modifiers.ctrl = self.mod_name_is_active(xkb::XKB_MOD_NAME_CTRL); + self.modifiers.alt = self.mod_name_is_active(xkb::XKB_MOD_NAME_ALT); + self.modifiers.shift = self.mod_name_is_active(xkb::XKB_MOD_NAME_SHIFT); + self.modifiers.caps_lock = self.mod_name_is_active(xkb::XKB_MOD_NAME_CAPS); + println!("caps: {}", self.modifiers.caps_lock); + self.modifiers.logo = self.mod_name_is_active(xkb::XKB_MOD_NAME_LOGO); + self.modifiers.num_lock = self.mod_name_is_active(xkb::XKB_MOD_NAME_NUM); + } +} + +#[derive(Debug, Default)] +pub struct ModifiersState { + ctrl: bool, + alt: bool, + shift: bool, + caps_lock: bool, + logo: bool, + num_lock: bool, +} + diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index fa8d2b5..ee69167 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -160,6 +160,7 @@ //! mod events; +mod keyboard; mod strtoshape; use std::fmt::Debug; @@ -181,7 +182,7 @@ use wayland_client::{ wl_buffer::WlBuffer, wl_compositor::WlCompositor, wl_display::WlDisplay, - wl_keyboard::{self, WlKeyboard}, + wl_keyboard::{self, KeymapFormat, WlKeyboard}, wl_output::{self, WlOutput}, wl_pointer::{self, WlPointer}, wl_registry, @@ -823,9 +824,20 @@ impl Dispatch for WindowState { event: ::Event, _data: &(), _conn: &Connection, - _qhandle: &wayland_client::QueueHandle, + _qhandle: &QueueHandle, ) { + use keyboard::*; match event { + wl_keyboard::Event::Keymap { format, fd, size } => { + if !matches!(format, WEnum::Value(KeymapFormat::XkbV1)) { + return; + } + println!("it is {format:?}, {fd:?}, {size}"); + let context = XkbContext::new(); + let keymap = XkbKeymap::from_fd(&context, fd, size as usize).unwrap(); + let state = XkbState::new_wayland(&keymap).unwrap(); + println!("{state:?}"); + } wl_keyboard::Event::Key { state: keystate, serial, @@ -1641,11 +1653,9 @@ impl WindowState { WaylandSource::new(connection.clone(), event_queue) .insert(event_loop.handle()) - .unwrap(); + .expect("Failed to init wayland source"); 'out: loop { - event_loop - .dispatch(Duration::from_millis(1), &mut self) - .unwrap(); + event_loop.dispatch(Duration::from_millis(1), &mut self)?; let mut messages = Vec::new(); std::mem::swap(&mut messages, &mut self.message);