From 0e140f63860dba26a3d66edebc3a7cb0371a464f Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Wed, 19 Jun 2024 13:38:08 +0800 Subject: [PATCH 01/16] chore: impl xkb base --- iced_layershell/Cargo.toml | 4 ++ layershellev/Cargo.toml | 4 ++ layershellev/src/keyboard.rs | 125 +++++++++++++++++++++++++++++++++++ layershellev/src/lib.rs | 22 ++++-- 4 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 layershellev/src/keyboard.rs 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 b1edec3..2f56bae 100644 --- a/layershellev/Cargo.toml +++ b/layershellev/Cargo.toml @@ -33,3 +33,7 @@ bitflags.workspace = true sctk.workspace = true log.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 a50480b..d739d3a 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -159,6 +159,7 @@ //! mod events; +mod keyboard; mod strtoshape; use std::fmt::Debug; @@ -180,7 +181,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, @@ -822,9 +823,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, @@ -1736,11 +1748,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); From d1a0475b22d5db11bbd4102019d6e464faddc2f3 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Sun, 28 Jul 2024 15:25:51 +0859 Subject: [PATCH 02/16] chore: update lock --- Cargo.lock | 6 ++++++ layershellev/src/keyboard.rs | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index e2f6b4a..002b7ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1452,8 +1452,11 @@ dependencies = [ "iced_runtime", "iced_style", "layershellev", + "memmap2 0.9.4", + "once_cell", "thiserror", "tracing", + "xkbcommon-dl", ] [[package]] @@ -1738,6 +1741,8 @@ version = "0.2.7" dependencies = [ "bitflags 2.6.0", "log", + "memmap2 0.9.4", + "once_cell", "raw-window-handle", "smithay-client-toolkit 0.18.1", "tempfile", @@ -1748,6 +1753,7 @@ dependencies = [ "wayland-protocols 0.31.2", "wayland-protocols-misc", "wayland-protocols-wlr 0.2.0", + "xkbcommon-dl", ] [[package]] diff --git a/layershellev/src/keyboard.rs b/layershellev/src/keyboard.rs index 083b4c0..3d1d87c 100644 --- a/layershellev/src/keyboard.rs +++ b/layershellev/src/keyboard.rs @@ -122,4 +122,3 @@ pub struct ModifiersState { logo: bool, num_lock: bool, } - From 57caf011e49b15b4001dfb2819dae736d172a954 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Mon, 29 Jul 2024 22:35:38 +0859 Subject: [PATCH 03/16] chore: stell some code from winit --- Cargo.lock | 2 +- layershellev/Cargo.toml | 2 +- layershellev/src/keyboard.rs | 288 ++++++++++++++++++++++++++++++++++- layershellev/src/lib.rs | 34 +++-- 4 files changed, 305 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 002b7ae..e4b602d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1742,9 +1742,9 @@ dependencies = [ "bitflags 2.6.0", "log", "memmap2 0.9.4", - "once_cell", "raw-window-handle", "smithay-client-toolkit 0.18.1", + "smol_str", "tempfile", "thiserror", "wayland-backend", diff --git a/layershellev/Cargo.toml b/layershellev/Cargo.toml index 2f56bae..d9a9b06 100644 --- a/layershellev/Cargo.toml +++ b/layershellev/Cargo.toml @@ -35,5 +35,5 @@ sctk.workspace = true log.workspace = true memmap2 = "0.9.4" -once_cell = "1.19.0" xkbcommon-dl = "0.4.2" +smol_str = "0.2.2" diff --git a/layershellev/src/keyboard.rs b/layershellev/src/keyboard.rs index 3d1d87c..77756ff 100644 --- a/layershellev/src/keyboard.rs +++ b/layershellev/src/keyboard.rs @@ -1,16 +1,141 @@ -use std::{ffi::c_char, ops::Deref, os::fd::OwnedFd, ptr::NonNull}; - use memmap2::MmapOptions; -use once_cell::sync::Lazy; +use smol_str::SmolStr; +use std::sync::LazyLock; +use std::{ + env, + ffi::{c_char, CString}, + ops::Deref, + os::{fd::OwnedFd, unix::ffi::OsStringExt}, + ptr::{self, NonNull}, + time::Duration, +}; +use wayland_client::{protocol::wl_keyboard::WlKeyboard, Proxy}; -use xkbcommon_dl::{self as xkb, xkbcommon_handle, XkbCommon}; +use xkbcommon_dl::{ + self as xkb, xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, + xkb_compose_state_flags, xkb_compose_status, xkb_compose_table, xkb_keysym_t, + xkbcommon_compose_handle, xkbcommon_handle, XkbCommon, XkbCommonCompose, +}; 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); +static XKBH: LazyLock<&'static XkbCommon> = LazyLock::new(xkbcommon_handle); +static XKBCH: LazyLock<&'static XkbCommonCompose> = LazyLock::new(xkbcommon_compose_handle); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RepeatInfo { + /// Keys will be repeated at the specified rate and delay. + Repeat { + /// The time between the key repeats. + gap: Duration, + + /// Delay (in milliseconds) between a key press and the start of repetition. + delay: Duration, + }, + + /// Keys should not be repeated. + Disable, +} + +impl Default for RepeatInfo { + /// The default repeat rate is 25 keys per second with the delay of 200ms. + /// + /// The values are picked based on the default in various compositors and Xorg. + fn default() -> Self { + Self::Repeat { + gap: Duration::from_millis(40), + delay: Duration::from_millis(200), + } + } +} + +#[derive(Debug)] +pub struct KeyboardState { + pub keyboard: WlKeyboard, + + pub xkb_context: Context, + pub repeat_info: RepeatInfo, + pub current_repeat: Option, +} + +impl KeyboardState { + pub fn new(keyboard: WlKeyboard) -> Self { + Self { + keyboard, + xkb_context: Context::new().unwrap(), + repeat_info: RepeatInfo::default(), + current_repeat: None, + } + } +} + +impl Drop for KeyboardState { + fn drop(&mut self) { + if self.keyboard.version() >= 3 { + self.keyboard.release(); + } + } +} + +#[derive(Debug)] +pub enum Error { + /// libxkbcommon is not available + XKBNotFound, +} + +#[derive(Debug)] +pub struct Context { + // NOTE: field order matters. + state: Option, + keymap: Option, + compose_state1: Option, + compose_state2: Option, + _compose_table: Option, + context: XkbContext, + scratch_buffer: Vec, +} + +impl Context { + pub fn new() -> Result { + if xkb::xkbcommon_option().is_none() { + return Err(Error::XKBNotFound); + } + + let context = XkbContext::new(); + let mut compose_table = XkbComposeTable::new(&context); + let mut compose_state1 = compose_table.as_ref().and_then(|table| table.new_state()); + let mut compose_state2 = compose_table.as_ref().and_then(|table| table.new_state()); + + // Disable compose if anything compose related failed to initialize. + if compose_table.is_none() || compose_state1.is_none() || compose_state2.is_none() { + compose_state2 = None; + compose_state1 = None; + compose_table = None; + } + + Ok(Self { + state: None, + keymap: None, + compose_state1, + compose_state2, + _compose_table: compose_table, + context, + scratch_buffer: Vec::with_capacity(8), + }) + } + pub fn set_keymap_from_fd(&mut self, fd: OwnedFd, size: usize) { + let keymap = XkbKeymap::from_fd(&self.context, fd, size); + let state = keymap.as_ref().and_then(XkbState::new_wayland); + if keymap.is_none() || state.is_none() { + log::warn!("failed to update xkb keymap"); + } + self.state = state; + self.keymap = keymap; + } +} #[derive(Debug)] pub struct XkbKeymap { @@ -107,7 +232,6 @@ impl XkbState { 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); } @@ -122,3 +246,155 @@ pub struct ModifiersState { logo: bool, num_lock: bool, } + +#[derive(Debug)] +pub struct XkbComposeTable { + table: NonNull, +} + +impl XkbComposeTable { + pub fn new(context: &XkbContext) -> Option { + 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 table = unsafe { + (XKBCH.xkb_compose_table_new_from_locale)( + context.as_ptr(), + locale.as_ptr(), + xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS, + ) + }; + + let table = NonNull::new(table)?; + Some(Self { table }) + } + + /// Create new state with the given compose table. + pub fn new_state(&self) -> Option { + let state = unsafe { + (XKBCH.xkb_compose_state_new)( + self.table.as_ptr(), + xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, + ) + }; + + let state = NonNull::new(state)?; + Some(XkbComposeState { state }) + } +} + +impl Deref for XkbComposeTable { + type Target = NonNull; + + fn deref(&self) -> &Self::Target { + &self.table + } +} + +impl Drop for XkbComposeTable { + fn drop(&mut self) { + unsafe { + (XKBCH.xkb_compose_table_unref)(self.table.as_ptr()); + } + } +} + +#[derive(Debug)] +pub struct XkbComposeState { + state: NonNull, +} + +// NOTE: This is track_caller so we can have more informative line numbers when logging +#[track_caller] +fn byte_slice_to_smol_str(bytes: &[u8]) -> Option { + std::str::from_utf8(bytes) + .map(SmolStr::new) + .map_err(|e| { + log::warn!( + "UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", + bytes + ) + }) + .ok() +} + +/// Shared logic for constructing a string with `xkb_compose_state_get_utf8` and +/// `xkb_state_key_get_utf8`. +fn make_string_with(scratch_buffer: &mut Vec, mut f: F) -> Option +where + F: FnMut(*mut c_char, usize) -> i32, +{ + let size = f(ptr::null_mut(), 0); + if size == 0 { + return None; + } + let size = usize::try_from(size).unwrap(); + scratch_buffer.clear(); + // The allocated buffer must include space for the null-terminator. + scratch_buffer.reserve(size + 1); + unsafe { + let written = f( + scratch_buffer.as_mut_ptr().cast(), + scratch_buffer.capacity(), + ); + if usize::try_from(written).unwrap() != size { + // This will likely never happen. + return None; + } + scratch_buffer.set_len(size); + }; + + byte_slice_to_smol_str(scratch_buffer) +} + +impl XkbComposeState { + pub fn get_string(&mut self, scratch_buffer: &mut Vec) -> Option { + make_string_with(scratch_buffer, |ptr, len| unsafe { + (XKBCH.xkb_compose_state_get_utf8)(self.state.as_ptr(), ptr, len) + }) + } + + #[inline] + pub fn feed(&mut self, keysym: xkb_keysym_t) -> ComposeStatus { + let feed_result = unsafe { (XKBCH.xkb_compose_state_feed)(self.state.as_ptr(), keysym) }; + match feed_result { + xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED => ComposeStatus::Ignored, + xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED => { + ComposeStatus::Accepted(self.status()) + } + } + } + + #[inline] + pub fn reset(&mut self) { + unsafe { + (XKBCH.xkb_compose_state_reset)(self.state.as_ptr()); + } + } + + #[inline] + pub fn status(&mut self) -> xkb_compose_status { + unsafe { (XKBCH.xkb_compose_state_get_status)(self.state.as_ptr()) } + } +} + +impl Drop for XkbComposeState { + fn drop(&mut self) { + unsafe { + (XKBCH.xkb_compose_state_unref)(self.state.as_ptr()); + }; + } +} + +#[derive(Copy, Clone, Debug)] +pub enum ComposeStatus { + Accepted(xkb_compose_status), + Ignored, + None, +} diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index d739d3a..573d285 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -173,6 +173,7 @@ pub mod key; pub use events::{AxisScroll, DispatchMessage, LayerEvent, ReturnData, XdgInfoChangedType}; use key::KeyModifierType; +use keyboard::KeyboardState; use strtoshape::str_to_shape; use wayland_client::{ delegate_noop, @@ -503,7 +504,8 @@ pub struct WindowState { // base managers seat: Option, - keyboard: Option, + keyboard_state: Option, + pointer: Option, touch: Option, @@ -560,7 +562,7 @@ impl WindowState { /// get the keyboard pub fn get_keyboard(&self) -> Option<&WlKeyboard> { - self.keyboard.as_ref() + Some(&self.keyboard_state.as_ref()?.keyboard) } /// get the pointer @@ -709,7 +711,7 @@ impl Default for WindowState { fractional_scale_manager: None, seat: None, - keyboard: None, + keyboard_state: None, pointer: None, touch: None, @@ -804,7 +806,7 @@ impl Dispatch for WindowState { } = event { if capabilities.contains(wl_seat::Capability::Keyboard) { - state.keyboard = Some(seat.get_keyboard(qh, ())); + state.keyboard_state = Some(KeyboardState::new(seat.get_keyboard(qh, ()))); } if capabilities.contains(wl_seat::Capability::Pointer) { state.pointer = Some(seat.get_pointer(qh, ())); @@ -825,17 +827,23 @@ impl Dispatch for WindowState { _conn: &Connection, _qhandle: &QueueHandle, ) { - use keyboard::*; + let keyboard_state = state.keyboard_state.as_mut().unwrap(); match event { - wl_keyboard::Event::Keymap { format, fd, size } => { - if !matches!(format, WEnum::Value(KeymapFormat::XkbV1)) { - return; + wl_keyboard::Event::Keymap { format, fd, size } => match format { + WEnum::Value(KeymapFormat::XkbV1) => { + let context = &mut keyboard_state.xkb_context; + context.set_keymap_from_fd(fd, size as usize) + } + WEnum::Value(KeymapFormat::NoKeymap) => { + log::warn!("non-xkb compatible keymap") } - 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:?}"); + _ => unreachable!(), + }, + wl_keyboard::Event::Leave { serial, surface } => { + // NOTE: clear modifier + } + wl_keyboard::Event::RepeatInfo { rate, delay } => { + // NOTE: RepeatInfo } wl_keyboard::Event::Key { state: keystate, From e50df6acbeb5a9565a837a01811af2ebe5fd477e Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Mon, 29 Jul 2024 23:34:04 +0859 Subject: [PATCH 04/16] chore: remove once_cell --- Cargo.lock | 1 - iced_layershell/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4b602d..c20a438 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1453,7 +1453,6 @@ dependencies = [ "iced_style", "layershellev", "memmap2 0.9.4", - "once_cell", "thiserror", "tracing", "xkbcommon-dl", diff --git a/iced_layershell/Cargo.toml b/iced_layershell/Cargo.toml index 25cec98..a93afb8 100644 --- a/iced_layershell/Cargo.toml +++ b/iced_layershell/Cargo.toml @@ -24,5 +24,4 @@ layershellev.workspace = true futures = "0.3.30" memmap2 = "0.9.4" -once_cell = "1.19.0" xkbcommon-dl = "0.4.2" From 4f4ddad4cde4a0633f4f27f60446a7a814ae9626 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 10:47:24 +0859 Subject: [PATCH 05/16] chore: update --- iced_layershell/src/application.rs | 2 +- iced_layershell/src/conversion.rs | 19 +- iced_layershell/src/conversion/keymap.rs | 344 ++++ iced_layershell/src/event.rs | 11 +- iced_layershell/src/multi_window.rs | 2 +- layershellev/src/events.rs | 45 +- layershellev/src/keyboard.rs | 1999 ++++++++++++++++++---- layershellev/src/keymap.rs | 922 ++++++++++ layershellev/src/lib.rs | 28 +- layershellev/src/xkb_keyboard.rs | 878 ++++++++++ 10 files changed, 3917 insertions(+), 333 deletions(-) create mode 100644 layershellev/src/keymap.rs create mode 100644 layershellev/src/xkb_keyboard.rs diff --git a/iced_layershell/src/application.rs b/iced_layershell/src/application.rs index 3e27f9c..ed59e46 100644 --- a/iced_layershell/src/application.rs +++ b/iced_layershell/src/application.rs @@ -221,7 +221,7 @@ where LayerEvent::NormalDispatch => match &key_event { Some(keyevent) => { if let IcedLayerEvent::Window(windowevent) = keyevent { - let event = IcedLayerEvent::Window(*windowevent); + let event = IcedLayerEvent::Window(windowevent.clone()); if key_ping_count > 70 && key_ping_count < 74 { event_sender.start_send(event).expect("Cannot send"); key_ping_count = 0; diff --git a/iced_layershell/src/conversion.rs b/iced_layershell/src/conversion.rs index 5a379d9..cee352c 100644 --- a/iced_layershell/src/conversion.rs +++ b/iced_layershell/src/conversion.rs @@ -3,9 +3,11 @@ mod keymap; use crate::event::IcedButtonState; use crate::event::IcedKeyState; use crate::event::WindowEvent as LayerShellEvent; -use keymap::{key_from_u32, text_from_key}; - +use iced_core::SmolStr; use iced_core::{keyboard, mouse, Event as IcedEvent}; +use keymap::key; +use keymap::{key_from_u32, text_from_key}; +use layershellev::KeyEvent as LayerShellKeyEvent; #[allow(unused)] pub fn window_event(id: iced_core::window::Id, layerevent: &LayerShellEvent) -> Option { @@ -54,6 +56,18 @@ pub fn window_event(id: iced_core::window::Id, layerevent: &LayerShellEvent) -> })), } } + LayerShellEvent::KeyBoardInput { event, .. } => { + let logical_key = event.key_without_modifiers(); + let text = event + .text_with_all_modifiers() + .map(SmolStr::new) + .filter(|text| !text.as_str().chars().any(is_private_use)); + let LayerShellKeyEvent { + state, location, .. + } = event; + let key = key(logical_key); + todo!() + } _ => None, } } @@ -75,7 +89,6 @@ pub(crate) fn mouse_interaction(interaction: mouse::Interaction) -> String { } } -#[allow(unused)] fn is_private_use(c: char) -> bool { ('\u{E000}'..='\u{F8FF}').contains(&c) } diff --git a/iced_layershell/src/conversion/keymap.rs b/iced_layershell/src/conversion/keymap.rs index 0d5e469..e200635 100644 --- a/iced_layershell/src/conversion/keymap.rs +++ b/iced_layershell/src/conversion/keymap.rs @@ -98,3 +98,347 @@ pub fn text_from_key(key: &IcedKey) -> Option { _ => None, } } + +/// Converts a `VirtualKeyCode` from [`winit`] to an [`iced`] key code. +/// +/// [`winit`]: https://github.com/rust-windowing/winit +/// [`iced`]: https://github.com/iced-rs/iced/tree/0.12 +pub fn key(key: layershellev::keyboard::Key) -> iced_core::keyboard::Key { + use iced_core::keyboard::key::Named; + use layershellev::keyboard::NamedKey; + use iced_core::keyboard; + + match key { + layershellev::keyboard::Key::Character(c) => keyboard::Key::Character(c), + layershellev::keyboard::Key::Named(named_key) => { + keyboard::Key::Named(match named_key { + NamedKey::Alt => Named::Alt, + NamedKey::AltGraph => Named::AltGraph, + NamedKey::CapsLock => Named::CapsLock, + NamedKey::Control => Named::Control, + NamedKey::Fn => Named::Fn, + NamedKey::FnLock => Named::FnLock, + NamedKey::NumLock => Named::NumLock, + NamedKey::ScrollLock => Named::ScrollLock, + NamedKey::Shift => Named::Shift, + NamedKey::Symbol => Named::Symbol, + NamedKey::SymbolLock => Named::SymbolLock, + NamedKey::Meta => Named::Meta, + NamedKey::Hyper => Named::Hyper, + NamedKey::Super => Named::Super, + NamedKey::Enter => Named::Enter, + NamedKey::Tab => Named::Tab, + NamedKey::Space => Named::Space, + NamedKey::ArrowDown => Named::ArrowDown, + NamedKey::ArrowLeft => Named::ArrowLeft, + NamedKey::ArrowRight => Named::ArrowRight, + NamedKey::ArrowUp => Named::ArrowUp, + NamedKey::End => Named::End, + NamedKey::Home => Named::Home, + NamedKey::PageDown => Named::PageDown, + NamedKey::PageUp => Named::PageUp, + NamedKey::Backspace => Named::Backspace, + NamedKey::Clear => Named::Clear, + NamedKey::Copy => Named::Copy, + NamedKey::CrSel => Named::CrSel, + NamedKey::Cut => Named::Cut, + NamedKey::Delete => Named::Delete, + NamedKey::EraseEof => Named::EraseEof, + NamedKey::ExSel => Named::ExSel, + NamedKey::Insert => Named::Insert, + NamedKey::Paste => Named::Paste, + NamedKey::Redo => Named::Redo, + NamedKey::Undo => Named::Undo, + NamedKey::Accept => Named::Accept, + NamedKey::Again => Named::Again, + NamedKey::Attn => Named::Attn, + NamedKey::Cancel => Named::Cancel, + NamedKey::ContextMenu => Named::ContextMenu, + NamedKey::Escape => Named::Escape, + NamedKey::Execute => Named::Execute, + NamedKey::Find => Named::Find, + NamedKey::Help => Named::Help, + NamedKey::Pause => Named::Pause, + NamedKey::Play => Named::Play, + NamedKey::Props => Named::Props, + NamedKey::Select => Named::Select, + NamedKey::ZoomIn => Named::ZoomIn, + NamedKey::ZoomOut => Named::ZoomOut, + NamedKey::BrightnessDown => Named::BrightnessDown, + NamedKey::BrightnessUp => Named::BrightnessUp, + NamedKey::Eject => Named::Eject, + NamedKey::LogOff => Named::LogOff, + NamedKey::Power => Named::Power, + NamedKey::PowerOff => Named::PowerOff, + NamedKey::PrintScreen => Named::PrintScreen, + NamedKey::Hibernate => Named::Hibernate, + NamedKey::Standby => Named::Standby, + NamedKey::WakeUp => Named::WakeUp, + NamedKey::AllCandidates => Named::AllCandidates, + NamedKey::Alphanumeric => Named::Alphanumeric, + NamedKey::CodeInput => Named::CodeInput, + NamedKey::Compose => Named::Compose, + NamedKey::Convert => Named::Convert, + NamedKey::FinalMode => Named::FinalMode, + NamedKey::GroupFirst => Named::GroupFirst, + NamedKey::GroupLast => Named::GroupLast, + NamedKey::GroupNext => Named::GroupNext, + NamedKey::GroupPrevious => Named::GroupPrevious, + NamedKey::ModeChange => Named::ModeChange, + NamedKey::NextCandidate => Named::NextCandidate, + NamedKey::NonConvert => Named::NonConvert, + NamedKey::PreviousCandidate => Named::PreviousCandidate, + NamedKey::Process => Named::Process, + NamedKey::SingleCandidate => Named::SingleCandidate, + NamedKey::HangulMode => Named::HangulMode, + NamedKey::HanjaMode => Named::HanjaMode, + NamedKey::JunjaMode => Named::JunjaMode, + NamedKey::Eisu => Named::Eisu, + NamedKey::Hankaku => Named::Hankaku, + NamedKey::Hiragana => Named::Hiragana, + NamedKey::HiraganaKatakana => Named::HiraganaKatakana, + NamedKey::KanaMode => Named::KanaMode, + NamedKey::KanjiMode => Named::KanjiMode, + NamedKey::Katakana => Named::Katakana, + NamedKey::Romaji => Named::Romaji, + NamedKey::Zenkaku => Named::Zenkaku, + NamedKey::ZenkakuHankaku => Named::ZenkakuHankaku, + NamedKey::Soft1 => Named::Soft1, + NamedKey::Soft2 => Named::Soft2, + NamedKey::Soft3 => Named::Soft3, + NamedKey::Soft4 => Named::Soft4, + NamedKey::ChannelDown => Named::ChannelDown, + NamedKey::ChannelUp => Named::ChannelUp, + NamedKey::Close => Named::Close, + NamedKey::MailForward => Named::MailForward, + NamedKey::MailReply => Named::MailReply, + NamedKey::MailSend => Named::MailSend, + NamedKey::MediaClose => Named::MediaClose, + NamedKey::MediaFastForward => Named::MediaFastForward, + NamedKey::MediaPause => Named::MediaPause, + NamedKey::MediaPlay => Named::MediaPlay, + NamedKey::MediaPlayPause => Named::MediaPlayPause, + NamedKey::MediaRecord => Named::MediaRecord, + NamedKey::MediaRewind => Named::MediaRewind, + NamedKey::MediaStop => Named::MediaStop, + NamedKey::MediaTrackNext => Named::MediaTrackNext, + NamedKey::MediaTrackPrevious => Named::MediaTrackPrevious, + NamedKey::New => Named::New, + NamedKey::Open => Named::Open, + NamedKey::Print => Named::Print, + NamedKey::Save => Named::Save, + NamedKey::SpellCheck => Named::SpellCheck, + NamedKey::Key11 => Named::Key11, + NamedKey::Key12 => Named::Key12, + NamedKey::AudioBalanceLeft => Named::AudioBalanceLeft, + NamedKey::AudioBalanceRight => Named::AudioBalanceRight, + NamedKey::AudioBassBoostDown => Named::AudioBassBoostDown, + NamedKey::AudioBassBoostToggle => Named::AudioBassBoostToggle, + NamedKey::AudioBassBoostUp => Named::AudioBassBoostUp, + NamedKey::AudioFaderFront => Named::AudioFaderFront, + NamedKey::AudioFaderRear => Named::AudioFaderRear, + NamedKey::AudioSurroundModeNext => Named::AudioSurroundModeNext, + NamedKey::AudioTrebleDown => Named::AudioTrebleDown, + NamedKey::AudioTrebleUp => Named::AudioTrebleUp, + NamedKey::AudioVolumeDown => Named::AudioVolumeDown, + NamedKey::AudioVolumeUp => Named::AudioVolumeUp, + NamedKey::AudioVolumeMute => Named::AudioVolumeMute, + NamedKey::MicrophoneToggle => Named::MicrophoneToggle, + NamedKey::MicrophoneVolumeDown => Named::MicrophoneVolumeDown, + NamedKey::MicrophoneVolumeUp => Named::MicrophoneVolumeUp, + NamedKey::MicrophoneVolumeMute => Named::MicrophoneVolumeMute, + NamedKey::SpeechCorrectionList => Named::SpeechCorrectionList, + NamedKey::SpeechInputToggle => Named::SpeechInputToggle, + NamedKey::LaunchApplication1 => Named::LaunchApplication1, + NamedKey::LaunchApplication2 => Named::LaunchApplication2, + NamedKey::LaunchCalendar => Named::LaunchCalendar, + NamedKey::LaunchContacts => Named::LaunchContacts, + NamedKey::LaunchMail => Named::LaunchMail, + NamedKey::LaunchMediaPlayer => Named::LaunchMediaPlayer, + NamedKey::LaunchMusicPlayer => Named::LaunchMusicPlayer, + NamedKey::LaunchPhone => Named::LaunchPhone, + NamedKey::LaunchScreenSaver => Named::LaunchScreenSaver, + NamedKey::LaunchSpreadsheet => Named::LaunchSpreadsheet, + NamedKey::LaunchWebBrowser => Named::LaunchWebBrowser, + NamedKey::LaunchWebCam => Named::LaunchWebCam, + NamedKey::LaunchWordProcessor => Named::LaunchWordProcessor, + NamedKey::BrowserBack => Named::BrowserBack, + NamedKey::BrowserFavorites => Named::BrowserFavorites, + NamedKey::BrowserForward => Named::BrowserForward, + NamedKey::BrowserHome => Named::BrowserHome, + NamedKey::BrowserRefresh => Named::BrowserRefresh, + NamedKey::BrowserSearch => Named::BrowserSearch, + NamedKey::BrowserStop => Named::BrowserStop, + NamedKey::AppSwitch => Named::AppSwitch, + NamedKey::Call => Named::Call, + NamedKey::Camera => Named::Camera, + NamedKey::CameraFocus => Named::CameraFocus, + NamedKey::EndCall => Named::EndCall, + NamedKey::GoBack => Named::GoBack, + NamedKey::GoHome => Named::GoHome, + NamedKey::HeadsetHook => Named::HeadsetHook, + NamedKey::LastNumberRedial => Named::LastNumberRedial, + NamedKey::Notification => Named::Notification, + NamedKey::MannerMode => Named::MannerMode, + NamedKey::VoiceDial => Named::VoiceDial, + NamedKey::TV => Named::TV, + NamedKey::TV3DMode => Named::TV3DMode, + NamedKey::TVAntennaCable => Named::TVAntennaCable, + NamedKey::TVAudioDescription => Named::TVAudioDescription, + NamedKey::TVAudioDescriptionMixDown => { + Named::TVAudioDescriptionMixDown + } + NamedKey::TVAudioDescriptionMixUp => { + Named::TVAudioDescriptionMixUp + } + NamedKey::TVContentsMenu => Named::TVContentsMenu, + NamedKey::TVDataService => Named::TVDataService, + NamedKey::TVInput => Named::TVInput, + NamedKey::TVInputComponent1 => Named::TVInputComponent1, + NamedKey::TVInputComponent2 => Named::TVInputComponent2, + NamedKey::TVInputComposite1 => Named::TVInputComposite1, + NamedKey::TVInputComposite2 => Named::TVInputComposite2, + NamedKey::TVInputHDMI1 => Named::TVInputHDMI1, + NamedKey::TVInputHDMI2 => Named::TVInputHDMI2, + NamedKey::TVInputHDMI3 => Named::TVInputHDMI3, + NamedKey::TVInputHDMI4 => Named::TVInputHDMI4, + NamedKey::TVInputVGA1 => Named::TVInputVGA1, + NamedKey::TVMediaContext => Named::TVMediaContext, + NamedKey::TVNetwork => Named::TVNetwork, + NamedKey::TVNumberEntry => Named::TVNumberEntry, + NamedKey::TVPower => Named::TVPower, + NamedKey::TVRadioService => Named::TVRadioService, + NamedKey::TVSatellite => Named::TVSatellite, + NamedKey::TVSatelliteBS => Named::TVSatelliteBS, + NamedKey::TVSatelliteCS => Named::TVSatelliteCS, + NamedKey::TVSatelliteToggle => Named::TVSatelliteToggle, + NamedKey::TVTerrestrialAnalog => Named::TVTerrestrialAnalog, + NamedKey::TVTerrestrialDigital => Named::TVTerrestrialDigital, + NamedKey::TVTimer => Named::TVTimer, + NamedKey::AVRInput => Named::AVRInput, + NamedKey::AVRPower => Named::AVRPower, + NamedKey::ColorF0Red => Named::ColorF0Red, + NamedKey::ColorF1Green => Named::ColorF1Green, + NamedKey::ColorF2Yellow => Named::ColorF2Yellow, + NamedKey::ColorF3Blue => Named::ColorF3Blue, + NamedKey::ColorF4Grey => Named::ColorF4Grey, + NamedKey::ColorF5Brown => Named::ColorF5Brown, + NamedKey::ClosedCaptionToggle => Named::ClosedCaptionToggle, + NamedKey::Dimmer => Named::Dimmer, + NamedKey::DisplaySwap => Named::DisplaySwap, + NamedKey::DVR => Named::DVR, + NamedKey::Exit => Named::Exit, + NamedKey::FavoriteClear0 => Named::FavoriteClear0, + NamedKey::FavoriteClear1 => Named::FavoriteClear1, + NamedKey::FavoriteClear2 => Named::FavoriteClear2, + NamedKey::FavoriteClear3 => Named::FavoriteClear3, + NamedKey::FavoriteRecall0 => Named::FavoriteRecall0, + NamedKey::FavoriteRecall1 => Named::FavoriteRecall1, + NamedKey::FavoriteRecall2 => Named::FavoriteRecall2, + NamedKey::FavoriteRecall3 => Named::FavoriteRecall3, + NamedKey::FavoriteStore0 => Named::FavoriteStore0, + NamedKey::FavoriteStore1 => Named::FavoriteStore1, + NamedKey::FavoriteStore2 => Named::FavoriteStore2, + NamedKey::FavoriteStore3 => Named::FavoriteStore3, + NamedKey::Guide => Named::Guide, + NamedKey::GuideNextDay => Named::GuideNextDay, + NamedKey::GuidePreviousDay => Named::GuidePreviousDay, + NamedKey::Info => Named::Info, + NamedKey::InstantReplay => Named::InstantReplay, + NamedKey::Link => Named::Link, + NamedKey::ListProgram => Named::ListProgram, + NamedKey::LiveContent => Named::LiveContent, + NamedKey::Lock => Named::Lock, + NamedKey::MediaApps => Named::MediaApps, + NamedKey::MediaAudioTrack => Named::MediaAudioTrack, + NamedKey::MediaLast => Named::MediaLast, + NamedKey::MediaSkipBackward => Named::MediaSkipBackward, + NamedKey::MediaSkipForward => Named::MediaSkipForward, + NamedKey::MediaStepBackward => Named::MediaStepBackward, + NamedKey::MediaStepForward => Named::MediaStepForward, + NamedKey::MediaTopMenu => Named::MediaTopMenu, + NamedKey::NavigateIn => Named::NavigateIn, + NamedKey::NavigateNext => Named::NavigateNext, + NamedKey::NavigateOut => Named::NavigateOut, + NamedKey::NavigatePrevious => Named::NavigatePrevious, + NamedKey::NextFavoriteChannel => Named::NextFavoriteChannel, + NamedKey::NextUserProfile => Named::NextUserProfile, + NamedKey::OnDemand => Named::OnDemand, + NamedKey::Pairing => Named::Pairing, + NamedKey::PinPDown => Named::PinPDown, + NamedKey::PinPMove => Named::PinPMove, + NamedKey::PinPToggle => Named::PinPToggle, + NamedKey::PinPUp => Named::PinPUp, + NamedKey::PlaySpeedDown => Named::PlaySpeedDown, + NamedKey::PlaySpeedReset => Named::PlaySpeedReset, + NamedKey::PlaySpeedUp => Named::PlaySpeedUp, + NamedKey::RandomToggle => Named::RandomToggle, + NamedKey::RcLowBattery => Named::RcLowBattery, + NamedKey::RecordSpeedNext => Named::RecordSpeedNext, + NamedKey::RfBypass => Named::RfBypass, + NamedKey::ScanChannelsToggle => Named::ScanChannelsToggle, + NamedKey::ScreenModeNext => Named::ScreenModeNext, + NamedKey::Settings => Named::Settings, + NamedKey::SplitScreenToggle => Named::SplitScreenToggle, + NamedKey::STBInput => Named::STBInput, + NamedKey::STBPower => Named::STBPower, + NamedKey::Subtitle => Named::Subtitle, + NamedKey::Teletext => Named::Teletext, + NamedKey::VideoModeNext => Named::VideoModeNext, + NamedKey::Wink => Named::Wink, + NamedKey::ZoomToggle => Named::ZoomToggle, + NamedKey::F1 => Named::F1, + NamedKey::F2 => Named::F2, + NamedKey::F3 => Named::F3, + NamedKey::F4 => Named::F4, + NamedKey::F5 => Named::F5, + NamedKey::F6 => Named::F6, + NamedKey::F7 => Named::F7, + NamedKey::F8 => Named::F8, + NamedKey::F9 => Named::F9, + NamedKey::F10 => Named::F10, + NamedKey::F11 => Named::F11, + NamedKey::F12 => Named::F12, + NamedKey::F13 => Named::F13, + NamedKey::F14 => Named::F14, + NamedKey::F15 => Named::F15, + NamedKey::F16 => Named::F16, + NamedKey::F17 => Named::F17, + NamedKey::F18 => Named::F18, + NamedKey::F19 => Named::F19, + NamedKey::F20 => Named::F20, + NamedKey::F21 => Named::F21, + NamedKey::F22 => Named::F22, + NamedKey::F23 => Named::F23, + NamedKey::F24 => Named::F24, + NamedKey::F25 => Named::F25, + NamedKey::F26 => Named::F26, + NamedKey::F27 => Named::F27, + NamedKey::F28 => Named::F28, + NamedKey::F29 => Named::F29, + NamedKey::F30 => Named::F30, + NamedKey::F31 => Named::F31, + NamedKey::F32 => Named::F32, + NamedKey::F33 => Named::F33, + NamedKey::F34 => Named::F34, + NamedKey::F35 => Named::F35, + _ => return keyboard::Key::Unidentified, + }) + } + _ => keyboard::Key::Unidentified, + } +} + +pub fn modifiers( + modifiers: layershellev::keyboard::ModifiersState, +) -> iced_core::keyboard::Modifiers { + use iced_core::keyboard; + let mut result = keyboard::Modifiers::empty(); + + result.set(keyboard::Modifiers::SHIFT, modifiers.shift_key()); + result.set(keyboard::Modifiers::CTRL, modifiers.control_key()); + result.set(keyboard::Modifiers::ALT, modifiers.alt_key()); + result.set(keyboard::Modifiers::LOGO, modifiers.super_key()); + + result +} diff --git a/iced_layershell/src/event.rs b/iced_layershell/src/event.rs index 0b3b426..9256f4e 100644 --- a/iced_layershell/src/event.rs +++ b/iced_layershell/src/event.rs @@ -1,5 +1,6 @@ use layershellev::id::Id; use layershellev::key::KeyModifierType; +use layershellev::KeyEvent as LayerShellKeyEvent; use layershellev::reexport::wayland_client::{ButtonState, KeyState, WEnum}; use layershellev::{DispatchMessage, WindowWrapper}; @@ -30,7 +31,7 @@ fn modifier_from_layershell_to_iced(modifier: KeyModifierType) -> IcedModifiers IcedModifiers::from_bits(modifier.bits()).unwrap_or(IcedModifiers::empty()) } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum WindowEvent { ScaleChanged(u32), CursorEnter { @@ -48,6 +49,10 @@ pub enum WindowEvent { key: u32, modifiers: IcedModifiers, }, + KeyBoardInput { + event: LayerShellKeyEvent, + is_synthetic: bool, + }, Axis { x: f32, y: f32, @@ -129,6 +134,10 @@ impl From<&DispatchMessage> for IcedLayerEvent { key: *key, modifiers: modifier_from_layershell_to_iced(*modifier), }), + DispatchMessage::KeyboardInput { event, is_synthetic } => IcedLayerEvent::Window(WindowEvent::KeyBoardInput{ + event: event.clone(), + is_synthetic: *is_synthetic + }), DispatchMessage::Axis { horizontal, vertical, diff --git a/iced_layershell/src/multi_window.rs b/iced_layershell/src/multi_window.rs index 8452b73..cf3fbc8 100644 --- a/iced_layershell/src/multi_window.rs +++ b/iced_layershell/src/multi_window.rs @@ -244,7 +244,7 @@ where LayerEvent::NormalDispatch => match &key_event { Some(keyevent) => { if let IcedLayerEvent::Window(windowevent) = keyevent { - let event = IcedLayerEvent::Window(*windowevent); + let event = IcedLayerEvent::Window(windowevent.clone()); if key_ping_count > 70 && key_ping_count < 74 { event_sender .start_send(MultiWindowIcedLayerEvent(id, event)) diff --git a/layershellev/src/events.rs b/layershellev/src/events.rs index b1726ad..48349b0 100644 --- a/layershellev/src/events.rs +++ b/layershellev/src/events.rs @@ -10,7 +10,10 @@ use wayland_client::{ QueueHandle, WEnum, }; -use crate::key::KeyModifierType; +use crate::{ + key::KeyModifierType, + xkb_keyboard::{KeyEvent, ModifiersState, ModifiersStateXkb}, +}; use super::WindowState; @@ -147,6 +150,21 @@ pub(crate) enum DispatchMessageInner { key: u32, time: u32, }, + ModifiersChanged(ModifiersState), + KeyboardInput { + event: KeyEvent, + + /// If `true`, the event was generated synthetically by winit + /// in one of the following circumstances: + /// + /// * Synthetic key press events are generated for all keys pressed when a window gains + /// focus. Likewise, synthetic key release events are generated for all keys pressed when + /// a window goes out of focus. ***Currently, this is only functional on X11 and + /// Windows*** + /// + /// Otherwise, this value is always `false`. + is_synthetic: bool, + }, RefreshSurface { width: u32, height: u32, @@ -218,6 +236,21 @@ pub enum DispatchMessage { key: u32, time: u32, }, + ModifiersChanged(ModifiersState), + KeyboardInput { + event: KeyEvent, + + /// If `true`, the event was generated synthetically by winit + /// in one of the following circumstances: + /// + /// * Synthetic key press events are generated for all keys pressed when a window gains + /// focus. Likewise, synthetic key release events are generated for all keys pressed when + /// a window goes out of focus. ***Currently, this is only functional on X11 and + /// Windows*** + /// + /// Otherwise, this value is always `false`. + is_synthetic: bool, + }, /// this will request to do refresh the whole screen, because the layershell tell that a new /// configure happened RequestRefresh { @@ -310,6 +343,16 @@ impl From for DispatchMessage { vertical, source, }, + DispatchMessageInner::ModifiersChanged(modifier) => { + DispatchMessage::ModifiersChanged(modifier) + } + DispatchMessageInner::KeyboardInput { + event, + is_synthetic, + } => DispatchMessage::KeyboardInput { + event, + is_synthetic, + }, DispatchMessageInner::PrefredScale(scale) => DispatchMessage::PrefredScale(scale), DispatchMessageInner::RefreshSurface { .. } => unimplemented!(), DispatchMessageInner::XdgInfoChanged(_) => unimplemented!(), diff --git a/layershellev/src/keyboard.rs b/layershellev/src/keyboard.rs index 77756ff..43c3398 100644 --- a/layershellev/src/keyboard.rs +++ b/layershellev/src/keyboard.rs @@ -1,400 +1,1761 @@ -use memmap2::MmapOptions; -use smol_str::SmolStr; -use std::sync::LazyLock; -use std::{ - env, - ffi::{c_char, CString}, - ops::Deref, - os::{fd::OwnedFd, unix::ffi::OsStringExt}, - ptr::{self, NonNull}, - time::Duration, -}; -use wayland_client::{protocol::wl_keyboard::WlKeyboard, Proxy}; - -use xkbcommon_dl::{ - self as xkb, xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, - xkb_compose_state_flags, xkb_compose_status, xkb_compose_table, xkb_keysym_t, - xkbcommon_compose_handle, xkbcommon_handle, XkbCommon, XkbCommonCompose, -}; - -use xkb::{ - xkb_context, xkb_context_flags, xkb_keymap, xkb_keymap_compile_flags, xkb_state, - xkb_state_component, -}; - -static XKBH: LazyLock<&'static XkbCommon> = LazyLock::new(xkbcommon_handle); -static XKBCH: LazyLock<&'static XkbCommonCompose> = LazyLock::new(xkbcommon_compose_handle); - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum RepeatInfo { - /// Keys will be repeated at the specified rate and delay. - Repeat { - /// The time between the key repeats. - gap: Duration, - - /// Delay (in milliseconds) between a key press and the start of repetition. - delay: Duration, - }, - - /// Keys should not be repeated. - Disable, -} - -impl Default for RepeatInfo { - /// The default repeat rate is 25 keys per second with the delay of 200ms. - /// - /// The values are picked based on the default in various compositors and Xorg. - fn default() -> Self { - Self::Repeat { - gap: Duration::from_millis(40), - delay: Duration::from_millis(200), +//! Types related to the keyboard. + +// This file contains a substantial portion of the UI Events Specification by the W3C. In +// particular, the variant names within `Key` and `KeyCode` and their documentation are modified +// versions of contents of the aforementioned specification. +// +// The original documents are: +// +// ### For `Key` +// UI Events KeyboardEvent key Values +// https://www.w3.org/TR/2017/CR-uievents-key-20170601/ +// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). +// +// ### For `KeyCode` +// UI Events KeyboardEvent code Values +// https://www.w3.org/TR/2017/CR-uievents-code-20170601/ +// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). +// +// These documents were used under the terms of the following license. This W3C license as well as +// the W3C short notice apply to the `Key` and `KeyCode` enums and their variants and the +// documentation attached to their variants. + +// --------- BEGINNING OF W3C LICENSE -------------------------------------------------------------- +// +// License +// +// By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, +// and will comply with the following terms and conditions. +// +// Permission to copy, modify, and distribute this work, with or without modification, for any +// purpose and without fee or royalty is hereby granted, provided that you include the following on +// ALL copies of the work or portions thereof, including modifications: +// +// - The full text of this NOTICE in a location viewable to users of the redistributed or derivative +// work. +// - Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none +// exist, the W3C Software and Document Short Notice should be included. +// - Notice of any changes or modifications, through a copyright statement on the new code or +// document such as "This software or document includes material copied from or derived from +// [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." +// +// Disclaimers +// +// THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR +// ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD +// PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +// +// COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES +// ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. +// +// The name and trademarks of copyright holders may NOT be used in advertising or publicity +// pertaining to the work without specific, written prior permission. Title to copyright in this +// work will at all times remain with copyright holders. +// +// --------- END OF W3C LICENSE -------------------------------------------------------------------- + +// --------- BEGINNING OF W3C SHORT NOTICE --------------------------------------------------------- +// +// winit: https://github.com/rust-windowing/winit +// +// Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology, European +// Research Consortium for Informatics and Mathematics, Keio University, Beihang). All Rights +// Reserved. This work is distributed under the W3C® Software License [1] in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// [1] http://www.w3.org/Consortium/Legal/copyright-software +// +// --------- END OF W3C SHORT NOTICE --------------------------------------------------------------- + +use bitflags::bitflags; +pub use smol_str::SmolStr; + +/// Contains the platform-native physical key identifier +/// +/// The exact values vary from platform to platform (which is part of why this is a per-platform +/// enum), but the values are primarily tied to the key's physical location on the keyboard. +/// +/// This enum is primarily used to store raw keycodes when Winit doesn't map a given native +/// physical key identifier to a meaningful [`KeyCode`] variant. In the presence of identifiers we +/// haven't mapped for you yet, this lets you use use [`KeyCode`] to: +/// +/// - Correctly match key press and release events. +/// - On non-Web platforms, support assigning keybinds to virtually any key through a UI. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum NativeKeyCode { + Unidentified, + /// An Android "scancode". + Android(u32), + /// A macOS "scancode". + MacOS(u16), + /// A Windows "scancode". + Windows(u16), + /// An XKB "keycode". + Xkb(u32), +} + +impl std::fmt::Debug for NativeKeyCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use NativeKeyCode::{Android, MacOS, Unidentified, Windows, Xkb}; + let mut debug_tuple; + match self { + Unidentified => { + debug_tuple = f.debug_tuple("Unidentified"); + }, + Android(code) => { + debug_tuple = f.debug_tuple("Android"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + MacOS(code) => { + debug_tuple = f.debug_tuple("MacOS"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Windows(code) => { + debug_tuple = f.debug_tuple("Windows"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Xkb(code) => { + debug_tuple = f.debug_tuple("Xkb"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, } + debug_tuple.finish() } } -#[derive(Debug)] -pub struct KeyboardState { - pub keyboard: WlKeyboard, - - pub xkb_context: Context, - pub repeat_info: RepeatInfo, - pub current_repeat: Option, +/// Contains the platform-native logical key identifier +/// +/// Exactly what that means differs from platform to platform, but the values are to some degree +/// tied to the currently active keyboard layout. The same key on the same keyboard may also report +/// different values on different platforms, which is one of the reasons this is a per-platform +/// enum. +/// +/// This enum is primarily used to store raw keysym when Winit doesn't map a given native logical +/// key identifier to a meaningful [`Key`] variant. This lets you use [`Key`], and let the user +/// define keybinds which work in the presence of identifiers we haven't mapped for you yet. +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum NativeKey { + Unidentified, + /// An Android "keycode", which is similar to a "virtual-key code" on Windows. + Android(u32), + /// A macOS "scancode". There does not appear to be any direct analogue to either keysyms or + /// "virtual-key" codes in macOS, so we report the scancode instead. + MacOS(u16), + /// A Windows "virtual-key code". + Windows(u16), + /// An XKB "keysym". + Xkb(u32), + /// A "key value string". + Web(SmolStr), } -impl KeyboardState { - pub fn new(keyboard: WlKeyboard) -> Self { - Self { - keyboard, - xkb_context: Context::new().unwrap(), - repeat_info: RepeatInfo::default(), - current_repeat: None, +impl std::fmt::Debug for NativeKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use NativeKey::{Android, MacOS, Unidentified, Web, Windows, Xkb}; + let mut debug_tuple; + match self { + Unidentified => { + debug_tuple = f.debug_tuple("Unidentified"); + }, + Android(code) => { + debug_tuple = f.debug_tuple("Android"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + MacOS(code) => { + debug_tuple = f.debug_tuple("MacOS"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Windows(code) => { + debug_tuple = f.debug_tuple("Windows"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Xkb(code) => { + debug_tuple = f.debug_tuple("Xkb"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Web(code) => { + debug_tuple = f.debug_tuple("Web"); + debug_tuple.field(code); + }, } + debug_tuple.finish() } } -impl Drop for KeyboardState { - fn drop(&mut self) { - if self.keyboard.version() >= 3 { - self.keyboard.release(); +impl From for NativeKey { + #[inline] + fn from(code: NativeKeyCode) -> Self { + match code { + NativeKeyCode::Unidentified => NativeKey::Unidentified, + NativeKeyCode::Android(x) => NativeKey::Android(x), + NativeKeyCode::MacOS(x) => NativeKey::MacOS(x), + NativeKeyCode::Windows(x) => NativeKey::Windows(x), + NativeKeyCode::Xkb(x) => NativeKey::Xkb(x), } } } -#[derive(Debug)] -pub enum Error { - /// libxkbcommon is not available - XKBNotFound, +impl PartialEq for NativeKeyCode { + #[allow(clippy::cmp_owned)] // uses less code than direct match; target is stack allocated + #[inline] + fn eq(&self, rhs: &NativeKey) -> bool { + NativeKey::from(*self) == *rhs + } } -#[derive(Debug)] -pub struct Context { - // NOTE: field order matters. - state: Option, - keymap: Option, - compose_state1: Option, - compose_state2: Option, - _compose_table: Option, - context: XkbContext, - scratch_buffer: Vec, +impl PartialEq for NativeKey { + #[inline] + fn eq(&self, rhs: &NativeKeyCode) -> bool { + rhs == self + } } -impl Context { - pub fn new() -> Result { - if xkb::xkbcommon_option().is_none() { - return Err(Error::XKBNotFound); - } - - let context = XkbContext::new(); - let mut compose_table = XkbComposeTable::new(&context); - let mut compose_state1 = compose_table.as_ref().and_then(|table| table.new_state()); - let mut compose_state2 = compose_table.as_ref().and_then(|table| table.new_state()); +/// Represents the location of a physical key. +/// +/// This type is a superset of [`KeyCode`], including an [`Unidentified`][Self::Unidentified] +/// variant. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PhysicalKey { + /// A known key code + Code(KeyCode), + /// This variant is used when the key cannot be translated to a [`KeyCode`] + /// + /// The native keycode is provided (if available) so you're able to more reliably match + /// key-press and key-release events by hashing the [`PhysicalKey`]. It is also possible to use + /// this for keybinds for non-standard keys, but such keybinds are tied to a given platform. + Unidentified(NativeKeyCode), +} - // Disable compose if anything compose related failed to initialize. - if compose_table.is_none() || compose_state1.is_none() || compose_state2.is_none() { - compose_state2 = None; - compose_state1 = None; - compose_table = None; - } +impl From for PhysicalKey { + #[inline] + fn from(code: KeyCode) -> Self { + PhysicalKey::Code(code) + } +} - Ok(Self { - state: None, - keymap: None, - compose_state1, - compose_state2, - _compose_table: compose_table, - context, - scratch_buffer: Vec::with_capacity(8), - }) +impl From for PhysicalKey { + #[inline] + fn from(code: NativeKeyCode) -> Self { + PhysicalKey::Unidentified(code) } - pub fn set_keymap_from_fd(&mut self, fd: OwnedFd, size: usize) { - let keymap = XkbKeymap::from_fd(&self.context, fd, size); - let state = keymap.as_ref().and_then(XkbState::new_wayland); - if keymap.is_none() || state.is_none() { - log::warn!("failed to update xkb keymap"); +} + +impl PartialEq for PhysicalKey { + #[inline] + fn eq(&self, rhs: &KeyCode) -> bool { + match self { + PhysicalKey::Code(ref code) => code == rhs, + _ => false, } - self.state = state; - self.keymap = keymap; } } -#[derive(Debug)] -pub struct XkbKeymap { - keymap: NonNull, +impl PartialEq for KeyCode { + #[inline] + fn eq(&self, rhs: &PhysicalKey) -> bool { + rhs == self + } } -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 PartialEq for PhysicalKey { + #[inline] + fn eq(&self, rhs: &NativeKeyCode) -> bool { + match self { + PhysicalKey::Unidentified(ref code) => code == rhs, + _ => false, + } } } -impl Drop for XkbKeymap { - fn drop(&mut self) { - unsafe { (XKBH.xkb_keymap_unref)(self.keymap.as_ptr()) } +impl PartialEq for NativeKeyCode { + #[inline] + fn eq(&self, rhs: &PhysicalKey) -> bool { + rhs == self } } -impl Deref for XkbKeymap { - type Target = NonNull; - fn deref(&self) -> &Self::Target { - &self.keymap - } +/// Code representing the location of a physical key +/// +/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few +/// exceptions: +/// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and +/// "SuperRight" here. +/// - The key that the specification calls "Super" is reported as `Unidentified` here. +/// +/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum KeyCode { + /// ` on a US keyboard. This is also called a backtick or grave. + /// This is the 半角/全角/漢字 + /// (hankaku/zenkaku/kanji) key on Japanese keyboards + Backquote, + /// Used for both the US \\ (on the 101-key layout) and also for the key + /// located between the " and Enter keys on row C of the 102-, + /// 104- and 106-key layouts. + /// Labeled # on a UK (102) keyboard. + Backslash, + /// [ on a US keyboard. + BracketLeft, + /// ] on a US keyboard. + BracketRight, + /// , on a US keyboard. + Comma, + /// 0 on a US keyboard. + Digit0, + /// 1 on a US keyboard. + Digit1, + /// 2 on a US keyboard. + Digit2, + /// 3 on a US keyboard. + Digit3, + /// 4 on a US keyboard. + Digit4, + /// 5 on a US keyboard. + Digit5, + /// 6 on a US keyboard. + Digit6, + /// 7 on a US keyboard. + Digit7, + /// 8 on a US keyboard. + Digit8, + /// 9 on a US keyboard. + Digit9, + /// = on a US keyboard. + Equal, + /// Located between the left Shift and Z keys. + /// Labeled \\ on a UK keyboard. + IntlBackslash, + /// Located between the / and right Shift keys. + /// Labeled \\ (ro) on a Japanese keyboard. + IntlRo, + /// Located between the = and Backspace keys. + /// Labeled ¥ (yen) on a Japanese keyboard. \\ on a + /// Russian keyboard. + IntlYen, + /// a on a US keyboard. + /// Labeled q on an AZERTY (e.g., French) keyboard. + KeyA, + /// b on a US keyboard. + KeyB, + /// c on a US keyboard. + KeyC, + /// d on a US keyboard. + KeyD, + /// e on a US keyboard. + KeyE, + /// f on a US keyboard. + KeyF, + /// g on a US keyboard. + KeyG, + /// h on a US keyboard. + KeyH, + /// i on a US keyboard. + KeyI, + /// j on a US keyboard. + KeyJ, + /// k on a US keyboard. + KeyK, + /// l on a US keyboard. + KeyL, + /// m on a US keyboard. + KeyM, + /// n on a US keyboard. + KeyN, + /// o on a US keyboard. + KeyO, + /// p on a US keyboard. + KeyP, + /// q on a US keyboard. + /// Labeled a on an AZERTY (e.g., French) keyboard. + KeyQ, + /// r on a US keyboard. + KeyR, + /// s on a US keyboard. + KeyS, + /// t on a US keyboard. + KeyT, + /// u on a US keyboard. + KeyU, + /// v on a US keyboard. + KeyV, + /// w on a US keyboard. + /// Labeled z on an AZERTY (e.g., French) keyboard. + KeyW, + /// x on a US keyboard. + KeyX, + /// y on a US keyboard. + /// Labeled z on a QWERTZ (e.g., German) keyboard. + KeyY, + /// z on a US keyboard. + /// Labeled w on an AZERTY (e.g., French) keyboard, and y on a + /// QWERTZ (e.g., German) keyboard. + KeyZ, + /// - on a US keyboard. + Minus, + /// . on a US keyboard. + Period, + /// ' on a US keyboard. + Quote, + /// ; on a US keyboard. + Semicolon, + /// / on a US keyboard. + Slash, + /// Alt, Option, or . + AltLeft, + /// Alt, Option, or . + /// This is labeled AltGr on many keyboard layouts. + AltRight, + /// Backspace or . + /// Labeled Delete on Apple keyboards. + Backspace, + /// CapsLock or + CapsLock, + /// The application context menu key, which is typically found between the right + /// Super key and the right Control key. + ContextMenu, + /// Control or + ControlLeft, + /// Control or + ControlRight, + /// Enter or . Labeled Return on Apple keyboards. + Enter, + /// The Windows, , Command, or other OS symbol key. + SuperLeft, + /// The Windows, , Command, or other OS symbol key. + SuperRight, + /// Shift or + ShiftLeft, + /// Shift or + ShiftRight, + ///   (space) + Space, + /// Tab or + Tab, + /// Japanese: (henkan) + Convert, + /// Japanese: カタカナ/ひらがな/ローマ字 + /// (katakana/hiragana/romaji) + KanaMode, + /// Korean: HangulMode 한/영 (han/yeong) + /// + /// Japanese (Mac keyboard): (kana) + Lang1, + /// Korean: Hanja (hanja) + /// + /// Japanese (Mac keyboard): (eisu) + Lang2, + /// Japanese (word-processing keyboard): Katakana + Lang3, + /// Japanese (word-processing keyboard): Hiragana + Lang4, + /// Japanese (word-processing keyboard): Zenkaku/Hankaku + Lang5, + /// Japanese: 無変換 (muhenkan) + NonConvert, + /// . The forward delete key. + /// Note that on Apple keyboards, the key labelled Delete on the main part of + /// the keyboard is encoded as [`Backspace`]. + /// + /// [`Backspace`]: Self::Backspace + Delete, + /// Page Down, End, or + End, + /// Help. Not present on standard PC keyboards. + Help, + /// Home or + Home, + /// Insert or Ins. Not present on Apple keyboards. + Insert, + /// Page Down, PgDn, or + PageDown, + /// Page Up, PgUp, or + PageUp, + /// + ArrowDown, + /// + ArrowLeft, + /// + ArrowRight, + /// + ArrowUp, + /// On the Mac, this is used for the numpad Clear key. + NumLock, + /// 0 Ins on a keyboard. 0 on a phone or remote control + Numpad0, + /// 1 End on a keyboard. 1 or 1 QZ on a phone or remote + /// control + Numpad1, + /// 2 ↓ on a keyboard. 2 ABC on a phone or remote control + Numpad2, + /// 3 PgDn on a keyboard. 3 DEF on a phone or remote control + Numpad3, + /// 4 ← on a keyboard. 4 GHI on a phone or remote control + Numpad4, + /// 5 on a keyboard. 5 JKL on a phone or remote control + Numpad5, + /// 6 → on a keyboard. 6 MNO on a phone or remote control + Numpad6, + /// 7 Home on a keyboard. 7 PQRS or 7 PRS on a phone + /// or remote control + Numpad7, + /// 8 ↑ on a keyboard. 8 TUV on a phone or remote control + Numpad8, + /// 9 PgUp on a keyboard. 9 WXYZ or 9 WXY on a phone + /// or remote control + Numpad9, + /// + + NumpadAdd, + /// Found on the Microsoft Natural Keyboard. + NumpadBackspace, + /// C or A (All Clear). Also for use with numpads that have a + /// Clear key that is separate from the NumLock key. On the Mac, the + /// numpad Clear key is encoded as [`NumLock`]. + /// + /// [`NumLock`]: Self::NumLock + NumpadClear, + /// C (Clear Entry) + NumpadClearEntry, + /// , (thousands separator). For locales where the thousands separator + /// is a "." (e.g., Brazil), this key may generate a .. + NumpadComma, + /// . Del. For locales where the decimal separator is "," (e.g., + /// Brazil), this key may generate a ,. + NumpadDecimal, + /// / + NumpadDivide, + NumpadEnter, + /// = + NumpadEqual, + /// # on a phone or remote control device. This key is typically found + /// below the 9 key and to the right of the 0 key. + NumpadHash, + /// M Add current entry to the value stored in memory. + NumpadMemoryAdd, + /// M Clear the value stored in memory. + NumpadMemoryClear, + /// M Replace the current entry with the value stored in memory. + NumpadMemoryRecall, + /// M Replace the value stored in memory with the current entry. + NumpadMemoryStore, + /// M Subtract current entry from the value stored in memory. + NumpadMemorySubtract, + /// * on a keyboard. For use with numpads that provide mathematical + /// operations (+, - * and /). + /// + /// Use `NumpadStar` for the * key on phones and remote controls. + NumpadMultiply, + /// ( Found on the Microsoft Natural Keyboard. + NumpadParenLeft, + /// ) Found on the Microsoft Natural Keyboard. + NumpadParenRight, + /// * on a phone or remote control device. + /// + /// This key is typically found below the 7 key and to the left of + /// the 0 key. + /// + /// Use "NumpadMultiply" for the * key on + /// numeric keypads. + NumpadStar, + /// - + NumpadSubtract, + /// Esc or + Escape, + /// Fn This is typically a hardware key that does not generate a separate code. + Fn, + /// FLock or FnLock. Function Lock key. Found on the Microsoft + /// Natural Keyboard. + FnLock, + /// PrtScr SysRq or Print Screen + PrintScreen, + /// Scroll Lock + ScrollLock, + /// Pause Break + Pause, + /// Some laptops place this key to the left of the key. + /// + /// This also the "back" button (triangle) on Android. + BrowserBack, + BrowserFavorites, + /// Some laptops place this key to the right of the key. + BrowserForward, + /// The "home" button on Android. + BrowserHome, + BrowserRefresh, + BrowserSearch, + BrowserStop, + /// Eject or . This key is placed in the function section on some Apple + /// keyboards. + Eject, + /// Sometimes labelled My Computer on the keyboard + LaunchApp1, + /// Sometimes labelled Calculator on the keyboard + LaunchApp2, + LaunchMail, + MediaPlayPause, + MediaSelect, + MediaStop, + MediaTrackNext, + MediaTrackPrevious, + /// This key is placed in the function section on some Apple keyboards, replacing the + /// Eject key. + Power, + Sleep, + AudioVolumeDown, + AudioVolumeMute, + AudioVolumeUp, + WakeUp, + // Legacy modifier key. Also called "Super" in certain places. + Meta, + // Legacy modifier key. + Hyper, + Turbo, + Abort, + Resume, + Suspend, + /// Found on Sun’s USB keyboard. + Again, + /// Found on Sun’s USB keyboard. + Copy, + /// Found on Sun’s USB keyboard. + Cut, + /// Found on Sun’s USB keyboard. + Find, + /// Found on Sun’s USB keyboard. + Open, + /// Found on Sun’s USB keyboard. + Paste, + /// Found on Sun’s USB keyboard. + Props, + /// Found on Sun’s USB keyboard. + Select, + /// Found on Sun’s USB keyboard. + Undo, + /// Use for dedicated ひらがな key found on some Japanese word processing keyboards. + Hiragana, + /// Use for dedicated カタカナ key found on some Japanese word processing keyboards. + Katakana, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F1, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F2, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F3, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F4, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F5, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F6, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F7, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F8, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F9, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F10, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F11, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F12, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F13, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F14, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F15, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F16, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F17, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F18, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F19, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F20, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F21, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F22, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F23, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F24, + /// General-purpose function key. + F25, + /// General-purpose function key. + F26, + /// General-purpose function key. + F27, + /// General-purpose function key. + F28, + /// General-purpose function key. + F29, + /// General-purpose function key. + F30, + /// General-purpose function key. + F31, + /// General-purpose function key. + F32, + /// General-purpose function key. + F33, + /// General-purpose function key. + F34, + /// General-purpose function key. + F35, } -#[derive(Debug)] -pub struct XkbContext { - context: NonNull, +/// A [`Key::Named`] value +/// +/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few +/// exceptions: +/// - The `Super` variant here, is named `Meta` in the aforementioned specification. (There's +/// another key which the specification calls `Super`. That does not exist here.) +/// - The `Space` variant here, can be identified by the character it generates in the +/// specification. +/// +/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum NamedKey { + /// The `Alt` (Alternative) key. + /// + /// This key enables the alternate modifier function for interpreting concurrent or subsequent + /// keyboard input. This key value is also used for the Apple Option key. + Alt, + /// The Alternate Graphics (AltGr or AltGraph) key. + /// + /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the + /// level 2 modifier). + AltGraph, + /// The `Caps Lock` (Capital) key. + /// + /// Toggle capital character lock function for interpreting subsequent keyboard input event. + CapsLock, + /// The `Control` or `Ctrl` key. + /// + /// Used to enable control modifier function for interpreting concurrent or subsequent keyboard + /// input. + Control, + /// The Function switch `Fn` key. Activating this key simultaneously with another key changes + /// that key’s value to an alternate character or function. This key is often handled directly + /// in the keyboard hardware and does not usually generate key events. + Fn, + /// The Function-Lock (`FnLock` or `F-Lock`) key. Activating this key switches the mode of the + /// keyboard to changes some keys' values to an alternate character or function. This key is + /// often handled directly in the keyboard hardware and does not usually generate key events. + FnLock, + /// The `NumLock` or Number Lock key. Used to toggle numpad mode function for interpreting + /// subsequent keyboard input. + NumLock, + /// Toggle between scrolling and cursor movement modes. + ScrollLock, + /// Used to enable shift modifier function for interpreting concurrent or subsequent keyboard + /// input. + Shift, + /// The Symbol modifier key (used on some virtual keyboards). + Symbol, + SymbolLock, + // Legacy modifier key. Also called "Super" in certain places. + Meta, + // Legacy modifier key. + Hyper, + /// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard + /// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` + /// key. + /// + /// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key. + Super, + /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This + /// key value is also used for the `Return` (Macintosh numpad) key. This key value is also + /// used for the Android `KEYCODE_DPAD_CENTER`. + Enter, + /// The Horizontal Tabulation `Tab` key. + Tab, + /// Used in text to insert a space between words. Usually located below the character keys. + Space, + /// Navigate or traverse downward. (`KEYCODE_DPAD_DOWN`) + ArrowDown, + /// Navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`) + ArrowLeft, + /// Navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`) + ArrowRight, + /// Navigate or traverse upward. (`KEYCODE_DPAD_UP`) + ArrowUp, + /// The End key, used with keyboard entry to go to the end of content (`KEYCODE_MOVE_END`). + End, + /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`). + /// For the mobile phone `Home` key (which goes to the phone’s main screen), use [`GoHome`]. + /// + /// [`GoHome`]: Self::GoHome + Home, + /// Scroll down or display next page of content. + PageDown, + /// Scroll up or display previous page of content. + PageUp, + /// Used to remove the character to the left of the cursor. This key value is also used for + /// the key labeled `Delete` on MacOS keyboards. + Backspace, + /// Remove the currently selected input. + Clear, + /// Copy the current selection. (`APPCOMMAND_COPY`) + Copy, + /// The Cursor Select key. + CrSel, + /// Cut the current selection. (`APPCOMMAND_CUT`) + Cut, + /// Used to delete the character to the right of the cursor. This key value is also used for + /// the key labeled `Delete` on MacOS keyboards when `Fn` is active. + Delete, + /// The Erase to End of Field key. This key deletes all characters from the current cursor + /// position to the end of the current field. + EraseEof, + /// The Extend Selection (Exsel) key. + ExSel, + /// Toggle between text modes for insertion or overtyping. + /// (`KEYCODE_INSERT`) + Insert, + /// The Paste key. (`APPCOMMAND_PASTE`) + Paste, + /// Redo the last action. (`APPCOMMAND_REDO`) + Redo, + /// Undo the last action. (`APPCOMMAND_UNDO`) + Undo, + /// The Accept (Commit, OK) key. Accept current option or input method sequence conversion. + Accept, + /// Redo or repeat an action. + Again, + /// The Attention (Attn) key. + Attn, + Cancel, + /// Show the application’s context menu. + /// This key is commonly found between the right `Super` key and the right `Control` key. + ContextMenu, + /// The `Esc` key. This key was originally used to initiate an escape sequence, but is + /// now more generally used to exit or "escape" the current context, such as closing a dialog + /// or exiting full screen mode. + Escape, + Execute, + /// Open the Find dialog. (`APPCOMMAND_FIND`) + Find, + /// Open a help dialog or toggle display of help information. (`APPCOMMAND_HELP`, + /// `KEYCODE_HELP`) + Help, + /// Pause the current state or application (as appropriate). + /// + /// Note: Do not use this value for the `Pause` button on media controllers. Use `"MediaPause"` + /// instead. + Pause, + /// Play or resume the current state or application (as appropriate). + /// + /// Note: Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"` + /// instead. + Play, + /// The properties (Props) key. + Props, + Select, + /// The ZoomIn key. (`KEYCODE_ZOOM_IN`) + ZoomIn, + /// The ZoomOut key. (`KEYCODE_ZOOM_OUT`) + ZoomOut, + /// The Brightness Down key. Typically controls the display brightness. + /// (`KEYCODE_BRIGHTNESS_DOWN`) + BrightnessDown, + /// The Brightness Up key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_UP`) + BrightnessUp, + /// Toggle removable media to eject (open) and insert (close) state. (`KEYCODE_MEDIA_EJECT`) + Eject, + LogOff, + /// Toggle power state. (`KEYCODE_POWER`) + /// Note: Note: Some devices might not expose this key to the operating environment. + Power, + /// The `PowerOff` key. Sometime called `PowerDown`. + PowerOff, + /// Initiate print-screen function. + PrintScreen, + /// The Hibernate key. This key saves the current state of the computer to disk so that it can + /// be restored. The computer will then shutdown. + Hibernate, + /// The Standby key. This key turns off the display and places the computer into a low-power + /// mode without completely shutting down. It is sometimes labelled `Suspend` or `Sleep` key. + /// (`KEYCODE_SLEEP`) + Standby, + /// The WakeUp key. (`KEYCODE_WAKEUP`) + WakeUp, + /// Initiate the multi-candidate mode. + AllCandidates, + Alphanumeric, + /// Initiate the Code Input mode to allow characters to be entered by + /// their code points. + CodeInput, + /// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a + /// manner similar to a dead key, triggering a mode where subsequent key presses are combined + /// to produce a different character. + Compose, + /// Convert the current input method sequence. + Convert, + /// The Final Mode `Final` key used on some Asian keyboards, to enable the final mode for IMEs. + FinalMode, + /// Switch to the first character group. (ISO/IEC 9995) + GroupFirst, + /// Switch to the last character group. (ISO/IEC 9995) + GroupLast, + /// Switch to the next character group. (ISO/IEC 9995) + GroupNext, + /// Switch to the previous character group. (ISO/IEC 9995) + GroupPrevious, + /// Toggle between or cycle through input modes of IMEs. + ModeChange, + NextCandidate, + /// Accept current input method sequence without + /// conversion in IMEs. + NonConvert, + PreviousCandidate, + Process, + SingleCandidate, + /// Toggle between Hangul and English modes. + HangulMode, + HanjaMode, + JunjaMode, + /// The Eisu key. This key may close the IME, but its purpose is defined by the current IME. + /// (`KEYCODE_EISU`) + Eisu, + /// The (Half-Width) Characters key. + Hankaku, + /// The Hiragana (Japanese Kana characters) key. + Hiragana, + /// The Hiragana/Katakana toggle key. (`KEYCODE_KATAKANA_HIRAGANA`) + HiraganaKatakana, + /// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from + /// romaji mode). + KanaMode, + /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key + /// is typically used to switch to a hiragana keyboard for the purpose of converting input + /// into kanji. (`KEYCODE_KANA`) + KanjiMode, + /// The Katakana (Japanese Kana characters) key. + Katakana, + /// The Roman characters function key. + Romaji, + /// The Zenkaku (Full-Width) Characters key. + Zenkaku, + /// The Zenkaku/Hankaku (full-width/half-width) toggle key. (`KEYCODE_ZENKAKU_HANKAKU`) + ZenkakuHankaku, + /// General purpose virtual function key, as index 1. + Soft1, + /// General purpose virtual function key, as index 2. + Soft2, + /// General purpose virtual function key, as index 3. + Soft3, + /// General purpose virtual function key, as index 4. + Soft4, + /// Select next (numerically or logically) lower channel. (`APPCOMMAND_MEDIA_CHANNEL_DOWN`, + /// `KEYCODE_CHANNEL_DOWN`) + ChannelDown, + /// Select next (numerically or logically) higher channel. (`APPCOMMAND_MEDIA_CHANNEL_UP`, + /// `KEYCODE_CHANNEL_UP`) + ChannelUp, + /// Close the current document or message (Note: This doesn’t close the application). + /// (`APPCOMMAND_CLOSE`) + Close, + /// Open an editor to forward the current message. (`APPCOMMAND_FORWARD_MAIL`) + MailForward, + /// Open an editor to reply to the current message. (`APPCOMMAND_REPLY_TO_MAIL`) + MailReply, + /// Send the current message. (`APPCOMMAND_SEND_MAIL`) + MailSend, + /// Close the current media, for example to close a CD or DVD tray. (`KEYCODE_MEDIA_CLOSE`) + MediaClose, + /// Initiate or continue forward playback at faster than normal speed, or increase speed if + /// already fast forwarding. (`APPCOMMAND_MEDIA_FAST_FORWARD`, `KEYCODE_MEDIA_FAST_FORWARD`) + MediaFastForward, + /// Pause the currently playing media. (`APPCOMMAND_MEDIA_PAUSE`, `KEYCODE_MEDIA_PAUSE`) + /// + /// Note: Media controller devices should use this value rather than `"Pause"` for their pause + /// keys. + MediaPause, + /// Initiate or continue media playback at normal speed, if not currently playing at normal + /// speed. (`APPCOMMAND_MEDIA_PLAY`, `KEYCODE_MEDIA_PLAY`) + MediaPlay, + /// Toggle media between play and pause states. (`APPCOMMAND_MEDIA_PLAY_PAUSE`, + /// `KEYCODE_MEDIA_PLAY_PAUSE`) + MediaPlayPause, + /// Initiate or resume recording of currently selected media. (`APPCOMMAND_MEDIA_RECORD`, + /// `KEYCODE_MEDIA_RECORD`) + MediaRecord, + /// Initiate or continue reverse playback at faster than normal speed, or increase speed if + /// already rewinding. (`APPCOMMAND_MEDIA_REWIND`, `KEYCODE_MEDIA_REWIND`) + MediaRewind, + /// Stop media playing, pausing, forwarding, rewinding, or recording, if not already stopped. + /// (`APPCOMMAND_MEDIA_STOP`, `KEYCODE_MEDIA_STOP`) + MediaStop, + /// Seek to next media or program track. (`APPCOMMAND_MEDIA_NEXTTRACK`, `KEYCODE_MEDIA_NEXT`) + MediaTrackNext, + /// Seek to previous media or program track. (`APPCOMMAND_MEDIA_PREVIOUSTRACK`, + /// `KEYCODE_MEDIA_PREVIOUS`) + MediaTrackPrevious, + /// Open a new document or message. (`APPCOMMAND_NEW`) + New, + /// Open an existing document or message. (`APPCOMMAND_OPEN`) + Open, + /// Print the current document or message. (`APPCOMMAND_PRINT`) + Print, + /// Save the current document or message. (`APPCOMMAND_SAVE`) + Save, + /// Spellcheck the current document or selection. (`APPCOMMAND_SPELL_CHECK`) + SpellCheck, + /// The `11` key found on media numpads that + /// have buttons from `1` ... `12`. + Key11, + /// The `12` key found on media numpads that + /// have buttons from `1` ... `12`. + Key12, + /// Adjust audio balance leftward. (`VK_AUDIO_BALANCE_LEFT`) + AudioBalanceLeft, + /// Adjust audio balance rightward. (`VK_AUDIO_BALANCE_RIGHT`) + AudioBalanceRight, + /// Decrease audio bass boost or cycle down through bass boost states. (`APPCOMMAND_BASS_DOWN`, + /// `VK_BASS_BOOST_DOWN`) + AudioBassBoostDown, + /// Toggle bass boost on/off. (`APPCOMMAND_BASS_BOOST`) + AudioBassBoostToggle, + /// Increase audio bass boost or cycle up through bass boost states. (`APPCOMMAND_BASS_UP`, + /// `VK_BASS_BOOST_UP`) + AudioBassBoostUp, + /// Adjust audio fader towards front. (`VK_FADER_FRONT`) + AudioFaderFront, + /// Adjust audio fader towards rear. (`VK_FADER_REAR`) + AudioFaderRear, + /// Advance surround audio mode to next available mode. (`VK_SURROUND_MODE_NEXT`) + AudioSurroundModeNext, + /// Decrease treble. (`APPCOMMAND_TREBLE_DOWN`) + AudioTrebleDown, + /// Increase treble. (`APPCOMMAND_TREBLE_UP`) + AudioTrebleUp, + /// Decrease audio volume. (`APPCOMMAND_VOLUME_DOWN`, `KEYCODE_VOLUME_DOWN`) + AudioVolumeDown, + /// Increase audio volume. (`APPCOMMAND_VOLUME_UP`, `KEYCODE_VOLUME_UP`) + AudioVolumeUp, + /// Toggle between muted state and prior volume level. (`APPCOMMAND_VOLUME_MUTE`, + /// `KEYCODE_VOLUME_MUTE`) + AudioVolumeMute, + /// Toggle the microphone on/off. (`APPCOMMAND_MIC_ON_OFF_TOGGLE`) + MicrophoneToggle, + /// Decrease microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_DOWN`) + MicrophoneVolumeDown, + /// Increase microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_UP`) + MicrophoneVolumeUp, + /// Mute the microphone. (`APPCOMMAND_MICROPHONE_VOLUME_MUTE`, `KEYCODE_MUTE`) + MicrophoneVolumeMute, + /// Show correction list when a word is incorrectly identified. (`APPCOMMAND_CORRECTION_LIST`) + SpeechCorrectionList, + /// Toggle between dictation mode and command/control mode. + /// (`APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE`) + SpeechInputToggle, + /// The first generic "LaunchApplication" key. This is commonly associated with launching "My + /// Computer", and may have a computer symbol on the key. (`APPCOMMAND_LAUNCH_APP1`) + LaunchApplication1, + /// The second generic "LaunchApplication" key. This is commonly associated with launching + /// "Calculator", and may have a calculator symbol on the key. (`APPCOMMAND_LAUNCH_APP2`, + /// `KEYCODE_CALCULATOR`) + LaunchApplication2, + /// The "Calendar" key. (`KEYCODE_CALENDAR`) + LaunchCalendar, + /// The "Contacts" key. (`KEYCODE_CONTACTS`) + LaunchContacts, + /// The "Mail" key. (`APPCOMMAND_LAUNCH_MAIL`) + LaunchMail, + /// The "Media Player" key. (`APPCOMMAND_LAUNCH_MEDIA_SELECT`) + LaunchMediaPlayer, + LaunchMusicPlayer, + LaunchPhone, + LaunchScreenSaver, + LaunchSpreadsheet, + LaunchWebBrowser, + LaunchWebCam, + LaunchWordProcessor, + /// Navigate to previous content or page in current history. (`APPCOMMAND_BROWSER_BACKWARD`) + BrowserBack, + /// Open the list of browser favorites. (`APPCOMMAND_BROWSER_FAVORITES`) + BrowserFavorites, + /// Navigate to next content or page in current history. (`APPCOMMAND_BROWSER_FORWARD`) + BrowserForward, + /// Go to the user’s preferred home page. (`APPCOMMAND_BROWSER_HOME`) + BrowserHome, + /// Refresh the current page or content. (`APPCOMMAND_BROWSER_REFRESH`) + BrowserRefresh, + /// Call up the user’s preferred search page. (`APPCOMMAND_BROWSER_SEARCH`) + BrowserSearch, + /// Stop loading the current page or content. (`APPCOMMAND_BROWSER_STOP`) + BrowserStop, + /// The Application switch key, which provides a list of recent apps to switch between. + /// (`KEYCODE_APP_SWITCH`) + AppSwitch, + /// The Call key. (`KEYCODE_CALL`) + Call, + /// The Camera key. (`KEYCODE_CAMERA`) + Camera, + /// The Camera focus key. (`KEYCODE_FOCUS`) + CameraFocus, + /// The End Call key. (`KEYCODE_ENDCALL`) + EndCall, + /// The Back key. (`KEYCODE_BACK`) + GoBack, + /// The Home key, which goes to the phone’s main screen. (`KEYCODE_HOME`) + GoHome, + /// The Headset Hook key. (`KEYCODE_HEADSETHOOK`) + HeadsetHook, + LastNumberRedial, + /// The Notification key. (`KEYCODE_NOTIFICATION`) + Notification, + /// Toggle between manner mode state: silent, vibrate, ring, ... (`KEYCODE_MANNER_MODE`) + MannerMode, + VoiceDial, + /// Switch to viewing TV. (`KEYCODE_TV`) + TV, + /// TV 3D Mode. (`KEYCODE_3D_MODE`) + TV3DMode, + /// Toggle between antenna and cable input. (`KEYCODE_TV_ANTENNA_CABLE`) + TVAntennaCable, + /// Audio description. (`KEYCODE_TV_AUDIO_DESCRIPTION`) + TVAudioDescription, + /// Audio description mixing volume down. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN`) + TVAudioDescriptionMixDown, + /// Audio description mixing volume up. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP`) + TVAudioDescriptionMixUp, + /// Contents menu. (`KEYCODE_TV_CONTENTS_MENU`) + TVContentsMenu, + /// Contents menu. (`KEYCODE_TV_DATA_SERVICE`) + TVDataService, + /// Switch the input mode on an external TV. (`KEYCODE_TV_INPUT`) + TVInput, + /// Switch to component input #1. (`KEYCODE_TV_INPUT_COMPONENT_1`) + TVInputComponent1, + /// Switch to component input #2. (`KEYCODE_TV_INPUT_COMPONENT_2`) + TVInputComponent2, + /// Switch to composite input #1. (`KEYCODE_TV_INPUT_COMPOSITE_1`) + TVInputComposite1, + /// Switch to composite input #2. (`KEYCODE_TV_INPUT_COMPOSITE_2`) + TVInputComposite2, + /// Switch to HDMI input #1. (`KEYCODE_TV_INPUT_HDMI_1`) + TVInputHDMI1, + /// Switch to HDMI input #2. (`KEYCODE_TV_INPUT_HDMI_2`) + TVInputHDMI2, + /// Switch to HDMI input #3. (`KEYCODE_TV_INPUT_HDMI_3`) + TVInputHDMI3, + /// Switch to HDMI input #4. (`KEYCODE_TV_INPUT_HDMI_4`) + TVInputHDMI4, + /// Switch to VGA input #1. (`KEYCODE_TV_INPUT_VGA_1`) + TVInputVGA1, + /// Media context menu. (`KEYCODE_TV_MEDIA_CONTEXT_MENU`) + TVMediaContext, + /// Toggle network. (`KEYCODE_TV_NETWORK`) + TVNetwork, + /// Number entry. (`KEYCODE_TV_NUMBER_ENTRY`) + TVNumberEntry, + /// Toggle the power on an external TV. (`KEYCODE_TV_POWER`) + TVPower, + /// Radio. (`KEYCODE_TV_RADIO_SERVICE`) + TVRadioService, + /// Satellite. (`KEYCODE_TV_SATELLITE`) + TVSatellite, + /// Broadcast Satellite. (`KEYCODE_TV_SATELLITE_BS`) + TVSatelliteBS, + /// Communication Satellite. (`KEYCODE_TV_SATELLITE_CS`) + TVSatelliteCS, + /// Toggle between available satellites. (`KEYCODE_TV_SATELLITE_SERVICE`) + TVSatelliteToggle, + /// Analog Terrestrial. (`KEYCODE_TV_TERRESTRIAL_ANALOG`) + TVTerrestrialAnalog, + /// Digital Terrestrial. (`KEYCODE_TV_TERRESTRIAL_DIGITAL`) + TVTerrestrialDigital, + /// Timer programming. (`KEYCODE_TV_TIMER_PROGRAMMING`) + TVTimer, + /// Switch the input mode on an external AVR (audio/video receiver). (`KEYCODE_AVR_INPUT`) + AVRInput, + /// Toggle the power on an external AVR (audio/video receiver). (`KEYCODE_AVR_POWER`) + AVRPower, + /// General purpose color-coded media function key, as index 0 (red). (`VK_COLORED_KEY_0`, + /// `KEYCODE_PROG_RED`) + ColorF0Red, + /// General purpose color-coded media function key, as index 1 (green). (`VK_COLORED_KEY_1`, + /// `KEYCODE_PROG_GREEN`) + ColorF1Green, + /// General purpose color-coded media function key, as index 2 (yellow). (`VK_COLORED_KEY_2`, + /// `KEYCODE_PROG_YELLOW`) + ColorF2Yellow, + /// General purpose color-coded media function key, as index 3 (blue). (`VK_COLORED_KEY_3`, + /// `KEYCODE_PROG_BLUE`) + ColorF3Blue, + /// General purpose color-coded media function key, as index 4 (grey). (`VK_COLORED_KEY_4`) + ColorF4Grey, + /// General purpose color-coded media function key, as index 5 (brown). (`VK_COLORED_KEY_5`) + ColorF5Brown, + /// Toggle the display of Closed Captions. (`VK_CC`, `KEYCODE_CAPTIONS`) + ClosedCaptionToggle, + /// Adjust brightness of device, by toggling between or cycling through states. (`VK_DIMMER`) + Dimmer, + /// Swap video sources. (`VK_DISPLAY_SWAP`) + DisplaySwap, + /// Select Digital Video Rrecorder. (`KEYCODE_DVR`) + DVR, + /// Exit the current application. (`VK_EXIT`) + Exit, + /// Clear program or content stored as favorite 0. (`VK_CLEAR_FAVORITE_0`) + FavoriteClear0, + /// Clear program or content stored as favorite 1. (`VK_CLEAR_FAVORITE_1`) + FavoriteClear1, + /// Clear program or content stored as favorite 2. (`VK_CLEAR_FAVORITE_2`) + FavoriteClear2, + /// Clear program or content stored as favorite 3. (`VK_CLEAR_FAVORITE_3`) + FavoriteClear3, + /// Select (recall) program or content stored as favorite 0. (`VK_RECALL_FAVORITE_0`) + FavoriteRecall0, + /// Select (recall) program or content stored as favorite 1. (`VK_RECALL_FAVORITE_1`) + FavoriteRecall1, + /// Select (recall) program or content stored as favorite 2. (`VK_RECALL_FAVORITE_2`) + FavoriteRecall2, + /// Select (recall) program or content stored as favorite 3. (`VK_RECALL_FAVORITE_3`) + FavoriteRecall3, + /// Store current program or content as favorite 0. (`VK_STORE_FAVORITE_0`) + FavoriteStore0, + /// Store current program or content as favorite 1. (`VK_STORE_FAVORITE_1`) + FavoriteStore1, + /// Store current program or content as favorite 2. (`VK_STORE_FAVORITE_2`) + FavoriteStore2, + /// Store current program or content as favorite 3. (`VK_STORE_FAVORITE_3`) + FavoriteStore3, + /// Toggle display of program or content guide. (`VK_GUIDE`, `KEYCODE_GUIDE`) + Guide, + /// If guide is active and displayed, then display next day’s content. (`VK_NEXT_DAY`) + GuideNextDay, + /// If guide is active and displayed, then display previous day’s content. (`VK_PREV_DAY`) + GuidePreviousDay, + /// Toggle display of information about currently selected context or media. (`VK_INFO`, + /// `KEYCODE_INFO`) + Info, + /// Toggle instant replay. (`VK_INSTANT_REPLAY`) + InstantReplay, + /// Launch linked content, if available and appropriate. (`VK_LINK`) + Link, + /// List the current program. (`VK_LIST`) + ListProgram, + /// Toggle display listing of currently available live content or programs. (`VK_LIVE`) + LiveContent, + /// Lock or unlock current content or program. (`VK_LOCK`) + Lock, + /// Show a list of media applications: audio/video players and image viewers. (`VK_APPS`) + /// + /// Note: Do not confuse this key value with the Windows' `VK_APPS` / `VK_CONTEXT_MENU` key, + /// which is encoded as `"ContextMenu"`. + MediaApps, + /// Audio track key. (`KEYCODE_MEDIA_AUDIO_TRACK`) + MediaAudioTrack, + /// Select previously selected channel or media. (`VK_LAST`, `KEYCODE_LAST_CHANNEL`) + MediaLast, + /// Skip backward to next content or program. (`KEYCODE_MEDIA_SKIP_BACKWARD`) + MediaSkipBackward, + /// Skip forward to next content or program. (`VK_SKIP`, `KEYCODE_MEDIA_SKIP_FORWARD`) + MediaSkipForward, + /// Step backward to next content or program. (`KEYCODE_MEDIA_STEP_BACKWARD`) + MediaStepBackward, + /// Step forward to next content or program. (`KEYCODE_MEDIA_STEP_FORWARD`) + MediaStepForward, + /// Media top menu. (`KEYCODE_MEDIA_TOP_MENU`) + MediaTopMenu, + /// Navigate in. (`KEYCODE_NAVIGATE_IN`) + NavigateIn, + /// Navigate to next key. (`KEYCODE_NAVIGATE_NEXT`) + NavigateNext, + /// Navigate out. (`KEYCODE_NAVIGATE_OUT`) + NavigateOut, + /// Navigate to previous key. (`KEYCODE_NAVIGATE_PREVIOUS`) + NavigatePrevious, + /// Cycle to next favorite channel (in favorites list). (`VK_NEXT_FAVORITE_CHANNEL`) + NextFavoriteChannel, + /// Cycle to next user profile (if there are multiple user profiles). (`VK_USER`) + NextUserProfile, + /// Access on-demand content or programs. (`VK_ON_DEMAND`) + OnDemand, + /// Pairing key to pair devices. (`KEYCODE_PAIRING`) + Pairing, + /// Move picture-in-picture window down. (`VK_PINP_DOWN`) + PinPDown, + /// Move picture-in-picture window. (`VK_PINP_MOVE`) + PinPMove, + /// Toggle display of picture-in-picture window. (`VK_PINP_TOGGLE`) + PinPToggle, + /// Move picture-in-picture window up. (`VK_PINP_UP`) + PinPUp, + /// Decrease media playback speed. (`VK_PLAY_SPEED_DOWN`) + PlaySpeedDown, + /// Reset playback to normal speed. (`VK_PLAY_SPEED_RESET`) + PlaySpeedReset, + /// Increase media playback speed. (`VK_PLAY_SPEED_UP`) + PlaySpeedUp, + /// Toggle random media or content shuffle mode. (`VK_RANDOM_TOGGLE`) + RandomToggle, + /// Not a physical key, but this key code is sent when the remote control battery is low. + /// (`VK_RC_LOW_BATTERY`) + RcLowBattery, + /// Toggle or cycle between media recording speeds. (`VK_RECORD_SPEED_NEXT`) + RecordSpeedNext, + /// Toggle RF (radio frequency) input bypass mode (pass RF input directly to the RF output). + /// (`VK_RF_BYPASS`) + RfBypass, + /// Toggle scan channels mode. (`VK_SCAN_CHANNELS_TOGGLE`) + ScanChannelsToggle, + /// Advance display screen mode to next available mode. (`VK_SCREEN_MODE_NEXT`) + ScreenModeNext, + /// Toggle display of device settings screen. (`VK_SETTINGS`, `KEYCODE_SETTINGS`) + Settings, + /// Toggle split screen mode. (`VK_SPLIT_SCREEN_TOGGLE`) + SplitScreenToggle, + /// Switch the input mode on an external STB (set top box). (`KEYCODE_STB_INPUT`) + STBInput, + /// Toggle the power on an external STB (set top box). (`KEYCODE_STB_POWER`) + STBPower, + /// Toggle display of subtitles, if available. (`VK_SUBTITLE`) + Subtitle, + /// Toggle display of teletext, if available (`VK_TELETEXT`, `KEYCODE_TV_TELETEXT`). + Teletext, + /// Advance video mode to next available mode. (`VK_VIDEO_MODE_NEXT`) + VideoModeNext, + /// Cause device to identify itself in some manner, e.g., audibly or visibly. (`VK_WINK`) + Wink, + /// Toggle between full-screen and scaled content, or alter magnification level. (`VK_ZOOM`, + /// `KEYCODE_TV_ZOOM_MODE`) + ZoomToggle, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F1, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F2, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F3, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F4, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F5, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F6, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F7, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F8, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F9, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F10, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F11, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F12, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F13, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F14, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F15, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F16, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F17, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F18, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F19, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F20, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F21, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F22, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F23, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F24, + /// General-purpose function key. + F25, + /// General-purpose function key. + F26, + /// General-purpose function key. + F27, + /// General-purpose function key. + F28, + /// General-purpose function key. + F29, + /// General-purpose function key. + F30, + /// General-purpose function key. + F31, + /// General-purpose function key. + F32, + /// General-purpose function key. + F33, + /// General-purpose function key. + F34, + /// General-purpose function key. + F35, } -impl Drop for XkbContext { - fn drop(&mut self) { - unsafe { (XKBH.xkb_context_unref)(self.context.as_ptr()) } - } +/// Key represents the meaning of a keypress. +/// +/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with +/// additions: +/// - All simple variants are wrapped under the `Named` variant +/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`. +/// - The `Dead` variant here, can specify the character which is inserted when pressing the +/// dead-key twice. +/// +/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Key { + /// A simple (unparameterised) action + Named(NamedKey), + + /// A key string that corresponds to the character typed by the user, taking into account the + /// user’s current locale setting, and any system-level keyboard mapping overrides that are in + /// effect. + Character(Str), + + /// This variant is used when the key cannot be translated to any other variant. + /// + /// The native key is provided (if available) in order to allow the user to specify keybindings + /// for keys which are not defined by this API, mainly through some sort of UI. + Unidentified(NativeKey), + + /// Contains the text representation of the dead-key when available. + /// + /// ## Platform-specific + /// - **Web:** Always contains `None` + Dead(Option), } -impl Deref for XkbContext { - type Target = NonNull; - fn deref(&self) -> &Self::Target { - &self.context +impl From for Key { + #[inline] + fn from(action: NamedKey) -> Self { + Key::Named(action) } } -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 } +impl From for Key { + #[inline] + fn from(code: NativeKey) -> Self { + Key::Unidentified(code) } } -#[derive(Debug)] -pub struct XkbState { - state: NonNull, - modifiers: ModifiersState, +impl PartialEq for Key { + #[inline] + fn eq(&self, rhs: &NamedKey) -> bool { + match self { + Key::Named(ref a) => a == rhs, + _ => false, + } + } } -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)) +impl> PartialEq for Key { + #[inline] + fn eq(&self, rhs: &str) -> bool { + match self { + Key::Character(ref s) => s == rhs, + _ => false, + } } +} - fn new_inner(state: NonNull) -> Self { - let modifiers = ModifiersState::default(); - let mut this = Self { state, modifiers }; - this.reload_modifiers(); - this +impl> PartialEq<&str> for Key { + #[inline] + fn eq(&self, rhs: &&str) -> bool { + self == *rhs } - // 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 +} + +impl PartialEq for Key { + #[inline] + fn eq(&self, rhs: &NativeKey) -> bool { + match self { + Key::Unidentified(ref code) => code == rhs, + _ => false, } } - 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); - 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, -} - -#[derive(Debug)] -pub struct XkbComposeTable { - table: NonNull, -} - -impl XkbComposeTable { - pub fn new(context: &XkbContext) -> Option { - 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 table = unsafe { - (XKBCH.xkb_compose_table_new_from_locale)( - context.as_ptr(), - locale.as_ptr(), - xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS, - ) - }; - - let table = NonNull::new(table)?; - Some(Self { table }) +impl PartialEq> for NativeKey { + #[inline] + fn eq(&self, rhs: &Key) -> bool { + rhs == self } +} - /// Create new state with the given compose table. - pub fn new_state(&self) -> Option { - let state = unsafe { - (XKBCH.xkb_compose_state_new)( - self.table.as_ptr(), - xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, - ) - }; - - let state = NonNull::new(state)?; - Some(XkbComposeState { state }) +impl Key { + /// Convert `Key::Character(SmolStr)` to `Key::Character(&str)` so you can more easily match on + /// `Key`. All other variants remain unchanged. + pub fn as_ref(&self) -> Key<&str> { + match self { + Key::Named(a) => Key::Named(*a), + Key::Character(ch) => Key::Character(ch.as_str()), + Key::Dead(d) => Key::Dead(*d), + Key::Unidentified(u) => Key::Unidentified(u.clone()), + } } } -impl Deref for XkbComposeTable { - type Target = NonNull; - - fn deref(&self) -> &Self::Target { - &self.table +impl NamedKey { + /// Convert an action to its approximate textual equivalent. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(web_platform)] + /// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)] + /// # fn main() { + /// use winit::keyboard::NamedKey; + /// + /// assert_eq!(NamedKey::Enter.to_text(), Some("\r")); + /// assert_eq!(NamedKey::F20.to_text(), None); + /// # } + /// ``` + pub fn to_text(&self) -> Option<&str> { + match self { + NamedKey::Enter => Some("\r"), + NamedKey::Backspace => Some("\x08"), + NamedKey::Tab => Some("\t"), + NamedKey::Space => Some(" "), + NamedKey::Escape => Some("\x1b"), + _ => None, + } } } -impl Drop for XkbComposeTable { - fn drop(&mut self) { - unsafe { - (XKBCH.xkb_compose_table_unref)(self.table.as_ptr()); +impl Key { + /// Convert a key to its approximate textual equivalent. + /// + /// # Examples + /// + /// ``` + /// # #[cfg(web_platform)] + /// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + /// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)] + /// # fn main() { + /// use winit::keyboard::{Key, NamedKey}; + /// + /// assert_eq!(Key::Character("a".into()).to_text(), Some("a")); + /// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r")); + /// assert_eq!(Key::Named(NamedKey::F20).to_text(), None); + /// # } + /// ``` + pub fn to_text(&self) -> Option<&str> { + match self { + Key::Named(action) => action.to_text(), + Key::Character(ch) => Some(ch.as_str()), + _ => None, } } } -#[derive(Debug)] -pub struct XkbComposeState { - state: NonNull, -} - -// NOTE: This is track_caller so we can have more informative line numbers when logging -#[track_caller] -fn byte_slice_to_smol_str(bytes: &[u8]) -> Option { - std::str::from_utf8(bytes) - .map(SmolStr::new) - .map_err(|e| { - log::warn!( - "UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", - bytes - ) - }) - .ok() -} - -/// Shared logic for constructing a string with `xkb_compose_state_get_utf8` and -/// `xkb_state_key_get_utf8`. -fn make_string_with(scratch_buffer: &mut Vec, mut f: F) -> Option -where - F: FnMut(*mut c_char, usize) -> i32, -{ - let size = f(ptr::null_mut(), 0); - if size == 0 { - return None; - } - let size = usize::try_from(size).unwrap(); - scratch_buffer.clear(); - // The allocated buffer must include space for the null-terminator. - scratch_buffer.reserve(size + 1); - unsafe { - let written = f( - scratch_buffer.as_mut_ptr().cast(), - scratch_buffer.capacity(), - ); - if usize::try_from(written).unwrap() != size { - // This will likely never happen. - return None; - } - scratch_buffer.set_len(size); - }; +/// The location of the key on the keyboard. +/// +/// Certain physical keys on the keyboard can have the same value, but are in different locations. +/// For instance, the Shift key can be on the left or right side of the keyboard, or the number +/// keys can be above the letters or on the numpad. This enum allows the user to differentiate +/// them. +/// +/// See the documentation for the [`location`] field on the [`KeyEvent`] struct for more +/// information. +/// +/// [`location`]: ../event/struct.KeyEvent.html#structfield.location +/// [`KeyEvent`]: crate::event::KeyEvent +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum KeyLocation { + /// The key is in its "normal" location on the keyboard. + /// + /// For instance, the "1" key above the "Q" key on a QWERTY keyboard will use this location. + /// This invariant is also returned when the location of the key cannot be identified. + /// + /// ![Standard 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_standard_1_key.svg) + /// + /// + /// For image attribution, see the + /// + /// ATTRIBUTION.md + /// + /// file. + /// + Standard, + + /// The key is on the left side of the keyboard. + /// + /// For instance, the left Shift key below the Caps Lock key on a QWERTY keyboard will use this + /// location. + /// + /// ![Left Shift key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_left_shift_key.svg) + /// + /// + /// For image attribution, see the + /// + /// ATTRIBUTION.md + /// + /// file. + /// + Left, - byte_slice_to_smol_str(scratch_buffer) + /// The key is on the right side of the keyboard. + /// + /// For instance, the right Shift key below the Enter key on a QWERTY keyboard will use this + /// location. + /// + /// ![Right Shift key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_right_shift_key.svg) + /// + /// + /// For image attribution, see the + /// + /// ATTRIBUTION.md + /// + /// file. + /// + Right, + + /// The key is on the numpad. + /// + /// For instance, the "1" key on the numpad will use this location. + /// + /// ![Numpad 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_numpad_1_key.svg) + /// + /// + /// For image attribution, see the + /// + /// ATTRIBUTION.md + /// + /// file. + /// + Numpad, } -impl XkbComposeState { - pub fn get_string(&mut self, scratch_buffer: &mut Vec) -> Option { - make_string_with(scratch_buffer, |ptr, len| unsafe { - (XKBCH.xkb_compose_state_get_utf8)(self.state.as_ptr(), ptr, len) - }) +bitflags! { + /// Represents the current state of the keyboard modifiers + /// + /// Each flag represents a modifier and is set if this modifier is active. + #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct ModifiersState: u32 { + /// The "shift" key. + const SHIFT = 0b100; + /// The "control" key. + const CONTROL = 0b100 << 3; + /// The "alt" key. + const ALT = 0b100 << 6; + /// This is the "windows" key on PC and "command" key on Mac. + const SUPER = 0b100 << 9; } +} - #[inline] - pub fn feed(&mut self, keysym: xkb_keysym_t) -> ComposeStatus { - let feed_result = unsafe { (XKBCH.xkb_compose_state_feed)(self.state.as_ptr(), keysym) }; - match feed_result { - xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED => ComposeStatus::Ignored, - xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED => { - ComposeStatus::Accepted(self.status()) - } - } +impl ModifiersState { + /// Returns `true` if the shift key is pressed. + pub fn shift_key(&self) -> bool { + self.intersects(Self::SHIFT) } - #[inline] - pub fn reset(&mut self) { - unsafe { - (XKBCH.xkb_compose_state_reset)(self.state.as_ptr()); - } + /// Returns `true` if the control key is pressed. + pub fn control_key(&self) -> bool { + self.intersects(Self::CONTROL) } - #[inline] - pub fn status(&mut self) -> xkb_compose_status { - unsafe { (XKBCH.xkb_compose_state_get_status)(self.state.as_ptr()) } + /// Returns `true` if the alt key is pressed. + pub fn alt_key(&self) -> bool { + self.intersects(Self::ALT) } -} -impl Drop for XkbComposeState { - fn drop(&mut self) { - unsafe { - (XKBCH.xkb_compose_state_unref)(self.state.as_ptr()); - }; + /// Returns `true` if the super key is pressed. + pub fn super_key(&self) -> bool { + self.intersects(Self::SUPER) } } -#[derive(Copy, Clone, Debug)] -pub enum ComposeStatus { - Accepted(xkb_compose_status), - Ignored, - None, +/// The state of the particular modifiers key. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub enum ModifiersKeyState { + /// The particular key is pressed. + Pressed, + /// The state of the key is unknown. + #[default] + Unknown, +} + +// NOTE: the exact modifier key is not used to represent modifiers state in the +// first place due to a fact that modifiers state could be changed without any +// key being pressed and on some platforms like Wayland/X11 which key resulted +// in modifiers change is hidden, also, not that it really matters. +// +// The reason this API is even exposed is mostly to provide a way for users +// to treat modifiers differently based on their position, which is required +// on macOS due to their AltGr/Option situation. +bitflags! { + #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub(crate) struct ModifiersKeys: u8 { + const LSHIFT = 0b0000_0001; + const RSHIFT = 0b0000_0010; + const LCONTROL = 0b0000_0100; + const RCONTROL = 0b0000_1000; + const LALT = 0b0001_0000; + const RALT = 0b0010_0000; + const LSUPER = 0b0100_0000; + const RSUPER = 0b1000_0000; + } } + + diff --git a/layershellev/src/keymap.rs b/layershellev/src/keymap.rs new file mode 100644 index 0000000..349837a --- /dev/null +++ b/layershellev/src/keymap.rs @@ -0,0 +1,922 @@ +//! XKB keymap. + +use std::ffi::c_char; +use std::ptr::NonNull; + +use xkb::XKB_MOD_INVALID; +use xkbcommon_dl::{self as xkb, xkb_keymap, xkb_mod_index_t}; + +use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey}; +use crate::xkb_keyboard::XKBH; + +/// Map the raw X11-style keycode to the `KeyCode` enum. +/// +/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses. +pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey { + scancode_to_physicalkey(keycode.saturating_sub(8)) +} + +/// Map the linux scancode to Keycode. +/// +/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode. +pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { + // The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as + // libxkbcommon's documentation seems to suggest that the keycode values we're interested in + // are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes, + // I can only hope they agree on what the keycodes mean. + // + // Some of the keycodes are likely superfluous for our purposes, and some are ones which are + // difficult to test the correctness of, or discover the purpose of. Because of this, they've + // either been commented out here, or not included at all. + PhysicalKey::Code(match scancode { + 0 => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(0)), + 1 => KeyCode::Escape, + 2 => KeyCode::Digit1, + 3 => KeyCode::Digit2, + 4 => KeyCode::Digit3, + 5 => KeyCode::Digit4, + 6 => KeyCode::Digit5, + 7 => KeyCode::Digit6, + 8 => KeyCode::Digit7, + 9 => KeyCode::Digit8, + 10 => KeyCode::Digit9, + 11 => KeyCode::Digit0, + 12 => KeyCode::Minus, + 13 => KeyCode::Equal, + 14 => KeyCode::Backspace, + 15 => KeyCode::Tab, + 16 => KeyCode::KeyQ, + 17 => KeyCode::KeyW, + 18 => KeyCode::KeyE, + 19 => KeyCode::KeyR, + 20 => KeyCode::KeyT, + 21 => KeyCode::KeyY, + 22 => KeyCode::KeyU, + 23 => KeyCode::KeyI, + 24 => KeyCode::KeyO, + 25 => KeyCode::KeyP, + 26 => KeyCode::BracketLeft, + 27 => KeyCode::BracketRight, + 28 => KeyCode::Enter, + 29 => KeyCode::ControlLeft, + 30 => KeyCode::KeyA, + 31 => KeyCode::KeyS, + 32 => KeyCode::KeyD, + 33 => KeyCode::KeyF, + 34 => KeyCode::KeyG, + 35 => KeyCode::KeyH, + 36 => KeyCode::KeyJ, + 37 => KeyCode::KeyK, + 38 => KeyCode::KeyL, + 39 => KeyCode::Semicolon, + 40 => KeyCode::Quote, + 41 => KeyCode::Backquote, + 42 => KeyCode::ShiftLeft, + 43 => KeyCode::Backslash, + 44 => KeyCode::KeyZ, + 45 => KeyCode::KeyX, + 46 => KeyCode::KeyC, + 47 => KeyCode::KeyV, + 48 => KeyCode::KeyB, + 49 => KeyCode::KeyN, + 50 => KeyCode::KeyM, + 51 => KeyCode::Comma, + 52 => KeyCode::Period, + 53 => KeyCode::Slash, + 54 => KeyCode::ShiftRight, + 55 => KeyCode::NumpadMultiply, + 56 => KeyCode::AltLeft, + 57 => KeyCode::Space, + 58 => KeyCode::CapsLock, + 59 => KeyCode::F1, + 60 => KeyCode::F2, + 61 => KeyCode::F3, + 62 => KeyCode::F4, + 63 => KeyCode::F5, + 64 => KeyCode::F6, + 65 => KeyCode::F7, + 66 => KeyCode::F8, + 67 => KeyCode::F9, + 68 => KeyCode::F10, + 69 => KeyCode::NumLock, + 70 => KeyCode::ScrollLock, + 71 => KeyCode::Numpad7, + 72 => KeyCode::Numpad8, + 73 => KeyCode::Numpad9, + 74 => KeyCode::NumpadSubtract, + 75 => KeyCode::Numpad4, + 76 => KeyCode::Numpad5, + 77 => KeyCode::Numpad6, + 78 => KeyCode::NumpadAdd, + 79 => KeyCode::Numpad1, + 80 => KeyCode::Numpad2, + 81 => KeyCode::Numpad3, + 82 => KeyCode::Numpad0, + 83 => KeyCode::NumpadDecimal, + 85 => KeyCode::Lang5, + 86 => KeyCode::IntlBackslash, + 87 => KeyCode::F11, + 88 => KeyCode::F12, + 89 => KeyCode::IntlRo, + 90 => KeyCode::Lang3, + 91 => KeyCode::Lang4, + 92 => KeyCode::Convert, + 93 => KeyCode::KanaMode, + 94 => KeyCode::NonConvert, + // 95 => KeyCode::KPJPCOMMA, + 96 => KeyCode::NumpadEnter, + 97 => KeyCode::ControlRight, + 98 => KeyCode::NumpadDivide, + 99 => KeyCode::PrintScreen, + 100 => KeyCode::AltRight, + // 101 => KeyCode::LINEFEED, + 102 => KeyCode::Home, + 103 => KeyCode::ArrowUp, + 104 => KeyCode::PageUp, + 105 => KeyCode::ArrowLeft, + 106 => KeyCode::ArrowRight, + 107 => KeyCode::End, + 108 => KeyCode::ArrowDown, + 109 => KeyCode::PageDown, + 110 => KeyCode::Insert, + 111 => KeyCode::Delete, + // 112 => KeyCode::MACRO, + 113 => KeyCode::AudioVolumeMute, + 114 => KeyCode::AudioVolumeDown, + 115 => KeyCode::AudioVolumeUp, + // 116 => KeyCode::POWER, + 117 => KeyCode::NumpadEqual, + // 118 => KeyCode::KPPLUSMINUS, + 119 => KeyCode::Pause, + // 120 => KeyCode::SCALE, + 121 => KeyCode::NumpadComma, + 122 => KeyCode::Lang1, + 123 => KeyCode::Lang2, + 124 => KeyCode::IntlYen, + 125 => KeyCode::SuperLeft, + 126 => KeyCode::SuperRight, + 127 => KeyCode::ContextMenu, + // 128 => KeyCode::STOP, + // 129 => KeyCode::AGAIN, + // 130 => KeyCode::PROPS, + // 131 => KeyCode::UNDO, + // 132 => KeyCode::FRONT, + // 133 => KeyCode::COPY, + // 134 => KeyCode::OPEN, + // 135 => KeyCode::PASTE, + // 136 => KeyCode::FIND, + // 137 => KeyCode::CUT, + // 138 => KeyCode::HELP, + // 139 => KeyCode::MENU, + // 140 => KeyCode::CALC, + // 141 => KeyCode::SETUP, + // 142 => KeyCode::SLEEP, + // 143 => KeyCode::WAKEUP, + // 144 => KeyCode::FILE, + // 145 => KeyCode::SENDFILE, + // 146 => KeyCode::DELETEFILE, + // 147 => KeyCode::XFER, + // 148 => KeyCode::PROG1, + // 149 => KeyCode::PROG2, + // 150 => KeyCode::WWW, + // 151 => KeyCode::MSDOS, + // 152 => KeyCode::COFFEE, + // 153 => KeyCode::ROTATE_DISPLAY, + // 154 => KeyCode::CYCLEWINDOWS, + // 155 => KeyCode::MAIL, + // 156 => KeyCode::BOOKMARKS, + // 157 => KeyCode::COMPUTER, + // 158 => KeyCode::BACK, + // 159 => KeyCode::FORWARD, + // 160 => KeyCode::CLOSECD, + // 161 => KeyCode::EJECTCD, + // 162 => KeyCode::EJECTCLOSECD, + 163 => KeyCode::MediaTrackNext, + 164 => KeyCode::MediaPlayPause, + 165 => KeyCode::MediaTrackPrevious, + 166 => KeyCode::MediaStop, + // 167 => KeyCode::RECORD, + // 168 => KeyCode::REWIND, + // 169 => KeyCode::PHONE, + // 170 => KeyCode::ISO, + // 171 => KeyCode::CONFIG, + // 172 => KeyCode::HOMEPAGE, + // 173 => KeyCode::REFRESH, + // 174 => KeyCode::EXIT, + // 175 => KeyCode::MOVE, + // 176 => KeyCode::EDIT, + // 177 => KeyCode::SCROLLUP, + // 178 => KeyCode::SCROLLDOWN, + // 179 => KeyCode::KPLEFTPAREN, + // 180 => KeyCode::KPRIGHTPAREN, + // 181 => KeyCode::NEW, + // 182 => KeyCode::REDO, + 183 => KeyCode::F13, + 184 => KeyCode::F14, + 185 => KeyCode::F15, + 186 => KeyCode::F16, + 187 => KeyCode::F17, + 188 => KeyCode::F18, + 189 => KeyCode::F19, + 190 => KeyCode::F20, + 191 => KeyCode::F21, + 192 => KeyCode::F22, + 193 => KeyCode::F23, + 194 => KeyCode::F24, + // 200 => KeyCode::PLAYCD, + // 201 => KeyCode::PAUSECD, + // 202 => KeyCode::PROG3, + // 203 => KeyCode::PROG4, + // 204 => KeyCode::DASHBOARD, + // 205 => KeyCode::SUSPEND, + // 206 => KeyCode::CLOSE, + // 207 => KeyCode::PLAY, + // 208 => KeyCode::FASTFORWARD, + // 209 => KeyCode::BASSBOOST, + // 210 => KeyCode::PRINT, + // 211 => KeyCode::HP, + // 212 => KeyCode::CAMERA, + // 213 => KeyCode::SOUND, + // 214 => KeyCode::QUESTION, + // 215 => KeyCode::EMAIL, + // 216 => KeyCode::CHAT, + // 217 => KeyCode::SEARCH, + // 218 => KeyCode::CONNECT, + // 219 => KeyCode::FINANCE, + // 220 => KeyCode::SPORT, + // 221 => KeyCode::SHOP, + // 222 => KeyCode::ALTERASE, + // 223 => KeyCode::CANCEL, + // 224 => KeyCode::BRIGHTNESSDOW, + // 225 => KeyCode::BRIGHTNESSU, + // 226 => KeyCode::MEDIA, + // 227 => KeyCode::SWITCHVIDEOMODE, + // 228 => KeyCode::KBDILLUMTOGGLE, + // 229 => KeyCode::KBDILLUMDOWN, + // 230 => KeyCode::KBDILLUMUP, + // 231 => KeyCode::SEND, + // 232 => KeyCode::REPLY, + // 233 => KeyCode::FORWARDMAIL, + // 234 => KeyCode::SAVE, + // 235 => KeyCode::DOCUMENTS, + // 236 => KeyCode::BATTERY, + // 237 => KeyCode::BLUETOOTH, + // 238 => KeyCode::WLAN, + // 239 => KeyCode::UWB, + 240 => return PhysicalKey::Unidentified(NativeKeyCode::Unidentified), + // 241 => KeyCode::VIDEO_NEXT, + // 242 => KeyCode::VIDEO_PREV, + // 243 => KeyCode::BRIGHTNESS_CYCLE, + // 244 => KeyCode::BRIGHTNESS_AUTO, + // 245 => KeyCode::DISPLAY_OFF, + // 246 => KeyCode::WWAN, + // 247 => KeyCode::RFKILL, + // 248 => KeyCode::KEY_MICMUTE, + _ => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(scancode)), + }) +} + +pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option { + let code = match key { + PhysicalKey::Code(code) => code, + PhysicalKey::Unidentified(code) => { + return match code { + NativeKeyCode::Unidentified => Some(240), + NativeKeyCode::Xkb(raw) => Some(raw), + _ => None, + }; + } + }; + + match code { + KeyCode::Escape => Some(1), + KeyCode::Digit1 => Some(2), + KeyCode::Digit2 => Some(3), + KeyCode::Digit3 => Some(4), + KeyCode::Digit4 => Some(5), + KeyCode::Digit5 => Some(6), + KeyCode::Digit6 => Some(7), + KeyCode::Digit7 => Some(8), + KeyCode::Digit8 => Some(9), + KeyCode::Digit9 => Some(10), + KeyCode::Digit0 => Some(11), + KeyCode::Minus => Some(12), + KeyCode::Equal => Some(13), + KeyCode::Backspace => Some(14), + KeyCode::Tab => Some(15), + KeyCode::KeyQ => Some(16), + KeyCode::KeyW => Some(17), + KeyCode::KeyE => Some(18), + KeyCode::KeyR => Some(19), + KeyCode::KeyT => Some(20), + KeyCode::KeyY => Some(21), + KeyCode::KeyU => Some(22), + KeyCode::KeyI => Some(23), + KeyCode::KeyO => Some(24), + KeyCode::KeyP => Some(25), + KeyCode::BracketLeft => Some(26), + KeyCode::BracketRight => Some(27), + KeyCode::Enter => Some(28), + KeyCode::ControlLeft => Some(29), + KeyCode::KeyA => Some(30), + KeyCode::KeyS => Some(31), + KeyCode::KeyD => Some(32), + KeyCode::KeyF => Some(33), + KeyCode::KeyG => Some(34), + KeyCode::KeyH => Some(35), + KeyCode::KeyJ => Some(36), + KeyCode::KeyK => Some(37), + KeyCode::KeyL => Some(38), + KeyCode::Semicolon => Some(39), + KeyCode::Quote => Some(40), + KeyCode::Backquote => Some(41), + KeyCode::ShiftLeft => Some(42), + KeyCode::Backslash => Some(43), + KeyCode::KeyZ => Some(44), + KeyCode::KeyX => Some(45), + KeyCode::KeyC => Some(46), + KeyCode::KeyV => Some(47), + KeyCode::KeyB => Some(48), + KeyCode::KeyN => Some(49), + KeyCode::KeyM => Some(50), + KeyCode::Comma => Some(51), + KeyCode::Period => Some(52), + KeyCode::Slash => Some(53), + KeyCode::ShiftRight => Some(54), + KeyCode::NumpadMultiply => Some(55), + KeyCode::AltLeft => Some(56), + KeyCode::Space => Some(57), + KeyCode::CapsLock => Some(58), + KeyCode::F1 => Some(59), + KeyCode::F2 => Some(60), + KeyCode::F3 => Some(61), + KeyCode::F4 => Some(62), + KeyCode::F5 => Some(63), + KeyCode::F6 => Some(64), + KeyCode::F7 => Some(65), + KeyCode::F8 => Some(66), + KeyCode::F9 => Some(67), + KeyCode::F10 => Some(68), + KeyCode::NumLock => Some(69), + KeyCode::ScrollLock => Some(70), + KeyCode::Numpad7 => Some(71), + KeyCode::Numpad8 => Some(72), + KeyCode::Numpad9 => Some(73), + KeyCode::NumpadSubtract => Some(74), + KeyCode::Numpad4 => Some(75), + KeyCode::Numpad5 => Some(76), + KeyCode::Numpad6 => Some(77), + KeyCode::NumpadAdd => Some(78), + KeyCode::Numpad1 => Some(79), + KeyCode::Numpad2 => Some(80), + KeyCode::Numpad3 => Some(81), + KeyCode::Numpad0 => Some(82), + KeyCode::NumpadDecimal => Some(83), + KeyCode::Lang5 => Some(85), + KeyCode::IntlBackslash => Some(86), + KeyCode::F11 => Some(87), + KeyCode::F12 => Some(88), + KeyCode::IntlRo => Some(89), + KeyCode::Lang3 => Some(90), + KeyCode::Lang4 => Some(91), + KeyCode::Convert => Some(92), + KeyCode::KanaMode => Some(93), + KeyCode::NonConvert => Some(94), + KeyCode::NumpadEnter => Some(96), + KeyCode::ControlRight => Some(97), + KeyCode::NumpadDivide => Some(98), + KeyCode::PrintScreen => Some(99), + KeyCode::AltRight => Some(100), + KeyCode::Home => Some(102), + KeyCode::ArrowUp => Some(103), + KeyCode::PageUp => Some(104), + KeyCode::ArrowLeft => Some(105), + KeyCode::ArrowRight => Some(106), + KeyCode::End => Some(107), + KeyCode::ArrowDown => Some(108), + KeyCode::PageDown => Some(109), + KeyCode::Insert => Some(110), + KeyCode::Delete => Some(111), + KeyCode::AudioVolumeMute => Some(113), + KeyCode::AudioVolumeDown => Some(114), + KeyCode::AudioVolumeUp => Some(115), + KeyCode::NumpadEqual => Some(117), + KeyCode::Pause => Some(119), + KeyCode::NumpadComma => Some(121), + KeyCode::Lang1 => Some(122), + KeyCode::Lang2 => Some(123), + KeyCode::IntlYen => Some(124), + KeyCode::SuperLeft => Some(125), + KeyCode::SuperRight => Some(126), + KeyCode::ContextMenu => Some(127), + KeyCode::MediaTrackNext => Some(163), + KeyCode::MediaPlayPause => Some(164), + KeyCode::MediaTrackPrevious => Some(165), + KeyCode::MediaStop => Some(166), + KeyCode::F13 => Some(183), + KeyCode::F14 => Some(184), + KeyCode::F15 => Some(185), + KeyCode::F16 => Some(186), + KeyCode::F17 => Some(187), + KeyCode::F18 => Some(188), + KeyCode::F19 => Some(189), + KeyCode::F20 => Some(190), + KeyCode::F21 => Some(191), + KeyCode::F22 => Some(192), + KeyCode::F23 => Some(193), + KeyCode::F24 => Some(194), + _ => None, + } +} + +pub fn keysym_to_key(keysym: u32) -> Key { + use xkbcommon_dl::keysyms; + Key::Named(match keysym { + // TTY function keys + keysyms::BackSpace => NamedKey::Backspace, + keysyms::Tab => NamedKey::Tab, + // keysyms::Linefeed => NamedKey::Linefeed, + keysyms::Clear => NamedKey::Clear, + keysyms::Return => NamedKey::Enter, + keysyms::Pause => NamedKey::Pause, + keysyms::Scroll_Lock => NamedKey::ScrollLock, + keysyms::Sys_Req => NamedKey::PrintScreen, + keysyms::Escape => NamedKey::Escape, + keysyms::Delete => NamedKey::Delete, + + // IME keys + keysyms::Multi_key => NamedKey::Compose, + keysyms::Codeinput => NamedKey::CodeInput, + keysyms::SingleCandidate => NamedKey::SingleCandidate, + keysyms::MultipleCandidate => NamedKey::AllCandidates, + keysyms::PreviousCandidate => NamedKey::PreviousCandidate, + + // Japanese keys + keysyms::Kanji => NamedKey::KanjiMode, + keysyms::Muhenkan => NamedKey::NonConvert, + keysyms::Henkan_Mode => NamedKey::Convert, + keysyms::Romaji => NamedKey::Romaji, + keysyms::Hiragana => NamedKey::Hiragana, + keysyms::Hiragana_Katakana => NamedKey::HiraganaKatakana, + keysyms::Zenkaku => NamedKey::Zenkaku, + keysyms::Hankaku => NamedKey::Hankaku, + keysyms::Zenkaku_Hankaku => NamedKey::ZenkakuHankaku, + // keysyms::Touroku => NamedKey::Touroku, + // keysyms::Massyo => NamedKey::Massyo, + keysyms::Kana_Lock => NamedKey::KanaMode, + keysyms::Kana_Shift => NamedKey::KanaMode, + keysyms::Eisu_Shift => NamedKey::Alphanumeric, + keysyms::Eisu_toggle => NamedKey::Alphanumeric, + // NOTE: The next three items are aliases for values we've already mapped. + // keysyms::Kanji_Bangou => NamedKey::CodeInput, + // keysyms::Zen_Koho => NamedKey::AllCandidates, + // keysyms::Mae_Koho => NamedKey::PreviousCandidate, + + // Cursor control & motion + keysyms::Home => NamedKey::Home, + keysyms::Left => NamedKey::ArrowLeft, + keysyms::Up => NamedKey::ArrowUp, + keysyms::Right => NamedKey::ArrowRight, + keysyms::Down => NamedKey::ArrowDown, + // keysyms::Prior => NamedKey::PageUp, + keysyms::Page_Up => NamedKey::PageUp, + // keysyms::Next => NamedKey::PageDown, + keysyms::Page_Down => NamedKey::PageDown, + keysyms::End => NamedKey::End, + // keysyms::Begin => NamedKey::Begin, + + // Misc. functions + keysyms::Select => NamedKey::Select, + keysyms::Print => NamedKey::PrintScreen, + keysyms::Execute => NamedKey::Execute, + keysyms::Insert => NamedKey::Insert, + keysyms::Undo => NamedKey::Undo, + keysyms::Redo => NamedKey::Redo, + keysyms::Menu => NamedKey::ContextMenu, + keysyms::Find => NamedKey::Find, + keysyms::Cancel => NamedKey::Cancel, + keysyms::Help => NamedKey::Help, + keysyms::Break => NamedKey::Pause, + keysyms::Mode_switch => NamedKey::ModeChange, + // keysyms::script_switch => NamedKey::ModeChange, + keysyms::Num_Lock => NamedKey::NumLock, + + // Keypad keys + // keysyms::KP_Space => return Key::Character(" "), + keysyms::KP_Tab => NamedKey::Tab, + keysyms::KP_Enter => NamedKey::Enter, + keysyms::KP_F1 => NamedKey::F1, + keysyms::KP_F2 => NamedKey::F2, + keysyms::KP_F3 => NamedKey::F3, + keysyms::KP_F4 => NamedKey::F4, + keysyms::KP_Home => NamedKey::Home, + keysyms::KP_Left => NamedKey::ArrowLeft, + keysyms::KP_Up => NamedKey::ArrowUp, + keysyms::KP_Right => NamedKey::ArrowRight, + keysyms::KP_Down => NamedKey::ArrowDown, + // keysyms::KP_Prior => NamedKey::PageUp, + keysyms::KP_Page_Up => NamedKey::PageUp, + // keysyms::KP_Next => NamedKey::PageDown, + keysyms::KP_Page_Down => NamedKey::PageDown, + keysyms::KP_End => NamedKey::End, + // This is the key labeled "5" on the numpad when NumLock is off. + // keysyms::KP_Begin => NamedKey::Begin, + keysyms::KP_Insert => NamedKey::Insert, + keysyms::KP_Delete => NamedKey::Delete, + // keysyms::KP_Equal => NamedKey::Equal, + // keysyms::KP_Multiply => NamedKey::Multiply, + // keysyms::KP_Add => NamedKey::Add, + // keysyms::KP_Separator => NamedKey::Separator, + // keysyms::KP_Subtract => NamedKey::Subtract, + // keysyms::KP_Decimal => NamedKey::Decimal, + // keysyms::KP_Divide => NamedKey::Divide, + + // keysyms::KP_0 => return Key::Character("0"), + // keysyms::KP_1 => return Key::Character("1"), + // keysyms::KP_2 => return Key::Character("2"), + // keysyms::KP_3 => return Key::Character("3"), + // keysyms::KP_4 => return Key::Character("4"), + // keysyms::KP_5 => return Key::Character("5"), + // keysyms::KP_6 => return Key::Character("6"), + // keysyms::KP_7 => return Key::Character("7"), + // keysyms::KP_8 => return Key::Character("8"), + // keysyms::KP_9 => return Key::Character("9"), + + // Function keys + keysyms::F1 => NamedKey::F1, + keysyms::F2 => NamedKey::F2, + keysyms::F3 => NamedKey::F3, + keysyms::F4 => NamedKey::F4, + keysyms::F5 => NamedKey::F5, + keysyms::F6 => NamedKey::F6, + keysyms::F7 => NamedKey::F7, + keysyms::F8 => NamedKey::F8, + keysyms::F9 => NamedKey::F9, + keysyms::F10 => NamedKey::F10, + keysyms::F11 => NamedKey::F11, + keysyms::F12 => NamedKey::F12, + keysyms::F13 => NamedKey::F13, + keysyms::F14 => NamedKey::F14, + keysyms::F15 => NamedKey::F15, + keysyms::F16 => NamedKey::F16, + keysyms::F17 => NamedKey::F17, + keysyms::F18 => NamedKey::F18, + keysyms::F19 => NamedKey::F19, + keysyms::F20 => NamedKey::F20, + keysyms::F21 => NamedKey::F21, + keysyms::F22 => NamedKey::F22, + keysyms::F23 => NamedKey::F23, + keysyms::F24 => NamedKey::F24, + keysyms::F25 => NamedKey::F25, + keysyms::F26 => NamedKey::F26, + keysyms::F27 => NamedKey::F27, + keysyms::F28 => NamedKey::F28, + keysyms::F29 => NamedKey::F29, + keysyms::F30 => NamedKey::F30, + keysyms::F31 => NamedKey::F31, + keysyms::F32 => NamedKey::F32, + keysyms::F33 => NamedKey::F33, + keysyms::F34 => NamedKey::F34, + keysyms::F35 => NamedKey::F35, + + // Modifiers + keysyms::Shift_L => NamedKey::Shift, + keysyms::Shift_R => NamedKey::Shift, + keysyms::Control_L => NamedKey::Control, + keysyms::Control_R => NamedKey::Control, + keysyms::Caps_Lock => NamedKey::CapsLock, + // keysyms::Shift_Lock => NamedKey::ShiftLock, + + // keysyms::Meta_L => NamedKey::Meta, + // keysyms::Meta_R => NamedKey::Meta, + keysyms::Alt_L => NamedKey::Alt, + keysyms::Alt_R => NamedKey::Alt, + keysyms::Super_L => NamedKey::Super, + keysyms::Super_R => NamedKey::Super, + keysyms::Hyper_L => NamedKey::Hyper, + keysyms::Hyper_R => NamedKey::Hyper, + + // XKB function and modifier keys + // keysyms::ISO_Lock => NamedKey::IsoLock, + // keysyms::ISO_Level2_Latch => NamedKey::IsoLevel2Latch, + keysyms::ISO_Level3_Shift => NamedKey::AltGraph, + keysyms::ISO_Level3_Latch => NamedKey::AltGraph, + keysyms::ISO_Level3_Lock => NamedKey::AltGraph, + // keysyms::ISO_Level5_Shift => NamedKey::IsoLevel5Shift, + // keysyms::ISO_Level5_Latch => NamedKey::IsoLevel5Latch, + // keysyms::ISO_Level5_Lock => NamedKey::IsoLevel5Lock, + // keysyms::ISO_Group_Shift => NamedKey::IsoGroupShift, + // keysyms::ISO_Group_Latch => NamedKey::IsoGroupLatch, + // keysyms::ISO_Group_Lock => NamedKey::IsoGroupLock, + keysyms::ISO_Next_Group => NamedKey::GroupNext, + // keysyms::ISO_Next_Group_Lock => NamedKey::GroupNextLock, + keysyms::ISO_Prev_Group => NamedKey::GroupPrevious, + // keysyms::ISO_Prev_Group_Lock => NamedKey::GroupPreviousLock, + keysyms::ISO_First_Group => NamedKey::GroupFirst, + // keysyms::ISO_First_Group_Lock => NamedKey::GroupFirstLock, + keysyms::ISO_Last_Group => NamedKey::GroupLast, + // keysyms::ISO_Last_Group_Lock => NamedKey::GroupLastLock, + keysyms::ISO_Left_Tab => NamedKey::Tab, + // keysyms::ISO_Move_Line_Up => NamedKey::IsoMoveLineUp, + // keysyms::ISO_Move_Line_Down => NamedKey::IsoMoveLineDown, + // keysyms::ISO_Partial_Line_Up => NamedKey::IsoPartialLineUp, + // keysyms::ISO_Partial_Line_Down => NamedKey::IsoPartialLineDown, + // keysyms::ISO_Partial_Space_Left => NamedKey::IsoPartialSpaceLeft, + // keysyms::ISO_Partial_Space_Right => NamedKey::IsoPartialSpaceRight, + // keysyms::ISO_Set_Margin_Left => NamedKey::IsoSetMarginLeft, + // keysyms::ISO_Set_Margin_Right => NamedKey::IsoSetMarginRight, + // keysyms::ISO_Release_Margin_Left => NamedKey::IsoReleaseMarginLeft, + // keysyms::ISO_Release_Margin_Right => NamedKey::IsoReleaseMarginRight, + // keysyms::ISO_Release_Both_Margins => NamedKey::IsoReleaseBothMargins, + // keysyms::ISO_Fast_Cursor_Left => NamedKey::IsoFastCursorLeft, + // keysyms::ISO_Fast_Cursor_Right => NamedKey::IsoFastCursorRight, + // keysyms::ISO_Fast_Cursor_Up => NamedKey::IsoFastCursorUp, + // keysyms::ISO_Fast_Cursor_Down => NamedKey::IsoFastCursorDown, + // keysyms::ISO_Continuous_Underline => NamedKey::IsoContinuousUnderline, + // keysyms::ISO_Discontinuous_Underline => NamedKey::IsoDiscontinuousUnderline, + // keysyms::ISO_Emphasize => NamedKey::IsoEmphasize, + // keysyms::ISO_Center_Object => NamedKey::IsoCenterObject, + keysyms::ISO_Enter => NamedKey::Enter, + + // dead_grave..dead_currency + + // dead_lowline..dead_longsolidusoverlay + + // dead_a..dead_capital_schwa + + // dead_greek + + // First_Virtual_Screen..Terminate_Server + + // AccessX_Enable..AudibleBell_Enable + + // Pointer_Left..Pointer_Drag5 + + // Pointer_EnableKeys..Pointer_DfltBtnPrev + + // ch..C_H + + // 3270 terminal keys + // keysyms::3270_Duplicate => NamedKey::Duplicate, + // keysyms::3270_FieldMark => NamedKey::FieldMark, + // keysyms::3270_Right2 => NamedKey::Right2, + // keysyms::3270_Left2 => NamedKey::Left2, + // keysyms::3270_BackTab => NamedKey::BackTab, + keysyms::_3270_EraseEOF => NamedKey::EraseEof, + // keysyms::3270_EraseInput => NamedKey::EraseInput, + // keysyms::3270_Reset => NamedKey::Reset, + // keysyms::3270_Quit => NamedKey::Quit, + // keysyms::3270_PA1 => NamedKey::Pa1, + // keysyms::3270_PA2 => NamedKey::Pa2, + // keysyms::3270_PA3 => NamedKey::Pa3, + // keysyms::3270_Test => NamedKey::Test, + keysyms::_3270_Attn => NamedKey::Attn, + // keysyms::3270_CursorBlink => NamedKey::CursorBlink, + // keysyms::3270_AltCursor => NamedKey::AltCursor, + // keysyms::3270_KeyClick => NamedKey::KeyClick, + // keysyms::3270_Jump => NamedKey::Jump, + // keysyms::3270_Ident => NamedKey::Ident, + // keysyms::3270_Rule => NamedKey::Rule, + // keysyms::3270_Copy => NamedKey::Copy, + keysyms::_3270_Play => NamedKey::Play, + // keysyms::3270_Setup => NamedKey::Setup, + // keysyms::3270_Record => NamedKey::Record, + // keysyms::3270_ChangeScreen => NamedKey::ChangeScreen, + // keysyms::3270_DeleteWord => NamedKey::DeleteWord, + keysyms::_3270_ExSelect => NamedKey::ExSel, + keysyms::_3270_CursorSelect => NamedKey::CrSel, + keysyms::_3270_PrintScreen => NamedKey::PrintScreen, + keysyms::_3270_Enter => NamedKey::Enter, + + keysyms::space => NamedKey::Space, + // exclam..Sinh_kunddaliya + + // XFree86 + // keysyms::XF86_ModeLock => NamedKey::ModeLock, + + // XFree86 - Backlight controls + keysyms::XF86_MonBrightnessUp => NamedKey::BrightnessUp, + keysyms::XF86_MonBrightnessDown => NamedKey::BrightnessDown, + // keysyms::XF86_KbdLightOnOff => NamedKey::LightOnOff, + // keysyms::XF86_KbdBrightnessUp => NamedKey::KeyboardBrightnessUp, + // keysyms::XF86_KbdBrightnessDown => NamedKey::KeyboardBrightnessDown, + + // XFree86 - "Internet" + keysyms::XF86_Standby => NamedKey::Standby, + keysyms::XF86_AudioLowerVolume => NamedKey::AudioVolumeDown, + keysyms::XF86_AudioRaiseVolume => NamedKey::AudioVolumeUp, + keysyms::XF86_AudioPlay => NamedKey::MediaPlay, + keysyms::XF86_AudioStop => NamedKey::MediaStop, + keysyms::XF86_AudioPrev => NamedKey::MediaTrackPrevious, + keysyms::XF86_AudioNext => NamedKey::MediaTrackNext, + keysyms::XF86_HomePage => NamedKey::BrowserHome, + keysyms::XF86_Mail => NamedKey::LaunchMail, + // keysyms::XF86_Start => NamedKey::Start, + keysyms::XF86_Search => NamedKey::BrowserSearch, + keysyms::XF86_AudioRecord => NamedKey::MediaRecord, + + // XFree86 - PDA + keysyms::XF86_Calculator => NamedKey::LaunchApplication2, + // keysyms::XF86_Memo => NamedKey::Memo, + // keysyms::XF86_ToDoList => NamedKey::ToDoList, + keysyms::XF86_Calendar => NamedKey::LaunchCalendar, + keysyms::XF86_PowerDown => NamedKey::Power, + // keysyms::XF86_ContrastAdjust => NamedKey::AdjustContrast, + // keysyms::XF86_RockerUp => NamedKey::RockerUp, + // keysyms::XF86_RockerDown => NamedKey::RockerDown, + // keysyms::XF86_RockerEnter => NamedKey::RockerEnter, + + // XFree86 - More "Internet" + keysyms::XF86_Back => NamedKey::BrowserBack, + keysyms::XF86_Forward => NamedKey::BrowserForward, + // keysyms::XF86_Stop => NamedKey::Stop, + keysyms::XF86_Refresh => NamedKey::BrowserRefresh, + keysyms::XF86_PowerOff => NamedKey::Power, + keysyms::XF86_WakeUp => NamedKey::WakeUp, + keysyms::XF86_Eject => NamedKey::Eject, + keysyms::XF86_ScreenSaver => NamedKey::LaunchScreenSaver, + keysyms::XF86_WWW => NamedKey::LaunchWebBrowser, + keysyms::XF86_Sleep => NamedKey::Standby, + keysyms::XF86_Favorites => NamedKey::BrowserFavorites, + keysyms::XF86_AudioPause => NamedKey::MediaPause, + // keysyms::XF86_AudioMedia => NamedKey::AudioMedia, + keysyms::XF86_MyComputer => NamedKey::LaunchApplication1, + // keysyms::XF86_VendorHome => NamedKey::VendorHome, + // keysyms::XF86_LightBulb => NamedKey::LightBulb, + // keysyms::XF86_Shop => NamedKey::BrowserShop, + // keysyms::XF86_History => NamedKey::BrowserHistory, + // keysyms::XF86_OpenURL => NamedKey::OpenUrl, + // keysyms::XF86_AddFavorite => NamedKey::AddFavorite, + // keysyms::XF86_HotLinks => NamedKey::HotLinks, + // keysyms::XF86_BrightnessAdjust => NamedKey::BrightnessAdjust, + // keysyms::XF86_Finance => NamedKey::BrowserFinance, + // keysyms::XF86_Community => NamedKey::BrowserCommunity, + keysyms::XF86_AudioRewind => NamedKey::MediaRewind, + // keysyms::XF86_BackForward => Key::???, + // XF86_Launch0..XF86_LaunchF + + // XF86_ApplicationLeft..XF86_CD + keysyms::XF86_Calculater => NamedKey::LaunchApplication2, // Nice typo, libxkbcommon :) + // XF86_Clear + keysyms::XF86_Close => NamedKey::Close, + keysyms::XF86_Copy => NamedKey::Copy, + keysyms::XF86_Cut => NamedKey::Cut, + // XF86_Display..XF86_Documents + keysyms::XF86_Excel => NamedKey::LaunchSpreadsheet, + // XF86_Explorer..XF86iTouch + keysyms::XF86_LogOff => NamedKey::LogOff, + // XF86_Market..XF86_MenuPB + keysyms::XF86_MySites => NamedKey::BrowserFavorites, + keysyms::XF86_New => NamedKey::New, + // XF86_News..XF86_OfficeHome + keysyms::XF86_Open => NamedKey::Open, + // XF86_Option + keysyms::XF86_Paste => NamedKey::Paste, + keysyms::XF86_Phone => NamedKey::LaunchPhone, + // XF86_Q + keysyms::XF86_Reply => NamedKey::MailReply, + keysyms::XF86_Reload => NamedKey::BrowserRefresh, + // XF86_RotateWindows..XF86_RotationKB + keysyms::XF86_Save => NamedKey::Save, + // XF86_ScrollUp..XF86_ScrollClick + keysyms::XF86_Send => NamedKey::MailSend, + keysyms::XF86_Spell => NamedKey::SpellCheck, + keysyms::XF86_SplitScreen => NamedKey::SplitScreenToggle, + // XF86_Support..XF86_User2KB + keysyms::XF86_Video => NamedKey::LaunchMediaPlayer, + // XF86_WheelButton + keysyms::XF86_Word => NamedKey::LaunchWordProcessor, + // XF86_Xfer + keysyms::XF86_ZoomIn => NamedKey::ZoomIn, + keysyms::XF86_ZoomOut => NamedKey::ZoomOut, + + // XF86_Away..XF86_Messenger + keysyms::XF86_WebCam => NamedKey::LaunchWebCam, + keysyms::XF86_MailForward => NamedKey::MailForward, + // XF86_Pictures + keysyms::XF86_Music => NamedKey::LaunchMusicPlayer, + + // XF86_Battery..XF86_UWB + keysyms::XF86_AudioForward => NamedKey::MediaFastForward, + // XF86_AudioRepeat + keysyms::XF86_AudioRandomPlay => NamedKey::RandomToggle, + keysyms::XF86_Subtitle => NamedKey::Subtitle, + keysyms::XF86_AudioCycleTrack => NamedKey::MediaAudioTrack, + // XF86_CycleAngle..XF86_Blue + keysyms::XF86_Suspend => NamedKey::Standby, + keysyms::XF86_Hibernate => NamedKey::Hibernate, + // XF86_TouchpadToggle..XF86_TouchpadOff + keysyms::XF86_AudioMute => NamedKey::AudioVolumeMute, + + // XF86_Switch_VT_1..XF86_Switch_VT_12 + + // XF86_Ungrab..XF86_ClearGrab + keysyms::XF86_Next_VMode => NamedKey::VideoModeNext, + // keysyms::XF86_Prev_VMode => NamedKey::VideoModePrevious, + // XF86_LogWindowTree..XF86_LogGrabInfo + + // SunFA_Grave..SunFA_Cedilla + + // keysyms::SunF36 => NamedKey::F36 | NamedKey::F11, + // keysyms::SunF37 => NamedKey::F37 | NamedKey::F12, + + // keysyms::SunSys_Req => NamedKey::PrintScreen, + // The next couple of xkb (until SunStop) are already handled. + // SunPrint_Screen..SunPageDown + + // SunUndo..SunFront + keysyms::SUN_Copy => NamedKey::Copy, + keysyms::SUN_Open => NamedKey::Open, + keysyms::SUN_Paste => NamedKey::Paste, + keysyms::SUN_Cut => NamedKey::Cut, + + // SunPowerSwitch + keysyms::SUN_AudioLowerVolume => NamedKey::AudioVolumeDown, + keysyms::SUN_AudioMute => NamedKey::AudioVolumeMute, + keysyms::SUN_AudioRaiseVolume => NamedKey::AudioVolumeUp, + // SUN_VideoDegauss + keysyms::SUN_VideoLowerBrightness => NamedKey::BrightnessDown, + keysyms::SUN_VideoRaiseBrightness => NamedKey::BrightnessUp, + // SunPowerSwitchShift + 0 => return Key::Unidentified(NativeKey::Unidentified), + _ => return Key::Unidentified(NativeKey::Xkb(keysym)), + }) +} + +pub fn keysym_location(keysym: u32) -> KeyLocation { + use xkbcommon_dl::keysyms; + match keysym { + keysyms::Shift_L + | keysyms::Control_L + | keysyms::Meta_L + | keysyms::Alt_L + | keysyms::Super_L + | keysyms::Hyper_L => KeyLocation::Left, + keysyms::Shift_R + | keysyms::Control_R + | keysyms::Meta_R + | keysyms::Alt_R + | keysyms::Super_R + | keysyms::Hyper_R => KeyLocation::Right, + keysyms::KP_0 + | keysyms::KP_1 + | keysyms::KP_2 + | keysyms::KP_3 + | keysyms::KP_4 + | keysyms::KP_5 + | keysyms::KP_6 + | keysyms::KP_7 + | keysyms::KP_8 + | keysyms::KP_9 + | keysyms::KP_Space + | keysyms::KP_Tab + | keysyms::KP_Enter + | keysyms::KP_F1 + | keysyms::KP_F2 + | keysyms::KP_F3 + | keysyms::KP_F4 + | keysyms::KP_Home + | keysyms::KP_Left + | keysyms::KP_Up + | keysyms::KP_Right + | keysyms::KP_Down + | keysyms::KP_Page_Up + | keysyms::KP_Page_Down + | keysyms::KP_End + | keysyms::KP_Begin + | keysyms::KP_Insert + | keysyms::KP_Delete + | keysyms::KP_Equal + | keysyms::KP_Multiply + | keysyms::KP_Add + | keysyms::KP_Separator + | keysyms::KP_Subtract + | keysyms::KP_Decimal + | keysyms::KP_Divide => KeyLocation::Numpad, + _ => KeyLocation::Standard, + } +} + +#[derive(Default, Debug, Clone, Copy)] +pub struct ModsIndices { + pub shift: Option, + pub caps: Option, + pub ctrl: Option, + pub alt: Option, + pub num: Option, + pub mod3: Option, + pub logo: Option, + pub mod5: Option, +} + +fn mod_index_for_name(keymap: NonNull, name: &[u8]) -> Option { + unsafe { + let mod_index = + (XKBH.xkb_keymap_mod_get_index)(keymap.as_ptr(), name.as_ptr() as *const c_char); + if mod_index == XKB_MOD_INVALID { + None + } else { + Some(mod_index) + } + } +} diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index 573d285..f064b9b 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -159,8 +159,10 @@ //! mod events; -mod keyboard; +pub mod keyboard; +mod keymap; mod strtoshape; +mod xkb_keyboard; use std::fmt::Debug; @@ -173,7 +175,6 @@ pub mod key; pub use events::{AxisScroll, DispatchMessage, LayerEvent, ReturnData, XdgInfoChangedType}; use key::KeyModifierType; -use keyboard::KeyboardState; use strtoshape::str_to_shape; use wayland_client::{ delegate_noop, @@ -194,6 +195,7 @@ use wayland_client::{ }, ConnectError, Connection, Dispatch, DispatchError, EventQueue, Proxy, QueueHandle, WEnum, }; +pub use xkb_keyboard::*; use sctk::reexports::{calloop::EventLoop, calloop_wayland_source::WaylandSource}; @@ -839,11 +841,11 @@ impl Dispatch for WindowState { } _ => unreachable!(), }, - wl_keyboard::Event::Leave { serial, surface } => { - // NOTE: clear modifier - } - wl_keyboard::Event::RepeatInfo { rate, delay } => { - // NOTE: RepeatInfo + wl_keyboard::Event::Leave { .. } => { + state.message.push(( + state.surface_pos(), + DispatchMessageInner::ModifiersChanged(ModifiersState::empty()), + )); } wl_keyboard::Event::Key { state: keystate, @@ -851,6 +853,18 @@ impl Dispatch for WindowState { key, time, } => { + + let key = key + 8; + if let Some(mut key_context) = keyboard_state.xkb_context.key_context() { + let event = key_context.process_key_event(key, ElementState::Released, false); + let event = DispatchMessageInner::KeyboardInput { + event, + is_synthetic: false, + }; + state.message.push((state.surface_pos(), event)); + } + + // REMOVE it later state.message.push(( state.surface_pos(), DispatchMessageInner::KeyBoard { diff --git a/layershellev/src/xkb_keyboard.rs b/layershellev/src/xkb_keyboard.rs new file mode 100644 index 0000000..481ea8b --- /dev/null +++ b/layershellev/src/xkb_keyboard.rs @@ -0,0 +1,878 @@ +use memmap2::MmapOptions; +use smol_str::SmolStr; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::LazyLock; +use std::{ + env, + ffi::{c_char, CString}, + ops::Deref, + os::{fd::OwnedFd, unix::ffi::OsStringExt}, + ptr::{self, NonNull}, + time::Duration, +}; +use wayland_client::{protocol::wl_keyboard::WlKeyboard, Proxy}; + +use crate::keymap; + +use xkbcommon_dl::{ + self as xkb, xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, + xkb_compose_state_flags, xkb_compose_status, xkb_compose_table, xkb_keycode_t, xkb_keysym_t, + xkb_layout_index_t, xkbcommon_compose_handle, xkbcommon_handle, XkbCommon, XkbCommonCompose, +}; + +use bitflags::bitflags; + +use xkb::{ + xkb_context, xkb_context_flags, xkb_keymap, xkb_keymap_compile_flags, xkb_state, + xkb_state_component, +}; + +use crate::keyboard::{Key, KeyLocation, PhysicalKey}; + +bitflags! { + /// Represents the current state of the keyboard modifiers + /// + /// Each flag represents a modifier and is set if this modifier is active. + #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct ModifiersState: u32 { + /// The "shift" key. + const SHIFT = 0b100; + /// The "control" key. + const CONTROL = 0b100 << 3; + /// The "alt" key. + const ALT = 0b100 << 6; + /// This is the "windows" key on PC and "command" key on Mac. + const SUPER = 0b100 << 9; + } +} + +impl ModifiersState { + /// Returns `true` if the shift key is pressed. + pub fn shift_key(&self) -> bool { + self.intersects(Self::SHIFT) + } + + /// Returns `true` if the control key is pressed. + pub fn control_key(&self) -> bool { + self.intersects(Self::CONTROL) + } + + /// Returns `true` if the alt key is pressed. + pub fn alt_key(&self) -> bool { + self.intersects(Self::ALT) + } + + /// Returns `true` if the super key is pressed. + pub fn super_key(&self) -> bool { + self.intersects(Self::SUPER) + } +} + +/// The state of the particular modifiers key. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub enum ModifiersKeyState { + /// The particular key is pressed. + Pressed, + /// The state of the key is unknown. + #[default] + Unknown, +} + +// NOTE: the exact modifier key is not used to represent modifiers state in the +// first place due to a fact that modifiers state could be changed without any +// key being pressed and on some platforms like Wayland/X11 which key resulted +// in modifiers change is hidden, also, not that it really matters. +// +// The reason this API is even exposed is mostly to provide a way for users +// to treat modifiers differently based on their position, which is required +// on macOS due to their AltGr/Option situation. +bitflags! { + #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub(crate) struct ModifiersKeys: u8 { + const LSHIFT = 0b0000_0001; + const RSHIFT = 0b0000_0010; + const LCONTROL = 0b0000_0100; + const RCONTROL = 0b0000_1000; + const LALT = 0b0001_0000; + const RALT = 0b0010_0000; + const LSUPER = 0b0100_0000; + const RSUPER = 0b1000_0000; + } +} + +static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); + +pub static XKBH: LazyLock<&'static XkbCommon> = LazyLock::new(xkbcommon_handle); +pub static XKBCH: LazyLock<&'static XkbCommonCompose> = LazyLock::new(xkbcommon_compose_handle); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RepeatInfo { + /// Keys will be repeated at the specified rate and delay. + Repeat { + /// The time between the key repeats. + gap: Duration, + + /// Delay (in milliseconds) between a key press and the start of repetition. + delay: Duration, + }, + + /// Keys should not be repeated. + Disable, +} + +impl Default for RepeatInfo { + /// The default repeat rate is 25 keys per second with the delay of 200ms. + /// + /// The values are picked based on the default in various compositors and Xorg. + fn default() -> Self { + Self::Repeat { + gap: Duration::from_millis(40), + delay: Duration::from_millis(200), + } + } +} + +#[derive(Debug)] +pub struct KeyboardState { + pub keyboard: WlKeyboard, + + pub xkb_context: Context, + pub repeat_info: RepeatInfo, + pub current_repeat: Option, +} + +impl KeyboardState { + pub fn new(keyboard: WlKeyboard) -> Self { + Self { + keyboard, + xkb_context: Context::new().unwrap(), + repeat_info: RepeatInfo::default(), + current_repeat: None, + } + } +} + +impl Drop for KeyboardState { + fn drop(&mut self) { + if self.keyboard.version() >= 3 { + self.keyboard.release(); + } + } +} + +#[derive(Debug)] +pub enum Error { + /// libxkbcommon is not available + XKBNotFound, +} + +#[derive(Debug)] +pub struct Context { + // NOTE: field order matters. + state: Option, + keymap: Option, + compose_state1: Option, + compose_state2: Option, + _compose_table: Option, + context: XkbContext, + scratch_buffer: Vec, +} + +impl Context { + pub fn new() -> Result { + if xkb::xkbcommon_option().is_none() { + return Err(Error::XKBNotFound); + } + + let context = XkbContext::new(); + let mut compose_table = XkbComposeTable::new(&context); + let mut compose_state1 = compose_table.as_ref().and_then(|table| table.new_state()); + let mut compose_state2 = compose_table.as_ref().and_then(|table| table.new_state()); + + // Disable compose if anything compose related failed to initialize. + if compose_table.is_none() || compose_state1.is_none() || compose_state2.is_none() { + compose_state2 = None; + compose_state1 = None; + compose_table = None; + } + + Ok(Self { + state: None, + keymap: None, + compose_state1, + compose_state2, + _compose_table: compose_table, + context, + scratch_buffer: Vec::with_capacity(8), + }) + } + pub fn set_keymap_from_fd(&mut self, fd: OwnedFd, size: usize) { + let keymap = XkbKeymap::from_fd(&self.context, fd, size); + let state = keymap.as_ref().and_then(XkbState::new_wayland); + if keymap.is_none() || state.is_none() { + log::warn!("failed to update xkb keymap"); + } + self.state = state; + self.keymap = keymap; + } + /// Key builder context with the user provided xkb state. + pub fn key_context(&mut self) -> Option> { + let state = self.state.as_mut()?; + let keymap = self.keymap.as_mut()?; + let compose_state1 = self.compose_state1.as_mut(); + let compose_state2 = self.compose_state2.as_mut(); + let scratch_buffer = &mut self.scratch_buffer; + Some(KeyContext { + state, + keymap, + compose_state1, + compose_state2, + scratch_buffer, + }) + } +} + +#[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 }) + } + + pub fn first_keysym_by_level( + &mut self, + layout: xkb_layout_index_t, + keycode: xkb_keycode_t, + ) -> xkb_keysym_t { + unsafe { + let mut keysyms = ptr::null(); + let count = (XKBH.xkb_keymap_key_get_syms_by_level)( + self.keymap.as_ptr(), + keycode, + layout, + // NOTE: The level should be zero to ignore modifiers. + 0, + &mut keysyms, + ); + + if count == 1 { + *keysyms + } else { + 0 + } + } + } +} + +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: ModifiersStateXkb, +} + +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 = ModifiersStateXkb::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); + 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); + } + + pub fn get_one_sym_raw(&mut self, keycode: xkb_keycode_t) -> xkb_keysym_t { + unsafe { (XKBH.xkb_state_key_get_one_sym)(self.state.as_ptr(), keycode) } + } + + pub fn layout(&mut self, key: xkb_keycode_t) -> xkb_layout_index_t { + unsafe { (XKBH.xkb_state_key_get_layout)(self.state.as_ptr(), key) } + } + + pub fn get_utf8_raw( + &mut self, + keycode: xkb_keycode_t, + scratch_buffer: &mut Vec, + ) -> Option { + make_string_with(scratch_buffer, |ptr, len| unsafe { + (XKBH.xkb_state_key_get_utf8)(self.state.as_ptr(), keycode, ptr, len) + }) + } +} + +#[derive(Debug, Default, Clone, Copy)] +pub struct ModifiersStateXkb { + ctrl: bool, + alt: bool, + shift: bool, + caps_lock: bool, + logo: bool, + num_lock: bool, +} + +impl From for ModifiersState { + fn from(mods: ModifiersStateXkb) -> ModifiersState { + let mut to_mods = ModifiersState::empty(); + to_mods.set(ModifiersState::SHIFT, mods.shift); + to_mods.set(ModifiersState::CONTROL, mods.ctrl); + to_mods.set(ModifiersState::ALT, mods.alt); + to_mods.set(ModifiersState::SUPER, mods.logo); + to_mods + } +} + +#[derive(Debug)] +pub struct XkbComposeTable { + table: NonNull, +} + +impl XkbComposeTable { + pub fn new(context: &XkbContext) -> Option { + 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 table = unsafe { + (XKBCH.xkb_compose_table_new_from_locale)( + context.as_ptr(), + locale.as_ptr(), + xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS, + ) + }; + + let table = NonNull::new(table)?; + Some(Self { table }) + } + + /// Create new state with the given compose table. + pub fn new_state(&self) -> Option { + let state = unsafe { + (XKBCH.xkb_compose_state_new)( + self.table.as_ptr(), + xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, + ) + }; + + let state = NonNull::new(state)?; + Some(XkbComposeState { state }) + } +} + +impl Deref for XkbComposeTable { + type Target = NonNull; + + fn deref(&self) -> &Self::Target { + &self.table + } +} + +impl Drop for XkbComposeTable { + fn drop(&mut self) { + unsafe { + (XKBCH.xkb_compose_table_unref)(self.table.as_ptr()); + } + } +} + +#[derive(Debug)] +pub struct XkbComposeState { + state: NonNull, +} + +// NOTE: This is track_caller so we can have more informative line numbers when logging +#[track_caller] +fn byte_slice_to_smol_str(bytes: &[u8]) -> Option { + std::str::from_utf8(bytes) + .map(SmolStr::new) + .map_err(|e| { + log::warn!( + "UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", + bytes + ) + }) + .ok() +} + +/// Shared logic for constructing a string with `xkb_compose_state_get_utf8` and +/// `xkb_state_key_get_utf8`. +fn make_string_with(scratch_buffer: &mut Vec, mut f: F) -> Option +where + F: FnMut(*mut c_char, usize) -> i32, +{ + let size = f(ptr::null_mut(), 0); + if size == 0 { + return None; + } + let size = usize::try_from(size).unwrap(); + scratch_buffer.clear(); + // The allocated buffer must include space for the null-terminator. + scratch_buffer.reserve(size + 1); + unsafe { + let written = f( + scratch_buffer.as_mut_ptr().cast(), + scratch_buffer.capacity(), + ); + if usize::try_from(written).unwrap() != size { + // This will likely never happen. + return None; + } + scratch_buffer.set_len(size); + }; + + byte_slice_to_smol_str(scratch_buffer) +} + +impl XkbComposeState { + pub fn get_string(&mut self, scratch_buffer: &mut Vec) -> Option { + make_string_with(scratch_buffer, |ptr, len| unsafe { + (XKBCH.xkb_compose_state_get_utf8)(self.state.as_ptr(), ptr, len) + }) + } + + #[inline] + pub fn feed(&mut self, keysym: xkb_keysym_t) -> ComposeStatus { + let feed_result = unsafe { (XKBCH.xkb_compose_state_feed)(self.state.as_ptr(), keysym) }; + match feed_result { + xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED => ComposeStatus::Ignored, + xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED => { + ComposeStatus::Accepted(self.status()) + } + } + } + + #[inline] + pub fn reset(&mut self) { + unsafe { + (XKBCH.xkb_compose_state_reset)(self.state.as_ptr()); + } + } + + #[inline] + pub fn status(&mut self) -> xkb_compose_status { + unsafe { (XKBCH.xkb_compose_state_get_status)(self.state.as_ptr()) } + } +} + +impl Drop for XkbComposeState { + fn drop(&mut self) { + unsafe { + (XKBCH.xkb_compose_state_unref)(self.state.as_ptr()); + }; + } +} + +#[derive(Copy, Clone, Debug)] +pub enum ComposeStatus { + Accepted(xkb_compose_status), + Ignored, + None, +} + +pub struct KeyContext<'a> { + pub state: &'a mut XkbState, + pub keymap: &'a mut XkbKeymap, + compose_state1: Option<&'a mut XkbComposeState>, + compose_state2: Option<&'a mut XkbComposeState>, + scratch_buffer: &'a mut Vec, +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum ElementState { + Pressed, + Released, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEventExtra { + pub text_with_all_modifiers: Option, + pub key_without_modifiers: Key, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEvent { + /// Represents the position of a key independent of the currently active layout. + /// + /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). + /// The most prevalent use case for this is games. For example the default keys for the player + /// to move around might be the W, A, S, and D keys on a US layout. The position of these keys + /// is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY" + /// layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.) + /// + /// ## Caveats + /// + /// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that + /// implements DVORAK in hardware (or firmware) + /// - Your application will likely have to handle keyboards which are missing keys that your + /// own keyboard has. + /// - Certain `KeyCode`s will move between a couple of different positions depending on what + /// layout the keyboard was manufactured to support. + /// + /// **Because of these caveats, it is important that you provide users with a way to configure + /// most (if not all) keybinds in your application.** + /// + /// ## `Fn` and `FnLock` + /// + /// `Fn` and `FnLock` key events are *exceedingly unlikely* to be emitted by Winit. These keys + /// are usually handled at the hardware or OS level, and aren't surfaced to applications. If + /// you somehow see this in the wild, we'd like to know :) + pub physical_key: PhysicalKey, + + /// This value is affected by all modifiers except Ctrl. + /// + /// This has two use cases: + /// - Allows querying whether the current input is a Dead key. + /// - Allows handling key-bindings on platforms which don't + /// support [`key_without_modifiers`]. + /// + /// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard + /// shortcuts, **it is important that you provide users with a way to configure your + /// application's shortcuts so you don't render your application unusable for users with an + /// incompatible keyboard layout.** + /// + /// ## Platform-specific + /// - **Web:** Dead keys might be reported as the real key instead + /// of `Dead` depending on the browser/OS. + /// + /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers + pub logical_key: Key, + + /// Contains the text produced by this keypress. + /// + /// In most cases this is identical to the content + /// of the `Character` variant of `logical_key`. + /// However, on Windows when a dead key was pressed earlier + /// but cannot be combined with the character from this + /// keypress, the produced text will consist of two characters: + /// the dead-key-character followed by the character resulting + /// from this keypress. + /// + /// An additional difference from `logical_key` is that + /// this field stores the text representation of any key + /// that has such a representation. For example when + /// `logical_key` is `Key::Named(NamedKey::Enter)`, this field is `Some("\r")`. + /// + /// This is `None` if the current keypress cannot + /// be interpreted as text. + /// + /// See also: `text_with_all_modifiers()` + pub text: Option, + + /// Contains the location of this key on the keyboard. + /// + /// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" + /// key appears on the left side of the QWERTY keyboard as well as the right side. However, + /// both keys have the same symbolic value. Another example of this phenomenon is the "1" + /// key, which appears both above the "Q" key and as the "Keypad 1" key. + /// + /// This field allows the user to differentiate between keys like this that have the same + /// symbolic value but different locations on the keyboard. + /// + /// See the [`KeyLocation`] type for more details. + /// + /// [`KeyLocation`]: crate::keyboard::KeyLocation + pub location: KeyLocation, + + /// Whether the key is being pressed or released. + /// + /// See the [`ElementState`] type for more details. + pub state: ElementState, + + /// Whether or not this key is a key repeat event. + /// + /// On some systems, holding down a key for some period of time causes that key to be repeated + /// as though it were being pressed and released repeatedly. This field is `true` if and only + /// if this event is the result of one of those repeats. + /// + pub repeat: bool, + + /// Platform-specific key event information. + /// + /// On Windows, Linux and macOS, this type contains the key without modifiers and the text with + /// all modifiers applied. + /// + /// On Android, iOS, Redox and Web, this type is a no-op. + pub(crate) platform_specific: KeyEventExtra, +} + +impl KeyEvent { + #[inline] + pub fn text_with_all_modifiers(&self) -> Option<&str> { + self.platform_specific + .text_with_all_modifiers + .as_ref() + .map(|s| s.as_str()) + } + + #[inline] + pub fn key_without_modifiers(&self) -> Key { + self.platform_specific.key_without_modifiers.clone() + } +} + +impl<'a> KeyContext<'a> { + pub fn process_key_event( + &mut self, + keycode: u32, + state: ElementState, + repeat: bool, + ) -> KeyEvent { + let mut event = + KeyEventResults::new(self, keycode, !repeat && state == ElementState::Pressed); + let physical_key = keymap::raw_keycode_to_physicalkey(keycode); + let (logical_key, location) = event.key(); + let text = event.text(); + let (key_without_modifiers, _) = event.key_without_modifiers(); + let text_with_all_modifiers = event.text_with_all_modifiers(); + + let platform_specific = KeyEventExtra { + text_with_all_modifiers, + key_without_modifiers, + }; + + KeyEvent { + physical_key, + logical_key, + text, + location, + state, + repeat, + platform_specific, + } + } + + fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option { + self.scratch_buffer.clear(); + self.scratch_buffer.reserve(8); + loop { + let bytes_written = unsafe { + (XKBH.xkb_keysym_to_utf8)( + keysym, + self.scratch_buffer.as_mut_ptr().cast(), + self.scratch_buffer.capacity(), + ) + }; + if bytes_written == 0 { + return None; + } else if bytes_written == -1 { + self.scratch_buffer.reserve(8); + } else { + unsafe { + self.scratch_buffer + .set_len(bytes_written.try_into().unwrap()) + }; + break; + } + } + + // Remove the null-terminator + self.scratch_buffer.pop(); + byte_slice_to_smol_str(self.scratch_buffer) + } +} + +struct KeyEventResults<'a, 'b> { + context: &'a mut KeyContext<'b>, + keycode: u32, + keysym: u32, + compose: ComposeStatus, +} + +impl<'a, 'b> KeyEventResults<'a, 'b> { + fn new(context: &'a mut KeyContext<'b>, keycode: u32, compose: bool) -> Self { + let keysym = context.state.get_one_sym_raw(keycode); + + let compose = if let Some(state) = context.compose_state1.as_mut().filter(|_| compose) { + if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) { + state.reset(); + context.compose_state2.as_mut().unwrap().reset(); + } + state.feed(keysym) + } else { + ComposeStatus::None + }; + + KeyEventResults { + context, + keycode, + keysym, + compose, + } + } + + pub fn key(&mut self) -> (Key, KeyLocation) { + let (key, location) = match self.keysym_to_key(self.keysym) { + Ok(known) => return known, + Err(undefined) => undefined, + }; + + if let ComposeStatus::Accepted(xkb_compose_status::XKB_COMPOSE_COMPOSING) = self.compose { + let compose_state = self.context.compose_state2.as_mut().unwrap(); + // When pressing a dead key twice, the non-combining variant of that character will + // be produced. Since this function only concerns itself with a single keypress, we + // simulate this double press here by feeding the keysym to the compose state + // twice. + + compose_state.feed(self.keysym); + if matches!(compose_state.feed(self.keysym), ComposeStatus::Accepted(_)) { + // Extracting only a single `char` here *should* be fine, assuming that no + // dead key's non-combining variant ever occupies more than one `char`. + let text = compose_state.get_string(self.context.scratch_buffer); + let key = Key::Dead(text.and_then(|s| s.chars().next())); + (key, location) + } else { + (key, location) + } + } else { + let key = self + .composed_text() + .unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym)) + .map(Key::Character) + .unwrap_or(key); + (key, location) + } + } + + pub fn key_without_modifiers(&mut self) -> (Key, KeyLocation) { + // This will become a pointer to an array which libxkbcommon owns, so we don't need to + // deallocate it. + let layout = self.context.state.layout(self.keycode); + let keysym = self + .context + .keymap + .first_keysym_by_level(layout, self.keycode); + + match self.keysym_to_key(keysym) { + Ok((key, location)) => (key, location), + Err((key, location)) => { + let key = self + .context + .keysym_to_utf8_raw(keysym) + .map(Key::Character) + .unwrap_or(key); + (key, location) + } + } + } + + fn keysym_to_key(&self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> { + let location = keymap::keysym_location(keysym); + let key = keymap::keysym_to_key(keysym); + if matches!(key, Key::Unidentified(_)) { + Err((key, location)) + } else { + Ok((key, location)) + } + } + + pub fn text(&mut self) -> Option { + self.composed_text() + .unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym)) + } + + // The current behaviour makes it so composing a character overrides attempts to input a + // control character with the `Ctrl` key. We can potentially add a configuration option + // if someone specifically wants the oppsite behaviour. + pub fn text_with_all_modifiers(&mut self) -> Option { + match self.composed_text() { + Ok(text) => text, + Err(_) => self + .context + .state + .get_utf8_raw(self.keycode, self.context.scratch_buffer), + } + } + + fn composed_text(&mut self) -> Result, ()> { + match self.compose { + ComposeStatus::Accepted(status) => match status { + xkb_compose_status::XKB_COMPOSE_COMPOSED => { + let state = self.context.compose_state1.as_mut().unwrap(); + Ok(state.get_string(self.context.scratch_buffer)) + } + xkb_compose_status::XKB_COMPOSE_COMPOSING + | xkb_compose_status::XKB_COMPOSE_CANCELLED => Ok(None), + xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()), + }, + _ => Err(()), + } + } +} From 7f7c5cb670e1ac81779ba617be8d81cdf28e5029 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 21:36:55 +0859 Subject: [PATCH 06/16] chore: almost finished, let me tidy up I do not fully unstand this code.. give me some time --- iced_layershell/src/application.rs | 4 +- iced_layershell/src/application/state.rs | 7 +++ iced_layershell/src/conversion.rs | 60 +++++++++++++---------- iced_layershell/src/conversion/keymap.rs | 2 +- iced_layershell/src/event.rs | 13 +++-- iced_layershell/src/multi_window.rs | 3 +- iced_layershell/src/multi_window/state.rs | 7 ++- layershellev/src/events.rs | 2 +- layershellev/src/lib.rs | 43 +++++++++------- layershellev/src/xkb_keyboard.rs | 34 +++++++++++++ 10 files changed, 123 insertions(+), 52 deletions(-) diff --git a/iced_layershell/src/application.rs b/iced_layershell/src/application.rs index ed59e46..3fb625f 100644 --- a/iced_layershell/src/application.rs +++ b/iced_layershell/src/application.rs @@ -439,7 +439,9 @@ async fn run_instance( IcedLayerEvent::Window(event) => { state.update(&event); - if let Some(event) = conversion::window_event(IcedCoreWindow::Id::MAIN, &event) { + if let Some(event) = + conversion::window_event(IcedCoreWindow::Id::MAIN, &event, state.modifiers()) + { events.push(event); } } diff --git a/iced_layershell/src/application/state.rs b/iced_layershell/src/application/state.rs index a399e02..fecb833 100644 --- a/iced_layershell/src/application/state.rs +++ b/iced_layershell/src/application/state.rs @@ -2,6 +2,7 @@ use crate::application::Application; use iced_core::{mouse as IcedMouse, Color, Point, Size}; use iced_graphics::Viewport; use iced_style::application::{self, StyleSheet}; +use layershellev::ModifiersState; use crate::event::WindowEvent; @@ -15,6 +16,7 @@ where theme: A::Theme, appearance: application::Appearance, mouse_position: Option, + modifiers: ModifiersState, } impl State @@ -38,9 +40,14 @@ where theme, appearance, mouse_position: None, + modifiers: ModifiersState::default(), } } + pub fn modifiers(&self) -> ModifiersState { + self.modifiers + } + pub fn update_view_port(&mut self, width: u32, height: u32) { self.viewport = Viewport::with_physical_size( iced_core::Size::new(width, height), diff --git a/iced_layershell/src/conversion.rs b/iced_layershell/src/conversion.rs index cee352c..008239a 100644 --- a/iced_layershell/src/conversion.rs +++ b/iced_layershell/src/conversion.rs @@ -6,11 +6,19 @@ use crate::event::WindowEvent as LayerShellEvent; use iced_core::SmolStr; use iced_core::{keyboard, mouse, Event as IcedEvent}; use keymap::key; +use keymap::modifiers; use keymap::{key_from_u32, text_from_key}; +use layershellev::keyboard::KeyLocation; +use layershellev::ElementState; use layershellev::KeyEvent as LayerShellKeyEvent; +use layershellev::ModifiersState; #[allow(unused)] -pub fn window_event(id: iced_core::window::Id, layerevent: &LayerShellEvent) -> Option { +pub fn window_event( + id: iced_core::window::Id, + layerevent: &LayerShellEvent, + modifiers: ModifiersState, +) -> Option { match layerevent { LayerShellEvent::CursorLeft => Some(IcedEvent::Mouse(mouse::Event::CursorLeft)), LayerShellEvent::CursorMoved { x, y } => { @@ -35,28 +43,7 @@ pub fn window_event(id: iced_core::window::Id, layerevent: &LayerShellEvent) -> delta: mouse::ScrollDelta::Pixels { x: *x, y: *y }, })) } - LayerShellEvent::Keyboard { - state, - key, - modifiers, - } => { - let key = key_from_u32(*key); - let text = text_from_key(&key); - match state { - IcedKeyState::Pressed => Some(IcedEvent::Keyboard(keyboard::Event::KeyPressed { - key, - location: keyboard::Location::Standard, - modifiers: *modifiers, - text, - })), - IcedKeyState::Released => Some(IcedEvent::Keyboard(keyboard::Event::KeyReleased { - key, - location: keyboard::Location::Standard, - modifiers: *modifiers, - })), - } - } - LayerShellEvent::KeyBoardInput { event, .. } => { + LayerShellEvent::KeyBoardInput { event, .. } => Some(IcedEvent::Keyboard({ let logical_key = event.key_without_modifiers(); let text = event .text_with_all_modifiers() @@ -66,8 +53,31 @@ pub fn window_event(id: iced_core::window::Id, layerevent: &LayerShellEvent) -> state, location, .. } = event; let key = key(logical_key); - todo!() - } + let modifiers = keymap::modifiers(modifiers); + + let location = match location { + KeyLocation::Standard => keyboard::Location::Standard, + KeyLocation::Left => keyboard::Location::Left, + KeyLocation::Right => keyboard::Location::Right, + KeyLocation::Numpad => keyboard::Location::Numpad, + }; + match state { + ElementState::Pressed => keyboard::Event::KeyPressed { + key, + location, + modifiers, + text, + }, + ElementState::Released => keyboard::Event::KeyReleased { + key, + location, + modifiers, + }, + } + })), + LayerShellEvent::ModifiersChanged(new_modifiers) => Some(IcedEvent::Keyboard( + keyboard::Event::ModifiersChanged(keymap::modifiers(new_modifiers.clone())), + )), _ => None, } } diff --git a/iced_layershell/src/conversion/keymap.rs b/iced_layershell/src/conversion/keymap.rs index e200635..958176c 100644 --- a/iced_layershell/src/conversion/keymap.rs +++ b/iced_layershell/src/conversion/keymap.rs @@ -430,7 +430,7 @@ pub fn key(key: layershellev::keyboard::Key) -> iced_core::keyboard::Key { } pub fn modifiers( - modifiers: layershellev::keyboard::ModifiersState, + modifiers: layershellev::ModifiersState, ) -> iced_core::keyboard::Modifiers { use iced_core::keyboard; let mut result = keyboard::Modifiers::empty(); diff --git a/iced_layershell/src/event.rs b/iced_layershell/src/event.rs index 9256f4e..75eff5d 100644 --- a/iced_layershell/src/event.rs +++ b/iced_layershell/src/event.rs @@ -1,7 +1,7 @@ use layershellev::id::Id; use layershellev::key::KeyModifierType; -use layershellev::KeyEvent as LayerShellKeyEvent; use layershellev::reexport::wayland_client::{ButtonState, KeyState, WEnum}; +use layershellev::KeyEvent as LayerShellKeyEvent; use layershellev::{DispatchMessage, WindowWrapper}; use iced_core::keyboard::Modifiers as IcedModifiers; @@ -53,6 +53,7 @@ pub enum WindowEvent { event: LayerShellKeyEvent, is_synthetic: bool, }, + ModifiersChanged(layershellev::ModifiersState), Axis { x: f32, y: f32, @@ -134,10 +135,16 @@ impl From<&DispatchMessage> for IcedLayerEvent { key: *key, modifiers: modifier_from_layershell_to_iced(*modifier), }), - DispatchMessage::KeyboardInput { event, is_synthetic } => IcedLayerEvent::Window(WindowEvent::KeyBoardInput{ + DispatchMessage::KeyboardInput { + event, + is_synthetic, + } => IcedLayerEvent::Window(WindowEvent::KeyBoardInput { event: event.clone(), - is_synthetic: *is_synthetic + is_synthetic: *is_synthetic, }), + DispatchMessage::ModifiersChanged(modifiers) => { + IcedLayerEvent::Window(WindowEvent::ModifiersChanged(*modifiers)) + } DispatchMessage::Axis { horizontal, vertical, diff --git a/iced_layershell/src/multi_window.rs b/iced_layershell/src/multi_window.rs index cf3fbc8..6116090 100644 --- a/iced_layershell/src/multi_window.rs +++ b/iced_layershell/src/multi_window.rs @@ -528,7 +528,8 @@ async fn run_instance( continue; }; window.state.update(&event); - if let Some(event) = conversion::window_event(id, &event) { + if let Some(event) = conversion::window_event(id, &event, window.state.modifiers()) + { events.push((Some(id), event)); } } diff --git a/iced_layershell/src/multi_window/state.rs b/iced_layershell/src/multi_window/state.rs index c428b2a..40918b8 100644 --- a/iced_layershell/src/multi_window/state.rs +++ b/iced_layershell/src/multi_window/state.rs @@ -2,6 +2,7 @@ use crate::multi_window::Application; use iced_core::{mouse as IcedMouse, Color, Point, Size}; use iced_graphics::Viewport; use iced_style::application::{self, StyleSheet}; +use layershellev::ModifiersState; use crate::event::WindowEvent; use iced::window; @@ -17,6 +18,7 @@ where theme: A::Theme, appearance: application::Appearance, mouse_position: Option, + modifiers: ModifiersState, } impl State @@ -38,9 +40,12 @@ where theme, appearance, mouse_position: None, + modifiers: ModifiersState::default(), } } - + pub fn modifiers(&self) -> ModifiersState { + self.modifiers + } pub fn update_view_port(&mut self, width: u32, height: u32) { self.viewport = Viewport::with_physical_size( iced_core::Size::new(width, height), diff --git a/layershellev/src/events.rs b/layershellev/src/events.rs index 48349b0..dd84485 100644 --- a/layershellev/src/events.rs +++ b/layershellev/src/events.rs @@ -12,7 +12,7 @@ use wayland_client::{ use crate::{ key::KeyModifierType, - xkb_keyboard::{KeyEvent, ModifiersState, ModifiersStateXkb}, + xkb_keyboard::{KeyEvent, ModifiersState}, }; use super::WindowState; diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index f064b9b..7014f21 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -183,7 +183,7 @@ use wayland_client::{ wl_buffer::WlBuffer, wl_compositor::WlCompositor, wl_display::WlDisplay, - wl_keyboard::{self, KeymapFormat, WlKeyboard}, + wl_keyboard::{self, KeyState, KeymapFormat, WlKeyboard}, wl_output::{self, WlOutput}, wl_pointer::{self, WlPointer}, wl_registry, @@ -849,40 +849,45 @@ impl Dispatch for WindowState { } wl_keyboard::Event::Key { state: keystate, - serial, key, - time, + .. } => { - + let pressed_state = match keystate { + WEnum::Value(KeyState::Pressed) => ElementState::Pressed, + WEnum::Value(KeyState::Released) => ElementState::Released, + _ => { + return; + } + }; let key = key + 8; if let Some(mut key_context) = keyboard_state.xkb_context.key_context() { - let event = key_context.process_key_event(key, ElementState::Released, false); + let event = key_context.process_key_event(key, pressed_state, false); let event = DispatchMessageInner::KeyboardInput { event, is_synthetic: false, }; state.message.push((state.surface_pos(), event)); } - - // REMOVE it later - state.message.push(( - state.surface_pos(), - DispatchMessageInner::KeyBoard { - state: keystate, - modifier: state.modifier, - serial, - key, - time, - }, - )); } wl_keyboard::Event::Modifiers { mods_depressed, mods_locked, + mods_latched, + group, .. } => { - state.modifier = KeyModifierType::from_bits(mods_depressed | mods_locked) - .unwrap_or(KeyModifierType::empty()); + let xkb_context = &mut keyboard_state.xkb_context; + let xkb_state = match xkb_context.state_mut() { + Some(state) => state, + None => return, + }; + xkb_state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group); + let modifiers = xkb_state.modifiers(); + + state.message.push(( + state.surface_pos(), + DispatchMessageInner::ModifiersChanged(modifiers.into()), + )) } _ => {} } diff --git a/layershellev/src/xkb_keyboard.rs b/layershellev/src/xkb_keyboard.rs index 481ea8b..9bbc7d8 100644 --- a/layershellev/src/xkb_keyboard.rs +++ b/layershellev/src/xkb_keyboard.rs @@ -215,6 +215,10 @@ impl Context { self.state = state; self.keymap = keymap; } + + pub fn state_mut(&mut self) -> Option<&mut XkbState> { + self.state.as_mut() + } /// Key builder context with the user provided xkb state. pub fn key_context(&mut self) -> Option> { let state = self.state.as_mut()?; @@ -346,6 +350,36 @@ impl XkbState { ) > 0 } } + pub fn modifiers(&self) -> ModifiersStateXkb { + self.modifiers + } + pub fn update_modifiers( + &mut self, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + depressed_group: u32, + latched_group: u32, + locked_group: u32, + ) { + let mask = unsafe { + (XKBH.xkb_state_update_mask)( + self.state.as_ptr(), + mods_depressed, + mods_latched, + mods_locked, + depressed_group, + latched_group, + locked_group, + ) + }; + + if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) { + // Effective value of mods have changed, we need to update our state. + self.reload_modifiers(); + } + } + 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); From 5d88a6c5be351fc550f80eff1da582c45de61825 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 21:38:49 +0859 Subject: [PATCH 07/16] chore: add _typos.toml --- _typos.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_typos.toml b/_typos.toml index 3f63f78..d70e89c 100644 --- a/_typos.toml +++ b/_typos.toml @@ -1,2 +1,6 @@ [files] extend-exclude = ["**/Cargo.lock"] + +[default.extend-words] +# Don't correct the surname "Teh" +Calculater = "Calculater" From 8c1b3e643dd0b0d2c4d600c7777ec0dd0696c147 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 21:48:01 +0859 Subject: [PATCH 08/16] chore: make some clean --- iced_layershell/src/application.rs | 45 ++-------- iced_layershell/src/conversion.rs | 3 - iced_layershell/src/conversion/keymap.rs | 101 ----------------------- iced_layershell/src/event.rs | 15 +--- iced_layershell/src/multi_window.rs | 51 ++---------- layershellev/src/events.rs | 36 +------- layershellev/src/key.rs | 59 ------------- layershellev/src/keymap.rs | 34 +------- layershellev/src/lib.rs | 8 -- 9 files changed, 20 insertions(+), 332 deletions(-) delete mode 100644 layershellev/src/key.rs diff --git a/iced_layershell/src/application.rs b/iced_layershell/src/application.rs index 3fb625f..0196e45 100644 --- a/iced_layershell/src/application.rs +++ b/iced_layershell/src/application.rs @@ -20,10 +20,7 @@ use iced_style::application::StyleSheet; use iced_futures::{Executor, Runtime, Subscription}; -use layershellev::{ - reexport::wayland_client::{KeyState, WEnum}, - LayerEvent, ReturnData, WindowState, WindowWrapper, -}; +use layershellev::{LayerEvent, ReturnData, WindowState, WindowWrapper}; use futures::{channel::mpsc, SinkExt, StreamExt}; @@ -189,8 +186,6 @@ where let mut context = task::Context::from_waker(task::noop_waker_ref()); let mut pointer_serial: u32 = 0; - let mut key_event: Option> = None; - let mut key_ping_count: u32 = 400; let _ = ev.running_with_proxy(message_receiver, move |event, ev, _| { use layershellev::DispatchMessage; @@ -203,14 +198,6 @@ where DispatchMessage::MouseEnter { serial, .. } => { pointer_serial = *serial; } - DispatchMessage::KeyBoard { state, .. } => { - if let WEnum::Value(KeyState::Pressed) = state { - key_event = Some(message.into()); - } else { - key_event = None; - key_ping_count = 400; - } - } _ => {} } @@ -218,31 +205,11 @@ where .start_send(message.into()) .expect("Cannot send"); } - LayerEvent::NormalDispatch => match &key_event { - Some(keyevent) => { - if let IcedLayerEvent::Window(windowevent) = keyevent { - let event = IcedLayerEvent::Window(windowevent.clone()); - if key_ping_count > 70 && key_ping_count < 74 { - event_sender.start_send(event).expect("Cannot send"); - key_ping_count = 0; - } else { - event_sender - .start_send(IcedLayerEvent::NormalUpdate) - .expect("Cannot send"); - } - if key_ping_count >= 74 { - key_ping_count -= 1; - } else { - key_ping_count += 1; - } - } - } - None => { - event_sender - .start_send(IcedLayerEvent::NormalUpdate) - .expect("Cannot send"); - } - }, + LayerEvent::NormalDispatch => { + event_sender + .start_send(IcedLayerEvent::NormalUpdate) + .expect("Cannot send"); + } LayerEvent::UserEvent(event) => { event_sender .start_send(IcedLayerEvent::UserEvent(event)) diff --git a/iced_layershell/src/conversion.rs b/iced_layershell/src/conversion.rs index 008239a..5808be9 100644 --- a/iced_layershell/src/conversion.rs +++ b/iced_layershell/src/conversion.rs @@ -1,13 +1,10 @@ mod keymap; use crate::event::IcedButtonState; -use crate::event::IcedKeyState; use crate::event::WindowEvent as LayerShellEvent; use iced_core::SmolStr; use iced_core::{keyboard, mouse, Event as IcedEvent}; use keymap::key; -use keymap::modifiers; -use keymap::{key_from_u32, text_from_key}; use layershellev::keyboard::KeyLocation; use layershellev::ElementState; use layershellev::KeyEvent as LayerShellKeyEvent; diff --git a/iced_layershell/src/conversion/keymap.rs b/iced_layershell/src/conversion/keymap.rs index 958176c..1692758 100644 --- a/iced_layershell/src/conversion/keymap.rs +++ b/iced_layershell/src/conversion/keymap.rs @@ -1,104 +1,3 @@ -use iced_core::SmolStr; - -use iced_core::keyboard::Key as IcedKey; - -// TODO: modifier -pub fn key_from_u32(value: u32) -> IcedKey { - use iced_core::keyboard::key::Named; - match value { - 0 => IcedKey::Unidentified, - 1 => IcedKey::Named(Named::Escape), - code @ 2..=10 => IcedKey::Character(SmolStr::new((code - 1).to_string())), - 11 => IcedKey::Character(SmolStr::new("0")), - 12 => IcedKey::Character(SmolStr::new("-")), - 13 => IcedKey::Character(SmolStr::new("=")), - 14 => IcedKey::Named(Named::Backspace), - 15 => IcedKey::Named(Named::Tab), - 16 => IcedKey::Character(SmolStr::new("q")), - 17 => IcedKey::Character(SmolStr::new("w")), - 18 => IcedKey::Character(SmolStr::new("e")), - 19 => IcedKey::Character(SmolStr::new("r")), - 20 => IcedKey::Character(SmolStr::new("t")), - 21 => IcedKey::Character(SmolStr::new("y")), - 22 => IcedKey::Character(SmolStr::new("u")), - 23 => IcedKey::Character(SmolStr::new("i")), - 24 => IcedKey::Character(SmolStr::new("o")), - 25 => IcedKey::Character(SmolStr::new("p")), - 26 => IcedKey::Character(SmolStr::new("[")), - 27 => IcedKey::Character(SmolStr::new("]")), - 28 => IcedKey::Named(Named::Enter), - 29 | 97 => IcedKey::Named(Named::Control), - 30 => IcedKey::Character(SmolStr::new("a")), - 31 => IcedKey::Character(SmolStr::new("s")), - 32 => IcedKey::Character(SmolStr::new("d")), - 33 => IcedKey::Character(SmolStr::new("f")), - 34 => IcedKey::Character(SmolStr::new("g")), - 35 => IcedKey::Character(SmolStr::new("h")), - 36 => IcedKey::Character(SmolStr::new("j")), - 37 => IcedKey::Character(SmolStr::new("k")), - 38 => IcedKey::Character(SmolStr::new("l")), - 39 => IcedKey::Character(SmolStr::new(";")), - 40 => IcedKey::Character(SmolStr::new("'")), - 41 => IcedKey::Character(SmolStr::new("'")), - 42 | 54 => IcedKey::Named(Named::Shift), - 43 => IcedKey::Character(SmolStr::new("\\")), - 44 => IcedKey::Character(SmolStr::new("z")), - 45 => IcedKey::Character(SmolStr::new("x")), - 46 => IcedKey::Character(SmolStr::new("c")), - 47 => IcedKey::Character(SmolStr::new("v")), - 48 => IcedKey::Character(SmolStr::new("b")), - 49 => IcedKey::Character(SmolStr::new("n")), - 50 => IcedKey::Character(SmolStr::new("m")), - 51 => IcedKey::Character(SmolStr::new(",")), - 52 => IcedKey::Character(SmolStr::new(".")), - 53 => IcedKey::Character(SmolStr::new("/")), - // TODO: 55 - 56 | 100 => IcedKey::Named(Named::Alt), - 57 => IcedKey::Named(Named::Space), - 58 => IcedKey::Named(Named::CapsLock), - 59 => IcedKey::Named(Named::F1), - 60 => IcedKey::Named(Named::F2), - 61 => IcedKey::Named(Named::F3), - 62 => IcedKey::Named(Named::F4), - 63 => IcedKey::Named(Named::F5), - 64 => IcedKey::Named(Named::F6), - 65 => IcedKey::Named(Named::F7), - 66 => IcedKey::Named(Named::F8), - 67 => IcedKey::Named(Named::F9), - 68 => IcedKey::Named(Named::F10), - 69 => IcedKey::Named(Named::NumLock), - 70 => IcedKey::Named(Named::ScrollLock), - 71 => IcedKey::Character(SmolStr::new("7")), - 72 => IcedKey::Character(SmolStr::new("8")), - 73 => IcedKey::Character(SmolStr::new("9")), - 74 => IcedKey::Character(SmolStr::new("-")), - 75 => IcedKey::Character(SmolStr::new("4")), - 76 => IcedKey::Character(SmolStr::new("5")), - 77 => IcedKey::Character(SmolStr::new("6")), - 78 => IcedKey::Character(SmolStr::new("+")), - 79 => IcedKey::Character(SmolStr::new("1")), - 80 => IcedKey::Character(SmolStr::new("2")), - 81 => IcedKey::Character(SmolStr::new("3")), - 82 => IcedKey::Character(SmolStr::new("0")), - 103 => IcedKey::Named(Named::ArrowUp), - 104 => IcedKey::Named(Named::PageUp), - 105 => IcedKey::Named(Named::ArrowLeft), - 106 => IcedKey::Named(Named::ArrowRight), - 107 => IcedKey::Named(Named::End), - 108 => IcedKey::Named(Named::ArrowDown), - _ => IcedKey::Unidentified, - } -} - -pub fn text_from_key(key: &IcedKey) -> Option { - use iced_core::keyboard::key::Named; - match key { - IcedKey::Character(c) => Some(c.clone()), - IcedKey::Named(Named::Space) => Some(SmolStr::new(" ")), - _ => None, - } -} - /// Converts a `VirtualKeyCode` from [`winit`] to an [`iced`] key code. /// /// [`winit`]: https://github.com/rust-windowing/winit diff --git a/iced_layershell/src/event.rs b/iced_layershell/src/event.rs index 75eff5d..7041bc7 100644 --- a/iced_layershell/src/event.rs +++ b/iced_layershell/src/event.rs @@ -1,5 +1,4 @@ use layershellev::id::Id; -use layershellev::key::KeyModifierType; use layershellev::reexport::wayland_client::{ButtonState, KeyState, WEnum}; use layershellev::KeyEvent as LayerShellKeyEvent; use layershellev::{DispatchMessage, WindowWrapper}; @@ -27,9 +26,6 @@ impl From> for IcedKeyState { } } -fn modifier_from_layershell_to_iced(modifier: KeyModifierType) -> IcedModifiers { - IcedModifiers::from_bits(modifier.bits()).unwrap_or(IcedModifiers::empty()) -} #[derive(Debug, Clone)] pub enum WindowEvent { @@ -125,16 +121,7 @@ impl From<&DispatchMessage> for IcedLayerEvent { DispatchMessage::PrefredScale(scale) => { IcedLayerEvent::Window(WindowEvent::ScaleChanged(*scale)) } - DispatchMessage::KeyBoard { - state, - key, - modifier, - .. - } => IcedLayerEvent::Window(WindowEvent::Keyboard { - state: (*state).into(), - key: *key, - modifiers: modifier_from_layershell_to_iced(*modifier), - }), + DispatchMessage::KeyboardInput { event, is_synthetic, diff --git a/iced_layershell/src/multi_window.rs b/iced_layershell/src/multi_window.rs index 6116090..307e13c 100644 --- a/iced_layershell/src/multi_window.rs +++ b/iced_layershell/src/multi_window.rs @@ -22,10 +22,7 @@ use iced_style::application::StyleSheet; use iced_futures::{Executor, Runtime, Subscription}; -use layershellev::{ - reexport::wayland_client::{KeyState, WEnum}, - LayerEvent, ReturnData, WindowState, -}; +use layershellev::{LayerEvent, ReturnData, WindowState}; use futures::{channel::mpsc, SinkExt, StreamExt}; @@ -198,8 +195,6 @@ where let mut context = task::Context::from_waker(task::noop_waker_ref()); let mut pointer_serial: u32 = 0; - let mut key_event: Option> = None; - let mut key_ping_count: u32 = 400; let _ = ev.running_with_proxy(message_receiver, move |event, ev, index| { use layershellev::DispatchMessage; @@ -226,14 +221,6 @@ where DispatchMessage::MouseEnter { serial, .. } => { pointer_serial = *serial; } - DispatchMessage::KeyBoard { state, .. } => { - if let WEnum::Value(KeyState::Pressed) = state { - key_event = Some(message.into()); - } else { - key_event = None; - key_ping_count = 400; - } - } _ => {} } @@ -241,36 +228,7 @@ where .start_send(MultiWindowIcedLayerEvent(id, message.into())) .expect("Cannot send"); } - LayerEvent::NormalDispatch => match &key_event { - Some(keyevent) => { - if let IcedLayerEvent::Window(windowevent) = keyevent { - let event = IcedLayerEvent::Window(windowevent.clone()); - if key_ping_count > 70 && key_ping_count < 74 { - event_sender - .start_send(MultiWindowIcedLayerEvent(id, event)) - .expect("Cannot send"); - key_ping_count = 0; - } else { - event_sender - .start_send(MultiWindowIcedLayerEvent( - id, - IcedLayerEvent::NormalUpdate, - )) - .expect("Cannot send"); - } - if key_ping_count >= 74 { - key_ping_count -= 1; - } else { - key_ping_count += 1; - } - } - } - None => { - event_sender - .start_send(MultiWindowIcedLayerEvent(id, IcedLayerEvent::NormalUpdate)) - .expect("Cannot send"); - } - }, + LayerEvent::UserEvent(event) => { event_sender .start_send(MultiWindowIcedLayerEvent( @@ -279,6 +237,11 @@ where )) .ok(); } + LayerEvent::NormalDispatch => { + event_sender + .start_send(MultiWindowIcedLayerEvent(id, IcedLayerEvent::NormalUpdate)) + .expect("Cannot send"); + } _ => {} } let poll = instance.as_mut().poll(&mut context); diff --git a/layershellev/src/events.rs b/layershellev/src/events.rs index dd84485..7797c66 100644 --- a/layershellev/src/events.rs +++ b/layershellev/src/events.rs @@ -2,7 +2,6 @@ use wayland_client::{ globals::GlobalList, protocol::{ wl_buffer::WlBuffer, - wl_keyboard::KeyState, wl_output::WlOutput, wl_pointer::{self, ButtonState, WlPointer}, wl_shm::WlShm, @@ -10,10 +9,7 @@ use wayland_client::{ QueueHandle, WEnum, }; -use crate::{ - key::KeyModifierType, - xkb_keyboard::{KeyEvent, ModifiersState}, -}; +use crate::xkb_keyboard::{KeyEvent, ModifiersState}; use super::WindowState; @@ -143,13 +139,7 @@ pub(crate) enum DispatchMessageInner { x: f64, y: f64, }, - KeyBoard { - state: WEnum, - modifier: KeyModifierType, - serial: u32, - key: u32, - time: u32, - }, + ModifiersChanged(ModifiersState), KeyboardInput { event: KeyEvent, @@ -228,14 +218,7 @@ pub enum DispatchMessage { x: f64, y: f64, }, - /// forward the event of wayland-keyboard - KeyBoard { - state: WEnum, - modifier: KeyModifierType, - serial: u32, - key: u32, - time: u32, - }, + ModifiersChanged(ModifiersState), KeyboardInput { event: KeyEvent, @@ -316,19 +299,6 @@ impl From for DispatchMessage { DispatchMessageInner::TouchMotion { time, id, x, y } => { DispatchMessage::TouchMotion { time, id, x, y } } - DispatchMessageInner::KeyBoard { - state, - modifier, - serial, - key, - time, - } => DispatchMessage::KeyBoard { - state, - modifier, - serial, - key, - time, - }, DispatchMessageInner::RequestRefresh { width, height } => { DispatchMessage::RequestRefresh { width, height } } diff --git a/layershellev/src/key.rs b/layershellev/src/key.rs deleted file mode 100644 index ffde80a..0000000 --- a/layershellev/src/key.rs +++ /dev/null @@ -1,59 +0,0 @@ -use bitflags::bitflags; - -#[allow(unused)] -mod otherkeys { - pub const LEFT: u32 = 105; - pub const RIGHT: u32 = 106; - pub const DOWN: u32 = 108; - pub const UP: u32 = 103; - pub const ESC: u32 = 1; - pub const SHIFT_LEFT: u32 = 42; - pub const SHIFT_RIGHT: u32 = 54; - pub const MENU: u32 = 139; - pub const CAPS_LOCK: u32 = 58; - pub const CTRL_LEFT: u32 = 29; - pub const CTRL_RIGHT: u32 = 97; - pub const ALT_LEFT: u32 = 56; - pub const ALT_RIGHT: u32 = 100; - - pub const MIN_KEYBOARD: u32 = 999; - pub const CLOSE_KEYBOARD: u32 = 1000; - - pub fn is_unique_key(key: u32) -> bool { - key == MIN_KEYBOARD || key == CLOSE_KEYBOARD - } -} - -bitflags! { - #[allow(unused)] - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] - pub struct KeyModifierType : u32 { - const NoMod = 0; - const Shift = 1; - const CapsLock = 2; - const Ctrl = 4; - const Alt = 8; - const Super = 64; - const AltGr = 128; - } -} - -impl From for KeyModifierType { - fn from(value: u32) -> Self { - match value { - otherkeys::CAPS_LOCK => KeyModifierType::CapsLock, - otherkeys::SHIFT_LEFT | otherkeys::SHIFT_RIGHT => KeyModifierType::Shift, - otherkeys::MENU => KeyModifierType::Super, - otherkeys::CTRL_LEFT | otherkeys::CTRL_RIGHT => KeyModifierType::Ctrl, - otherkeys::ALT_LEFT | otherkeys::ALT_RIGHT => KeyModifierType::Alt, - _ => KeyModifierType::NoMod, - } - } -} - -impl From for KeyModifierType { - fn from(value: usize) -> Self { - let value = value as u32; - value.into() - } -} diff --git a/layershellev/src/keymap.rs b/layershellev/src/keymap.rs index 349837a..59d022d 100644 --- a/layershellev/src/keymap.rs +++ b/layershellev/src/keymap.rs @@ -1,13 +1,6 @@ //! XKB keymap. -use std::ffi::c_char; -use std::ptr::NonNull; - -use xkb::XKB_MOD_INVALID; -use xkbcommon_dl::{self as xkb, xkb_keymap, xkb_mod_index_t}; - use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey}; -use crate::xkb_keyboard::XKBH; /// Map the raw X11-style keycode to the `KeyCode` enum. /// @@ -276,6 +269,9 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { }) } + +// NOTE: maybe one day need it +#[allow(unused)] pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option { let code = match key { PhysicalKey::Code(code) => code, @@ -896,27 +892,3 @@ pub fn keysym_location(keysym: u32) -> KeyLocation { _ => KeyLocation::Standard, } } - -#[derive(Default, Debug, Clone, Copy)] -pub struct ModsIndices { - pub shift: Option, - pub caps: Option, - pub ctrl: Option, - pub alt: Option, - pub num: Option, - pub mod3: Option, - pub logo: Option, - pub mod5: Option, -} - -fn mod_index_for_name(keymap: NonNull, name: &[u8]) -> Option { - unsafe { - let mod_index = - (XKBH.xkb_keymap_mod_get_index)(keymap.as_ptr(), name.as_ptr() as *const c_char); - if mod_index == XKB_MOD_INVALID { - None - } else { - Some(mod_index) - } - } -} diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index 7014f21..0101754 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -170,11 +170,8 @@ use events::DispatchMessageInner; pub mod id; -pub mod key; - pub use events::{AxisScroll, DispatchMessage, LayerEvent, ReturnData, XdgInfoChangedType}; -use key::KeyModifierType; use strtoshape::str_to_shape; use wayland_client::{ delegate_noop, @@ -520,9 +517,6 @@ pub struct WindowState { exclusive_zone: Option, margin: Option<(i32, i32, i32, i32)>, - // keyboard - modifier: KeyModifierType, - // settings use_display_handle: bool, } @@ -725,8 +719,6 @@ impl Default for WindowState { exclusive_zone: None, margin: None, - modifier: KeyModifierType::NoMod, - use_display_handle: false, } } From 0ff5749d0a34c3053d55e611fe5a4c28848e06cb Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 22:04:33 +0859 Subject: [PATCH 09/16] chore: finally tidy up --- iced_layershell/src/application/state.rs | 2 +- iced_layershell/src/conversion.rs | 6 +- iced_layershell/src/conversion/keymap.rs | 631 +++++++++++----------- iced_layershell/src/event.rs | 5 +- iced_layershell/src/multi_window/state.rs | 2 +- layershellev/examples/simplelayer.rs | 60 +- layershellev/src/events.rs | 4 +- layershellev/src/keyboard.rs | 31 -- layershellev/src/lib.rs | 70 +-- layershellev/src/xkb_keyboard.rs | 72 +-- 10 files changed, 344 insertions(+), 539 deletions(-) diff --git a/iced_layershell/src/application/state.rs b/iced_layershell/src/application/state.rs index fecb833..bf98efe 100644 --- a/iced_layershell/src/application/state.rs +++ b/iced_layershell/src/application/state.rs @@ -2,7 +2,7 @@ use crate::application::Application; use iced_core::{mouse as IcedMouse, Color, Point, Size}; use iced_graphics::Viewport; use iced_style::application::{self, StyleSheet}; -use layershellev::ModifiersState; +use layershellev::keyboard::ModifiersState; use crate::event::WindowEvent; diff --git a/iced_layershell/src/conversion.rs b/iced_layershell/src/conversion.rs index 5808be9..70bf832 100644 --- a/iced_layershell/src/conversion.rs +++ b/iced_layershell/src/conversion.rs @@ -6,9 +6,9 @@ use iced_core::SmolStr; use iced_core::{keyboard, mouse, Event as IcedEvent}; use keymap::key; use layershellev::keyboard::KeyLocation; -use layershellev::ElementState; -use layershellev::KeyEvent as LayerShellKeyEvent; -use layershellev::ModifiersState; +use layershellev::xkb_keyboard::ElementState; +use layershellev::xkb_keyboard::KeyEvent as LayerShellKeyEvent; +use layershellev::keyboard::ModifiersState; #[allow(unused)] pub fn window_event( diff --git a/iced_layershell/src/conversion/keymap.rs b/iced_layershell/src/conversion/keymap.rs index 1692758..8f29460 100644 --- a/iced_layershell/src/conversion/keymap.rs +++ b/iced_layershell/src/conversion/keymap.rs @@ -1,336 +1,329 @@ +use layershellev::keyboard::ModifiersState; /// Converts a `VirtualKeyCode` from [`winit`] to an [`iced`] key code. /// /// [`winit`]: https://github.com/rust-windowing/winit /// [`iced`]: https://github.com/iced-rs/iced/tree/0.12 pub fn key(key: layershellev::keyboard::Key) -> iced_core::keyboard::Key { + use iced_core::keyboard; use iced_core::keyboard::key::Named; use layershellev::keyboard::NamedKey; - use iced_core::keyboard; match key { layershellev::keyboard::Key::Character(c) => keyboard::Key::Character(c), - layershellev::keyboard::Key::Named(named_key) => { - keyboard::Key::Named(match named_key { - NamedKey::Alt => Named::Alt, - NamedKey::AltGraph => Named::AltGraph, - NamedKey::CapsLock => Named::CapsLock, - NamedKey::Control => Named::Control, - NamedKey::Fn => Named::Fn, - NamedKey::FnLock => Named::FnLock, - NamedKey::NumLock => Named::NumLock, - NamedKey::ScrollLock => Named::ScrollLock, - NamedKey::Shift => Named::Shift, - NamedKey::Symbol => Named::Symbol, - NamedKey::SymbolLock => Named::SymbolLock, - NamedKey::Meta => Named::Meta, - NamedKey::Hyper => Named::Hyper, - NamedKey::Super => Named::Super, - NamedKey::Enter => Named::Enter, - NamedKey::Tab => Named::Tab, - NamedKey::Space => Named::Space, - NamedKey::ArrowDown => Named::ArrowDown, - NamedKey::ArrowLeft => Named::ArrowLeft, - NamedKey::ArrowRight => Named::ArrowRight, - NamedKey::ArrowUp => Named::ArrowUp, - NamedKey::End => Named::End, - NamedKey::Home => Named::Home, - NamedKey::PageDown => Named::PageDown, - NamedKey::PageUp => Named::PageUp, - NamedKey::Backspace => Named::Backspace, - NamedKey::Clear => Named::Clear, - NamedKey::Copy => Named::Copy, - NamedKey::CrSel => Named::CrSel, - NamedKey::Cut => Named::Cut, - NamedKey::Delete => Named::Delete, - NamedKey::EraseEof => Named::EraseEof, - NamedKey::ExSel => Named::ExSel, - NamedKey::Insert => Named::Insert, - NamedKey::Paste => Named::Paste, - NamedKey::Redo => Named::Redo, - NamedKey::Undo => Named::Undo, - NamedKey::Accept => Named::Accept, - NamedKey::Again => Named::Again, - NamedKey::Attn => Named::Attn, - NamedKey::Cancel => Named::Cancel, - NamedKey::ContextMenu => Named::ContextMenu, - NamedKey::Escape => Named::Escape, - NamedKey::Execute => Named::Execute, - NamedKey::Find => Named::Find, - NamedKey::Help => Named::Help, - NamedKey::Pause => Named::Pause, - NamedKey::Play => Named::Play, - NamedKey::Props => Named::Props, - NamedKey::Select => Named::Select, - NamedKey::ZoomIn => Named::ZoomIn, - NamedKey::ZoomOut => Named::ZoomOut, - NamedKey::BrightnessDown => Named::BrightnessDown, - NamedKey::BrightnessUp => Named::BrightnessUp, - NamedKey::Eject => Named::Eject, - NamedKey::LogOff => Named::LogOff, - NamedKey::Power => Named::Power, - NamedKey::PowerOff => Named::PowerOff, - NamedKey::PrintScreen => Named::PrintScreen, - NamedKey::Hibernate => Named::Hibernate, - NamedKey::Standby => Named::Standby, - NamedKey::WakeUp => Named::WakeUp, - NamedKey::AllCandidates => Named::AllCandidates, - NamedKey::Alphanumeric => Named::Alphanumeric, - NamedKey::CodeInput => Named::CodeInput, - NamedKey::Compose => Named::Compose, - NamedKey::Convert => Named::Convert, - NamedKey::FinalMode => Named::FinalMode, - NamedKey::GroupFirst => Named::GroupFirst, - NamedKey::GroupLast => Named::GroupLast, - NamedKey::GroupNext => Named::GroupNext, - NamedKey::GroupPrevious => Named::GroupPrevious, - NamedKey::ModeChange => Named::ModeChange, - NamedKey::NextCandidate => Named::NextCandidate, - NamedKey::NonConvert => Named::NonConvert, - NamedKey::PreviousCandidate => Named::PreviousCandidate, - NamedKey::Process => Named::Process, - NamedKey::SingleCandidate => Named::SingleCandidate, - NamedKey::HangulMode => Named::HangulMode, - NamedKey::HanjaMode => Named::HanjaMode, - NamedKey::JunjaMode => Named::JunjaMode, - NamedKey::Eisu => Named::Eisu, - NamedKey::Hankaku => Named::Hankaku, - NamedKey::Hiragana => Named::Hiragana, - NamedKey::HiraganaKatakana => Named::HiraganaKatakana, - NamedKey::KanaMode => Named::KanaMode, - NamedKey::KanjiMode => Named::KanjiMode, - NamedKey::Katakana => Named::Katakana, - NamedKey::Romaji => Named::Romaji, - NamedKey::Zenkaku => Named::Zenkaku, - NamedKey::ZenkakuHankaku => Named::ZenkakuHankaku, - NamedKey::Soft1 => Named::Soft1, - NamedKey::Soft2 => Named::Soft2, - NamedKey::Soft3 => Named::Soft3, - NamedKey::Soft4 => Named::Soft4, - NamedKey::ChannelDown => Named::ChannelDown, - NamedKey::ChannelUp => Named::ChannelUp, - NamedKey::Close => Named::Close, - NamedKey::MailForward => Named::MailForward, - NamedKey::MailReply => Named::MailReply, - NamedKey::MailSend => Named::MailSend, - NamedKey::MediaClose => Named::MediaClose, - NamedKey::MediaFastForward => Named::MediaFastForward, - NamedKey::MediaPause => Named::MediaPause, - NamedKey::MediaPlay => Named::MediaPlay, - NamedKey::MediaPlayPause => Named::MediaPlayPause, - NamedKey::MediaRecord => Named::MediaRecord, - NamedKey::MediaRewind => Named::MediaRewind, - NamedKey::MediaStop => Named::MediaStop, - NamedKey::MediaTrackNext => Named::MediaTrackNext, - NamedKey::MediaTrackPrevious => Named::MediaTrackPrevious, - NamedKey::New => Named::New, - NamedKey::Open => Named::Open, - NamedKey::Print => Named::Print, - NamedKey::Save => Named::Save, - NamedKey::SpellCheck => Named::SpellCheck, - NamedKey::Key11 => Named::Key11, - NamedKey::Key12 => Named::Key12, - NamedKey::AudioBalanceLeft => Named::AudioBalanceLeft, - NamedKey::AudioBalanceRight => Named::AudioBalanceRight, - NamedKey::AudioBassBoostDown => Named::AudioBassBoostDown, - NamedKey::AudioBassBoostToggle => Named::AudioBassBoostToggle, - NamedKey::AudioBassBoostUp => Named::AudioBassBoostUp, - NamedKey::AudioFaderFront => Named::AudioFaderFront, - NamedKey::AudioFaderRear => Named::AudioFaderRear, - NamedKey::AudioSurroundModeNext => Named::AudioSurroundModeNext, - NamedKey::AudioTrebleDown => Named::AudioTrebleDown, - NamedKey::AudioTrebleUp => Named::AudioTrebleUp, - NamedKey::AudioVolumeDown => Named::AudioVolumeDown, - NamedKey::AudioVolumeUp => Named::AudioVolumeUp, - NamedKey::AudioVolumeMute => Named::AudioVolumeMute, - NamedKey::MicrophoneToggle => Named::MicrophoneToggle, - NamedKey::MicrophoneVolumeDown => Named::MicrophoneVolumeDown, - NamedKey::MicrophoneVolumeUp => Named::MicrophoneVolumeUp, - NamedKey::MicrophoneVolumeMute => Named::MicrophoneVolumeMute, - NamedKey::SpeechCorrectionList => Named::SpeechCorrectionList, - NamedKey::SpeechInputToggle => Named::SpeechInputToggle, - NamedKey::LaunchApplication1 => Named::LaunchApplication1, - NamedKey::LaunchApplication2 => Named::LaunchApplication2, - NamedKey::LaunchCalendar => Named::LaunchCalendar, - NamedKey::LaunchContacts => Named::LaunchContacts, - NamedKey::LaunchMail => Named::LaunchMail, - NamedKey::LaunchMediaPlayer => Named::LaunchMediaPlayer, - NamedKey::LaunchMusicPlayer => Named::LaunchMusicPlayer, - NamedKey::LaunchPhone => Named::LaunchPhone, - NamedKey::LaunchScreenSaver => Named::LaunchScreenSaver, - NamedKey::LaunchSpreadsheet => Named::LaunchSpreadsheet, - NamedKey::LaunchWebBrowser => Named::LaunchWebBrowser, - NamedKey::LaunchWebCam => Named::LaunchWebCam, - NamedKey::LaunchWordProcessor => Named::LaunchWordProcessor, - NamedKey::BrowserBack => Named::BrowserBack, - NamedKey::BrowserFavorites => Named::BrowserFavorites, - NamedKey::BrowserForward => Named::BrowserForward, - NamedKey::BrowserHome => Named::BrowserHome, - NamedKey::BrowserRefresh => Named::BrowserRefresh, - NamedKey::BrowserSearch => Named::BrowserSearch, - NamedKey::BrowserStop => Named::BrowserStop, - NamedKey::AppSwitch => Named::AppSwitch, - NamedKey::Call => Named::Call, - NamedKey::Camera => Named::Camera, - NamedKey::CameraFocus => Named::CameraFocus, - NamedKey::EndCall => Named::EndCall, - NamedKey::GoBack => Named::GoBack, - NamedKey::GoHome => Named::GoHome, - NamedKey::HeadsetHook => Named::HeadsetHook, - NamedKey::LastNumberRedial => Named::LastNumberRedial, - NamedKey::Notification => Named::Notification, - NamedKey::MannerMode => Named::MannerMode, - NamedKey::VoiceDial => Named::VoiceDial, - NamedKey::TV => Named::TV, - NamedKey::TV3DMode => Named::TV3DMode, - NamedKey::TVAntennaCable => Named::TVAntennaCable, - NamedKey::TVAudioDescription => Named::TVAudioDescription, - NamedKey::TVAudioDescriptionMixDown => { - Named::TVAudioDescriptionMixDown - } - NamedKey::TVAudioDescriptionMixUp => { - Named::TVAudioDescriptionMixUp - } - NamedKey::TVContentsMenu => Named::TVContentsMenu, - NamedKey::TVDataService => Named::TVDataService, - NamedKey::TVInput => Named::TVInput, - NamedKey::TVInputComponent1 => Named::TVInputComponent1, - NamedKey::TVInputComponent2 => Named::TVInputComponent2, - NamedKey::TVInputComposite1 => Named::TVInputComposite1, - NamedKey::TVInputComposite2 => Named::TVInputComposite2, - NamedKey::TVInputHDMI1 => Named::TVInputHDMI1, - NamedKey::TVInputHDMI2 => Named::TVInputHDMI2, - NamedKey::TVInputHDMI3 => Named::TVInputHDMI3, - NamedKey::TVInputHDMI4 => Named::TVInputHDMI4, - NamedKey::TVInputVGA1 => Named::TVInputVGA1, - NamedKey::TVMediaContext => Named::TVMediaContext, - NamedKey::TVNetwork => Named::TVNetwork, - NamedKey::TVNumberEntry => Named::TVNumberEntry, - NamedKey::TVPower => Named::TVPower, - NamedKey::TVRadioService => Named::TVRadioService, - NamedKey::TVSatellite => Named::TVSatellite, - NamedKey::TVSatelliteBS => Named::TVSatelliteBS, - NamedKey::TVSatelliteCS => Named::TVSatelliteCS, - NamedKey::TVSatelliteToggle => Named::TVSatelliteToggle, - NamedKey::TVTerrestrialAnalog => Named::TVTerrestrialAnalog, - NamedKey::TVTerrestrialDigital => Named::TVTerrestrialDigital, - NamedKey::TVTimer => Named::TVTimer, - NamedKey::AVRInput => Named::AVRInput, - NamedKey::AVRPower => Named::AVRPower, - NamedKey::ColorF0Red => Named::ColorF0Red, - NamedKey::ColorF1Green => Named::ColorF1Green, - NamedKey::ColorF2Yellow => Named::ColorF2Yellow, - NamedKey::ColorF3Blue => Named::ColorF3Blue, - NamedKey::ColorF4Grey => Named::ColorF4Grey, - NamedKey::ColorF5Brown => Named::ColorF5Brown, - NamedKey::ClosedCaptionToggle => Named::ClosedCaptionToggle, - NamedKey::Dimmer => Named::Dimmer, - NamedKey::DisplaySwap => Named::DisplaySwap, - NamedKey::DVR => Named::DVR, - NamedKey::Exit => Named::Exit, - NamedKey::FavoriteClear0 => Named::FavoriteClear0, - NamedKey::FavoriteClear1 => Named::FavoriteClear1, - NamedKey::FavoriteClear2 => Named::FavoriteClear2, - NamedKey::FavoriteClear3 => Named::FavoriteClear3, - NamedKey::FavoriteRecall0 => Named::FavoriteRecall0, - NamedKey::FavoriteRecall1 => Named::FavoriteRecall1, - NamedKey::FavoriteRecall2 => Named::FavoriteRecall2, - NamedKey::FavoriteRecall3 => Named::FavoriteRecall3, - NamedKey::FavoriteStore0 => Named::FavoriteStore0, - NamedKey::FavoriteStore1 => Named::FavoriteStore1, - NamedKey::FavoriteStore2 => Named::FavoriteStore2, - NamedKey::FavoriteStore3 => Named::FavoriteStore3, - NamedKey::Guide => Named::Guide, - NamedKey::GuideNextDay => Named::GuideNextDay, - NamedKey::GuidePreviousDay => Named::GuidePreviousDay, - NamedKey::Info => Named::Info, - NamedKey::InstantReplay => Named::InstantReplay, - NamedKey::Link => Named::Link, - NamedKey::ListProgram => Named::ListProgram, - NamedKey::LiveContent => Named::LiveContent, - NamedKey::Lock => Named::Lock, - NamedKey::MediaApps => Named::MediaApps, - NamedKey::MediaAudioTrack => Named::MediaAudioTrack, - NamedKey::MediaLast => Named::MediaLast, - NamedKey::MediaSkipBackward => Named::MediaSkipBackward, - NamedKey::MediaSkipForward => Named::MediaSkipForward, - NamedKey::MediaStepBackward => Named::MediaStepBackward, - NamedKey::MediaStepForward => Named::MediaStepForward, - NamedKey::MediaTopMenu => Named::MediaTopMenu, - NamedKey::NavigateIn => Named::NavigateIn, - NamedKey::NavigateNext => Named::NavigateNext, - NamedKey::NavigateOut => Named::NavigateOut, - NamedKey::NavigatePrevious => Named::NavigatePrevious, - NamedKey::NextFavoriteChannel => Named::NextFavoriteChannel, - NamedKey::NextUserProfile => Named::NextUserProfile, - NamedKey::OnDemand => Named::OnDemand, - NamedKey::Pairing => Named::Pairing, - NamedKey::PinPDown => Named::PinPDown, - NamedKey::PinPMove => Named::PinPMove, - NamedKey::PinPToggle => Named::PinPToggle, - NamedKey::PinPUp => Named::PinPUp, - NamedKey::PlaySpeedDown => Named::PlaySpeedDown, - NamedKey::PlaySpeedReset => Named::PlaySpeedReset, - NamedKey::PlaySpeedUp => Named::PlaySpeedUp, - NamedKey::RandomToggle => Named::RandomToggle, - NamedKey::RcLowBattery => Named::RcLowBattery, - NamedKey::RecordSpeedNext => Named::RecordSpeedNext, - NamedKey::RfBypass => Named::RfBypass, - NamedKey::ScanChannelsToggle => Named::ScanChannelsToggle, - NamedKey::ScreenModeNext => Named::ScreenModeNext, - NamedKey::Settings => Named::Settings, - NamedKey::SplitScreenToggle => Named::SplitScreenToggle, - NamedKey::STBInput => Named::STBInput, - NamedKey::STBPower => Named::STBPower, - NamedKey::Subtitle => Named::Subtitle, - NamedKey::Teletext => Named::Teletext, - NamedKey::VideoModeNext => Named::VideoModeNext, - NamedKey::Wink => Named::Wink, - NamedKey::ZoomToggle => Named::ZoomToggle, - NamedKey::F1 => Named::F1, - NamedKey::F2 => Named::F2, - NamedKey::F3 => Named::F3, - NamedKey::F4 => Named::F4, - NamedKey::F5 => Named::F5, - NamedKey::F6 => Named::F6, - NamedKey::F7 => Named::F7, - NamedKey::F8 => Named::F8, - NamedKey::F9 => Named::F9, - NamedKey::F10 => Named::F10, - NamedKey::F11 => Named::F11, - NamedKey::F12 => Named::F12, - NamedKey::F13 => Named::F13, - NamedKey::F14 => Named::F14, - NamedKey::F15 => Named::F15, - NamedKey::F16 => Named::F16, - NamedKey::F17 => Named::F17, - NamedKey::F18 => Named::F18, - NamedKey::F19 => Named::F19, - NamedKey::F20 => Named::F20, - NamedKey::F21 => Named::F21, - NamedKey::F22 => Named::F22, - NamedKey::F23 => Named::F23, - NamedKey::F24 => Named::F24, - NamedKey::F25 => Named::F25, - NamedKey::F26 => Named::F26, - NamedKey::F27 => Named::F27, - NamedKey::F28 => Named::F28, - NamedKey::F29 => Named::F29, - NamedKey::F30 => Named::F30, - NamedKey::F31 => Named::F31, - NamedKey::F32 => Named::F32, - NamedKey::F33 => Named::F33, - NamedKey::F34 => Named::F34, - NamedKey::F35 => Named::F35, - _ => return keyboard::Key::Unidentified, - }) - } + layershellev::keyboard::Key::Named(named_key) => keyboard::Key::Named(match named_key { + NamedKey::Alt => Named::Alt, + NamedKey::AltGraph => Named::AltGraph, + NamedKey::CapsLock => Named::CapsLock, + NamedKey::Control => Named::Control, + NamedKey::Fn => Named::Fn, + NamedKey::FnLock => Named::FnLock, + NamedKey::NumLock => Named::NumLock, + NamedKey::ScrollLock => Named::ScrollLock, + NamedKey::Shift => Named::Shift, + NamedKey::Symbol => Named::Symbol, + NamedKey::SymbolLock => Named::SymbolLock, + NamedKey::Meta => Named::Meta, + NamedKey::Hyper => Named::Hyper, + NamedKey::Super => Named::Super, + NamedKey::Enter => Named::Enter, + NamedKey::Tab => Named::Tab, + NamedKey::Space => Named::Space, + NamedKey::ArrowDown => Named::ArrowDown, + NamedKey::ArrowLeft => Named::ArrowLeft, + NamedKey::ArrowRight => Named::ArrowRight, + NamedKey::ArrowUp => Named::ArrowUp, + NamedKey::End => Named::End, + NamedKey::Home => Named::Home, + NamedKey::PageDown => Named::PageDown, + NamedKey::PageUp => Named::PageUp, + NamedKey::Backspace => Named::Backspace, + NamedKey::Clear => Named::Clear, + NamedKey::Copy => Named::Copy, + NamedKey::CrSel => Named::CrSel, + NamedKey::Cut => Named::Cut, + NamedKey::Delete => Named::Delete, + NamedKey::EraseEof => Named::EraseEof, + NamedKey::ExSel => Named::ExSel, + NamedKey::Insert => Named::Insert, + NamedKey::Paste => Named::Paste, + NamedKey::Redo => Named::Redo, + NamedKey::Undo => Named::Undo, + NamedKey::Accept => Named::Accept, + NamedKey::Again => Named::Again, + NamedKey::Attn => Named::Attn, + NamedKey::Cancel => Named::Cancel, + NamedKey::ContextMenu => Named::ContextMenu, + NamedKey::Escape => Named::Escape, + NamedKey::Execute => Named::Execute, + NamedKey::Find => Named::Find, + NamedKey::Help => Named::Help, + NamedKey::Pause => Named::Pause, + NamedKey::Play => Named::Play, + NamedKey::Props => Named::Props, + NamedKey::Select => Named::Select, + NamedKey::ZoomIn => Named::ZoomIn, + NamedKey::ZoomOut => Named::ZoomOut, + NamedKey::BrightnessDown => Named::BrightnessDown, + NamedKey::BrightnessUp => Named::BrightnessUp, + NamedKey::Eject => Named::Eject, + NamedKey::LogOff => Named::LogOff, + NamedKey::Power => Named::Power, + NamedKey::PowerOff => Named::PowerOff, + NamedKey::PrintScreen => Named::PrintScreen, + NamedKey::Hibernate => Named::Hibernate, + NamedKey::Standby => Named::Standby, + NamedKey::WakeUp => Named::WakeUp, + NamedKey::AllCandidates => Named::AllCandidates, + NamedKey::Alphanumeric => Named::Alphanumeric, + NamedKey::CodeInput => Named::CodeInput, + NamedKey::Compose => Named::Compose, + NamedKey::Convert => Named::Convert, + NamedKey::FinalMode => Named::FinalMode, + NamedKey::GroupFirst => Named::GroupFirst, + NamedKey::GroupLast => Named::GroupLast, + NamedKey::GroupNext => Named::GroupNext, + NamedKey::GroupPrevious => Named::GroupPrevious, + NamedKey::ModeChange => Named::ModeChange, + NamedKey::NextCandidate => Named::NextCandidate, + NamedKey::NonConvert => Named::NonConvert, + NamedKey::PreviousCandidate => Named::PreviousCandidate, + NamedKey::Process => Named::Process, + NamedKey::SingleCandidate => Named::SingleCandidate, + NamedKey::HangulMode => Named::HangulMode, + NamedKey::HanjaMode => Named::HanjaMode, + NamedKey::JunjaMode => Named::JunjaMode, + NamedKey::Eisu => Named::Eisu, + NamedKey::Hankaku => Named::Hankaku, + NamedKey::Hiragana => Named::Hiragana, + NamedKey::HiraganaKatakana => Named::HiraganaKatakana, + NamedKey::KanaMode => Named::KanaMode, + NamedKey::KanjiMode => Named::KanjiMode, + NamedKey::Katakana => Named::Katakana, + NamedKey::Romaji => Named::Romaji, + NamedKey::Zenkaku => Named::Zenkaku, + NamedKey::ZenkakuHankaku => Named::ZenkakuHankaku, + NamedKey::Soft1 => Named::Soft1, + NamedKey::Soft2 => Named::Soft2, + NamedKey::Soft3 => Named::Soft3, + NamedKey::Soft4 => Named::Soft4, + NamedKey::ChannelDown => Named::ChannelDown, + NamedKey::ChannelUp => Named::ChannelUp, + NamedKey::Close => Named::Close, + NamedKey::MailForward => Named::MailForward, + NamedKey::MailReply => Named::MailReply, + NamedKey::MailSend => Named::MailSend, + NamedKey::MediaClose => Named::MediaClose, + NamedKey::MediaFastForward => Named::MediaFastForward, + NamedKey::MediaPause => Named::MediaPause, + NamedKey::MediaPlay => Named::MediaPlay, + NamedKey::MediaPlayPause => Named::MediaPlayPause, + NamedKey::MediaRecord => Named::MediaRecord, + NamedKey::MediaRewind => Named::MediaRewind, + NamedKey::MediaStop => Named::MediaStop, + NamedKey::MediaTrackNext => Named::MediaTrackNext, + NamedKey::MediaTrackPrevious => Named::MediaTrackPrevious, + NamedKey::New => Named::New, + NamedKey::Open => Named::Open, + NamedKey::Print => Named::Print, + NamedKey::Save => Named::Save, + NamedKey::SpellCheck => Named::SpellCheck, + NamedKey::Key11 => Named::Key11, + NamedKey::Key12 => Named::Key12, + NamedKey::AudioBalanceLeft => Named::AudioBalanceLeft, + NamedKey::AudioBalanceRight => Named::AudioBalanceRight, + NamedKey::AudioBassBoostDown => Named::AudioBassBoostDown, + NamedKey::AudioBassBoostToggle => Named::AudioBassBoostToggle, + NamedKey::AudioBassBoostUp => Named::AudioBassBoostUp, + NamedKey::AudioFaderFront => Named::AudioFaderFront, + NamedKey::AudioFaderRear => Named::AudioFaderRear, + NamedKey::AudioSurroundModeNext => Named::AudioSurroundModeNext, + NamedKey::AudioTrebleDown => Named::AudioTrebleDown, + NamedKey::AudioTrebleUp => Named::AudioTrebleUp, + NamedKey::AudioVolumeDown => Named::AudioVolumeDown, + NamedKey::AudioVolumeUp => Named::AudioVolumeUp, + NamedKey::AudioVolumeMute => Named::AudioVolumeMute, + NamedKey::MicrophoneToggle => Named::MicrophoneToggle, + NamedKey::MicrophoneVolumeDown => Named::MicrophoneVolumeDown, + NamedKey::MicrophoneVolumeUp => Named::MicrophoneVolumeUp, + NamedKey::MicrophoneVolumeMute => Named::MicrophoneVolumeMute, + NamedKey::SpeechCorrectionList => Named::SpeechCorrectionList, + NamedKey::SpeechInputToggle => Named::SpeechInputToggle, + NamedKey::LaunchApplication1 => Named::LaunchApplication1, + NamedKey::LaunchApplication2 => Named::LaunchApplication2, + NamedKey::LaunchCalendar => Named::LaunchCalendar, + NamedKey::LaunchContacts => Named::LaunchContacts, + NamedKey::LaunchMail => Named::LaunchMail, + NamedKey::LaunchMediaPlayer => Named::LaunchMediaPlayer, + NamedKey::LaunchMusicPlayer => Named::LaunchMusicPlayer, + NamedKey::LaunchPhone => Named::LaunchPhone, + NamedKey::LaunchScreenSaver => Named::LaunchScreenSaver, + NamedKey::LaunchSpreadsheet => Named::LaunchSpreadsheet, + NamedKey::LaunchWebBrowser => Named::LaunchWebBrowser, + NamedKey::LaunchWebCam => Named::LaunchWebCam, + NamedKey::LaunchWordProcessor => Named::LaunchWordProcessor, + NamedKey::BrowserBack => Named::BrowserBack, + NamedKey::BrowserFavorites => Named::BrowserFavorites, + NamedKey::BrowserForward => Named::BrowserForward, + NamedKey::BrowserHome => Named::BrowserHome, + NamedKey::BrowserRefresh => Named::BrowserRefresh, + NamedKey::BrowserSearch => Named::BrowserSearch, + NamedKey::BrowserStop => Named::BrowserStop, + NamedKey::AppSwitch => Named::AppSwitch, + NamedKey::Call => Named::Call, + NamedKey::Camera => Named::Camera, + NamedKey::CameraFocus => Named::CameraFocus, + NamedKey::EndCall => Named::EndCall, + NamedKey::GoBack => Named::GoBack, + NamedKey::GoHome => Named::GoHome, + NamedKey::HeadsetHook => Named::HeadsetHook, + NamedKey::LastNumberRedial => Named::LastNumberRedial, + NamedKey::Notification => Named::Notification, + NamedKey::MannerMode => Named::MannerMode, + NamedKey::VoiceDial => Named::VoiceDial, + NamedKey::TV => Named::TV, + NamedKey::TV3DMode => Named::TV3DMode, + NamedKey::TVAntennaCable => Named::TVAntennaCable, + NamedKey::TVAudioDescription => Named::TVAudioDescription, + NamedKey::TVAudioDescriptionMixDown => Named::TVAudioDescriptionMixDown, + NamedKey::TVAudioDescriptionMixUp => Named::TVAudioDescriptionMixUp, + NamedKey::TVContentsMenu => Named::TVContentsMenu, + NamedKey::TVDataService => Named::TVDataService, + NamedKey::TVInput => Named::TVInput, + NamedKey::TVInputComponent1 => Named::TVInputComponent1, + NamedKey::TVInputComponent2 => Named::TVInputComponent2, + NamedKey::TVInputComposite1 => Named::TVInputComposite1, + NamedKey::TVInputComposite2 => Named::TVInputComposite2, + NamedKey::TVInputHDMI1 => Named::TVInputHDMI1, + NamedKey::TVInputHDMI2 => Named::TVInputHDMI2, + NamedKey::TVInputHDMI3 => Named::TVInputHDMI3, + NamedKey::TVInputHDMI4 => Named::TVInputHDMI4, + NamedKey::TVInputVGA1 => Named::TVInputVGA1, + NamedKey::TVMediaContext => Named::TVMediaContext, + NamedKey::TVNetwork => Named::TVNetwork, + NamedKey::TVNumberEntry => Named::TVNumberEntry, + NamedKey::TVPower => Named::TVPower, + NamedKey::TVRadioService => Named::TVRadioService, + NamedKey::TVSatellite => Named::TVSatellite, + NamedKey::TVSatelliteBS => Named::TVSatelliteBS, + NamedKey::TVSatelliteCS => Named::TVSatelliteCS, + NamedKey::TVSatelliteToggle => Named::TVSatelliteToggle, + NamedKey::TVTerrestrialAnalog => Named::TVTerrestrialAnalog, + NamedKey::TVTerrestrialDigital => Named::TVTerrestrialDigital, + NamedKey::TVTimer => Named::TVTimer, + NamedKey::AVRInput => Named::AVRInput, + NamedKey::AVRPower => Named::AVRPower, + NamedKey::ColorF0Red => Named::ColorF0Red, + NamedKey::ColorF1Green => Named::ColorF1Green, + NamedKey::ColorF2Yellow => Named::ColorF2Yellow, + NamedKey::ColorF3Blue => Named::ColorF3Blue, + NamedKey::ColorF4Grey => Named::ColorF4Grey, + NamedKey::ColorF5Brown => Named::ColorF5Brown, + NamedKey::ClosedCaptionToggle => Named::ClosedCaptionToggle, + NamedKey::Dimmer => Named::Dimmer, + NamedKey::DisplaySwap => Named::DisplaySwap, + NamedKey::DVR => Named::DVR, + NamedKey::Exit => Named::Exit, + NamedKey::FavoriteClear0 => Named::FavoriteClear0, + NamedKey::FavoriteClear1 => Named::FavoriteClear1, + NamedKey::FavoriteClear2 => Named::FavoriteClear2, + NamedKey::FavoriteClear3 => Named::FavoriteClear3, + NamedKey::FavoriteRecall0 => Named::FavoriteRecall0, + NamedKey::FavoriteRecall1 => Named::FavoriteRecall1, + NamedKey::FavoriteRecall2 => Named::FavoriteRecall2, + NamedKey::FavoriteRecall3 => Named::FavoriteRecall3, + NamedKey::FavoriteStore0 => Named::FavoriteStore0, + NamedKey::FavoriteStore1 => Named::FavoriteStore1, + NamedKey::FavoriteStore2 => Named::FavoriteStore2, + NamedKey::FavoriteStore3 => Named::FavoriteStore3, + NamedKey::Guide => Named::Guide, + NamedKey::GuideNextDay => Named::GuideNextDay, + NamedKey::GuidePreviousDay => Named::GuidePreviousDay, + NamedKey::Info => Named::Info, + NamedKey::InstantReplay => Named::InstantReplay, + NamedKey::Link => Named::Link, + NamedKey::ListProgram => Named::ListProgram, + NamedKey::LiveContent => Named::LiveContent, + NamedKey::Lock => Named::Lock, + NamedKey::MediaApps => Named::MediaApps, + NamedKey::MediaAudioTrack => Named::MediaAudioTrack, + NamedKey::MediaLast => Named::MediaLast, + NamedKey::MediaSkipBackward => Named::MediaSkipBackward, + NamedKey::MediaSkipForward => Named::MediaSkipForward, + NamedKey::MediaStepBackward => Named::MediaStepBackward, + NamedKey::MediaStepForward => Named::MediaStepForward, + NamedKey::MediaTopMenu => Named::MediaTopMenu, + NamedKey::NavigateIn => Named::NavigateIn, + NamedKey::NavigateNext => Named::NavigateNext, + NamedKey::NavigateOut => Named::NavigateOut, + NamedKey::NavigatePrevious => Named::NavigatePrevious, + NamedKey::NextFavoriteChannel => Named::NextFavoriteChannel, + NamedKey::NextUserProfile => Named::NextUserProfile, + NamedKey::OnDemand => Named::OnDemand, + NamedKey::Pairing => Named::Pairing, + NamedKey::PinPDown => Named::PinPDown, + NamedKey::PinPMove => Named::PinPMove, + NamedKey::PinPToggle => Named::PinPToggle, + NamedKey::PinPUp => Named::PinPUp, + NamedKey::PlaySpeedDown => Named::PlaySpeedDown, + NamedKey::PlaySpeedReset => Named::PlaySpeedReset, + NamedKey::PlaySpeedUp => Named::PlaySpeedUp, + NamedKey::RandomToggle => Named::RandomToggle, + NamedKey::RcLowBattery => Named::RcLowBattery, + NamedKey::RecordSpeedNext => Named::RecordSpeedNext, + NamedKey::RfBypass => Named::RfBypass, + NamedKey::ScanChannelsToggle => Named::ScanChannelsToggle, + NamedKey::ScreenModeNext => Named::ScreenModeNext, + NamedKey::Settings => Named::Settings, + NamedKey::SplitScreenToggle => Named::SplitScreenToggle, + NamedKey::STBInput => Named::STBInput, + NamedKey::STBPower => Named::STBPower, + NamedKey::Subtitle => Named::Subtitle, + NamedKey::Teletext => Named::Teletext, + NamedKey::VideoModeNext => Named::VideoModeNext, + NamedKey::Wink => Named::Wink, + NamedKey::ZoomToggle => Named::ZoomToggle, + NamedKey::F1 => Named::F1, + NamedKey::F2 => Named::F2, + NamedKey::F3 => Named::F3, + NamedKey::F4 => Named::F4, + NamedKey::F5 => Named::F5, + NamedKey::F6 => Named::F6, + NamedKey::F7 => Named::F7, + NamedKey::F8 => Named::F8, + NamedKey::F9 => Named::F9, + NamedKey::F10 => Named::F10, + NamedKey::F11 => Named::F11, + NamedKey::F12 => Named::F12, + NamedKey::F13 => Named::F13, + NamedKey::F14 => Named::F14, + NamedKey::F15 => Named::F15, + NamedKey::F16 => Named::F16, + NamedKey::F17 => Named::F17, + NamedKey::F18 => Named::F18, + NamedKey::F19 => Named::F19, + NamedKey::F20 => Named::F20, + NamedKey::F21 => Named::F21, + NamedKey::F22 => Named::F22, + NamedKey::F23 => Named::F23, + NamedKey::F24 => Named::F24, + NamedKey::F25 => Named::F25, + NamedKey::F26 => Named::F26, + NamedKey::F27 => Named::F27, + NamedKey::F28 => Named::F28, + NamedKey::F29 => Named::F29, + NamedKey::F30 => Named::F30, + NamedKey::F31 => Named::F31, + NamedKey::F32 => Named::F32, + NamedKey::F33 => Named::F33, + NamedKey::F34 => Named::F34, + NamedKey::F35 => Named::F35, + _ => return keyboard::Key::Unidentified, + }), _ => keyboard::Key::Unidentified, } } -pub fn modifiers( - modifiers: layershellev::ModifiersState, -) -> iced_core::keyboard::Modifiers { +pub fn modifiers(modifiers: ModifiersState) -> iced_core::keyboard::Modifiers { use iced_core::keyboard; let mut result = keyboard::Modifiers::empty(); diff --git a/iced_layershell/src/event.rs b/iced_layershell/src/event.rs index 7041bc7..3a7b7d3 100644 --- a/iced_layershell/src/event.rs +++ b/iced_layershell/src/event.rs @@ -1,6 +1,7 @@ use layershellev::id::Id; use layershellev::reexport::wayland_client::{ButtonState, KeyState, WEnum}; -use layershellev::KeyEvent as LayerShellKeyEvent; +use layershellev::xkb_keyboard::KeyEvent as LayerShellKeyEvent; +use layershellev::keyboard::ModifiersState; use layershellev::{DispatchMessage, WindowWrapper}; use iced_core::keyboard::Modifiers as IcedModifiers; @@ -49,7 +50,7 @@ pub enum WindowEvent { event: LayerShellKeyEvent, is_synthetic: bool, }, - ModifiersChanged(layershellev::ModifiersState), + ModifiersChanged(ModifiersState), Axis { x: f32, y: f32, diff --git a/iced_layershell/src/multi_window/state.rs b/iced_layershell/src/multi_window/state.rs index 40918b8..910f68f 100644 --- a/iced_layershell/src/multi_window/state.rs +++ b/iced_layershell/src/multi_window/state.rs @@ -2,7 +2,7 @@ use crate::multi_window::Application; use iced_core::{mouse as IcedMouse, Color, Point, Size}; use iced_graphics::Viewport; use iced_style::application::{self, StyleSheet}; -use layershellev::ModifiersState; +use layershellev::keyboard::ModifiersState; use crate::event::WindowEvent; use iced::window; diff --git a/layershellev/examples/simplelayer.rs b/layershellev/examples/simplelayer.rs index 6228f86..fe1ead3 100644 --- a/layershellev/examples/simplelayer.rs +++ b/layershellev/examples/simplelayer.rs @@ -1,20 +1,10 @@ use std::fs::File; use std::os::fd::AsFd; +use layershellev::keyboard::{KeyCode, PhysicalKey}; use layershellev::reexport::*; use layershellev::*; -const Q_KEY: u32 = 16; -const W_KEY: u32 = 17; -const E_KEY: u32 = 18; -const A_KEY: u32 = 30; -const S_KEY: u32 = 31; -const D_KEY: u32 = 32; -const Z_KEY: u32 = 44; -const X_KEY: u32 = 45; -const C_KEY: u32 = 46; -const ESC_KEY: u32 = 1; - fn main() { let ev: WindowState<()> = WindowState::new("Hello") .with_single(false) @@ -85,48 +75,12 @@ fn main() { println!("{time}, {surface_x}, {surface_y}"); ReturnData::None } - LayerEvent::RequestMessages(DispatchMessage::KeyBoard { key, .. }) => { - match index { - Some(index) => { - let ev_unit = ev.get_unit(index); - match *key { - Q_KEY => ev_unit.set_anchor(Anchor::Top | Anchor::Left), - W_KEY => ev_unit.set_anchor(Anchor::Top), - E_KEY => ev_unit.set_anchor(Anchor::Top | Anchor::Right), - A_KEY => ev_unit.set_anchor(Anchor::Left), - S_KEY => ev_unit.set_anchor( - Anchor::Left | Anchor::Right | Anchor::Top | Anchor::Bottom, - ), - D_KEY => ev_unit.set_anchor(Anchor::Right), - Z_KEY => ev_unit.set_anchor(Anchor::Left | Anchor::Bottom), - X_KEY => ev_unit.set_anchor(Anchor::Bottom), - C_KEY => ev_unit.set_anchor(Anchor::Bottom | Anchor::Right), - ESC_KEY => return ReturnData::RequestExist, - _ => {} - } - } - None => { - for ev_unit in ev.get_unit_iter() { - match *key { - Q_KEY => ev_unit.set_anchor(Anchor::Top | Anchor::Left), - W_KEY => ev_unit.set_anchor(Anchor::Top), - E_KEY => ev_unit.set_anchor(Anchor::Top | Anchor::Right), - A_KEY => ev_unit.set_anchor(Anchor::Left), - S_KEY => ev_unit.set_anchor( - Anchor::Left | Anchor::Right | Anchor::Top | Anchor::Bottom, - ), - D_KEY => ev_unit.set_anchor(Anchor::Right), - Z_KEY => ev_unit.set_anchor(Anchor::Left | Anchor::Bottom), - X_KEY => ev_unit.set_anchor(Anchor::Bottom), - C_KEY => ev_unit.set_anchor(Anchor::Bottom | Anchor::Right), - ESC_KEY => return ReturnData::RequestExist, - _ => {} - } - } - } - }; - - ReturnData::None + LayerEvent::RequestMessages(DispatchMessage::KeyboardInput { event, .. }) => { + if let PhysicalKey::Code(KeyCode::Escape) = event.physical_key { + ReturnData::RequestExist + } else { + ReturnData::None + } } _ => ReturnData::None, } diff --git a/layershellev/src/events.rs b/layershellev/src/events.rs index 7797c66..dd34f57 100644 --- a/layershellev/src/events.rs +++ b/layershellev/src/events.rs @@ -9,7 +9,9 @@ use wayland_client::{ QueueHandle, WEnum, }; -use crate::xkb_keyboard::{KeyEvent, ModifiersState}; +use crate::xkb_keyboard::KeyEvent; + +use crate::keyboard::ModifiersState; use super::WindowState; diff --git a/layershellev/src/keyboard.rs b/layershellev/src/keyboard.rs index 43c3398..eba59fd 100644 --- a/layershellev/src/keyboard.rs +++ b/layershellev/src/keyboard.rs @@ -1556,21 +1556,6 @@ impl Key { } impl NamedKey { - /// Convert an action to its approximate textual equivalent. - /// - /// # Examples - /// - /// ``` - /// # #[cfg(web_platform)] - /// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - /// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)] - /// # fn main() { - /// use winit::keyboard::NamedKey; - /// - /// assert_eq!(NamedKey::Enter.to_text(), Some("\r")); - /// assert_eq!(NamedKey::F20.to_text(), None); - /// # } - /// ``` pub fn to_text(&self) -> Option<&str> { match self { NamedKey::Enter => Some("\r"), @@ -1584,22 +1569,6 @@ impl NamedKey { } impl Key { - /// Convert a key to its approximate textual equivalent. - /// - /// # Examples - /// - /// ``` - /// # #[cfg(web_platform)] - /// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - /// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)] - /// # fn main() { - /// use winit::keyboard::{Key, NamedKey}; - /// - /// assert_eq!(Key::Character("a".into()).to_text(), Some("a")); - /// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r")); - /// assert_eq!(Key::Named(NamedKey::F20).to_text(), None); - /// # } - /// ``` pub fn to_text(&self) -> Option<&str> { match self { Key::Named(action) => action.to_text(), diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index 0101754..f2a85ab 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -6,20 +6,10 @@ //! use std::fs::File; //! use std::os::fd::AsFd; //! +//! use layershellev::keyboard::{KeyCode, PhysicalKey}; //! use layershellev::reexport::*; //! use layershellev::*; //! -//! const Q_KEY: u32 = 16; -//! const W_KEY: u32 = 17; -//! const E_KEY: u32 = 18; -//! const A_KEY: u32 = 30; -//! const S_KEY: u32 = 31; -//! const D_KEY: u32 = 32; -//! const Z_KEY: u32 = 44; -//! const X_KEY: u32 = 45; -//! const C_KEY: u32 = 46; -//! const ESC_KEY: u32 = 1; -//! //! fn main() { //! let mut ev: WindowState<()> = WindowState::new("Hello") //! .with_single(false) @@ -90,49 +80,13 @@ //! println!("{time}, {surface_x}, {surface_y}"); //! ReturnData::None //! } -//! LayerEvent::RequestMessages(DispatchMessage::KeyBoard { key, .. }) => { -//! match index { -//! Some(index) => { -//! let ev_unit = ev.get_unit(index); -//! match *key { -//! Q_KEY => ev_unit.set_anchor(Anchor::Top | Anchor::Left), -//! W_KEY => ev_unit.set_anchor(Anchor::Top), -//! E_KEY => ev_unit.set_anchor(Anchor::Top | Anchor::Right), -//! A_KEY => ev_unit.set_anchor(Anchor::Left), -//! S_KEY => ev_unit.set_anchor( -//! Anchor::Left | Anchor::Right | Anchor::Top | Anchor::Bottom, -//! ), -//! D_KEY => ev_unit.set_anchor(Anchor::Right), -//! Z_KEY => ev_unit.set_anchor(Anchor::Left | Anchor::Bottom), -//! X_KEY => ev_unit.set_anchor(Anchor::Bottom), -//! C_KEY => ev_unit.set_anchor(Anchor::Bottom | Anchor::Right), -//! ESC_KEY => return ReturnData::RequestExist, -//! _ => {} -//! } -//! } -//! None => { -//! for ev_unit in ev.get_unit_iter() { -//! match *key { -//! Q_KEY => ev_unit.set_anchor(Anchor::Top | Anchor::Left), -//! W_KEY => ev_unit.set_anchor(Anchor::Top), -//! E_KEY => ev_unit.set_anchor(Anchor::Top | Anchor::Right), -//! A_KEY => ev_unit.set_anchor(Anchor::Left), -//! S_KEY => ev_unit.set_anchor( -//! Anchor::Left | Anchor::Right | Anchor::Top | Anchor::Bottom, -//! ), -//! D_KEY => ev_unit.set_anchor(Anchor::Right), -//! Z_KEY => ev_unit.set_anchor(Anchor::Left | Anchor::Bottom), -//! X_KEY => ev_unit.set_anchor(Anchor::Bottom), -//! C_KEY => ev_unit.set_anchor(Anchor::Bottom | Anchor::Right), -//! ESC_KEY => return ReturnData::RequestExist, -//! _ => {} -//! } -//! } -//! } -//! }; -//! -//! ReturnData::None -//! } +//! LayerEvent::RequestMessages(DispatchMessage::KeyboardInput { event, .. }) => { +//! if let PhysicalKey::Code(KeyCode::Escape) = event.physical_key { +//! ReturnData::RequestExist +//! } else { +//! ReturnData::None +//! } +//! } //! _ => ReturnData::None, //! } //! }) @@ -162,7 +116,7 @@ mod events; pub mod keyboard; mod keymap; mod strtoshape; -mod xkb_keyboard; +pub mod xkb_keyboard; use std::fmt::Debug; @@ -192,7 +146,6 @@ use wayland_client::{ }, ConnectError, Connection, Dispatch, DispatchError, EventQueue, Proxy, QueueHandle, WEnum, }; -pub use xkb_keyboard::*; use sctk::reexports::{calloop::EventLoop, calloop_wayland_source::WaylandSource}; @@ -503,7 +456,7 @@ pub struct WindowState { // base managers seat: Option, - keyboard_state: Option, + keyboard_state: Option, pointer: Option, touch: Option, @@ -795,6 +748,7 @@ impl Dispatch for WindowState { _conn: &Connection, qh: &wayland_client::QueueHandle, ) { + use xkb_keyboard::KeyboardState; if let wl_seat::Event::Capabilities { capabilities: WEnum::Value(capabilities), } = event @@ -821,6 +775,8 @@ impl Dispatch for WindowState { _conn: &Connection, _qhandle: &QueueHandle, ) { + use keyboard::*; + use xkb_keyboard::ElementState; let keyboard_state = state.keyboard_state.as_mut().unwrap(); match event { wl_keyboard::Event::Keymap { format, fd, size } => match format { diff --git a/layershellev/src/xkb_keyboard.rs b/layershellev/src/xkb_keyboard.rs index 9bbc7d8..4597e80 100644 --- a/layershellev/src/xkb_keyboard.rs +++ b/layershellev/src/xkb_keyboard.rs @@ -20,8 +20,7 @@ use xkbcommon_dl::{ xkb_layout_index_t, xkbcommon_compose_handle, xkbcommon_handle, XkbCommon, XkbCommonCompose, }; -use bitflags::bitflags; - +use crate::keyboard::ModifiersState; use xkb::{ xkb_context, xkb_context_flags, xkb_keymap, xkb_keymap_compile_flags, xkb_state, xkb_state_component, @@ -29,76 +28,7 @@ use xkb::{ use crate::keyboard::{Key, KeyLocation, PhysicalKey}; -bitflags! { - /// Represents the current state of the keyboard modifiers - /// - /// Each flag represents a modifier and is set if this modifier is active. - #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct ModifiersState: u32 { - /// The "shift" key. - const SHIFT = 0b100; - /// The "control" key. - const CONTROL = 0b100 << 3; - /// The "alt" key. - const ALT = 0b100 << 6; - /// This is the "windows" key on PC and "command" key on Mac. - const SUPER = 0b100 << 9; - } -} - -impl ModifiersState { - /// Returns `true` if the shift key is pressed. - pub fn shift_key(&self) -> bool { - self.intersects(Self::SHIFT) - } - - /// Returns `true` if the control key is pressed. - pub fn control_key(&self) -> bool { - self.intersects(Self::CONTROL) - } - - /// Returns `true` if the alt key is pressed. - pub fn alt_key(&self) -> bool { - self.intersects(Self::ALT) - } - - /// Returns `true` if the super key is pressed. - pub fn super_key(&self) -> bool { - self.intersects(Self::SUPER) - } -} -/// The state of the particular modifiers key. -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] -pub enum ModifiersKeyState { - /// The particular key is pressed. - Pressed, - /// The state of the key is unknown. - #[default] - Unknown, -} - -// NOTE: the exact modifier key is not used to represent modifiers state in the -// first place due to a fact that modifiers state could be changed without any -// key being pressed and on some platforms like Wayland/X11 which key resulted -// in modifiers change is hidden, also, not that it really matters. -// -// The reason this API is even exposed is mostly to provide a way for users -// to treat modifiers differently based on their position, which is required -// on macOS due to their AltGr/Option situation. -bitflags! { - #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub(crate) struct ModifiersKeys: u8 { - const LSHIFT = 0b0000_0001; - const RSHIFT = 0b0000_0010; - const LCONTROL = 0b0000_0100; - const RCONTROL = 0b0000_1000; - const LALT = 0b0001_0000; - const RALT = 0b0010_0000; - const LSUPER = 0b0100_0000; - const RSUPER = 0b1000_0000; - } -} static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); From c661cfd0e67d2f79366692cb575f28d812bc6c93 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 22:24:09 +0859 Subject: [PATCH 10/16] feat: finishe sessionlock --- Cargo.lock | 3 + Cargo.toml | 4 + iced_sessionlock/src/conversion.rs | 60 +- iced_sessionlock/src/conversion/keymap.rs | 426 +++-- iced_sessionlock/src/event.rs | 32 +- iced_sessionlock/src/multi_window.rs | 59 +- iced_sessionlock/src/multi_window/state.rs | 7 +- layershellev/Cargo.toml | 6 +- sessionlockev/Cargo.toml | 4 + sessionlockev/src/events.rs | 73 +- sessionlockev/src/key.rs | 59 - sessionlockev/src/keyboard.rs | 1730 ++++++++++++++++++++ sessionlockev/src/keymap.rs | 894 ++++++++++ sessionlockev/src/lib.rs | 87 +- sessionlockev/src/xkb_keyboard.rs | 842 ++++++++++ 15 files changed, 3992 insertions(+), 294 deletions(-) delete mode 100644 sessionlockev/src/key.rs create mode 100644 sessionlockev/src/keyboard.rs create mode 100644 sessionlockev/src/keymap.rs create mode 100644 sessionlockev/src/xkb_keyboard.rs diff --git a/Cargo.lock b/Cargo.lock index c20a438..5ca952e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2800,8 +2800,10 @@ version = "0.2.7" dependencies = [ "bitflags 2.6.0", "log", + "memmap2 0.9.4", "raw-window-handle", "smithay-client-toolkit 0.18.1", + "smol_str", "tempfile", "thiserror", "wayland-backend", @@ -2810,6 +2812,7 @@ dependencies = [ "wayland-protocols 0.31.2", "wayland-protocols-misc", "wayland-protocols-wlr 0.2.0", + "xkbcommon-dl", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e311113..a112d80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,3 +68,7 @@ iced_futures = "0.12.0" iced_graphics = "0.12.0" bitflags = "2.6.0" log = "0.4.22" + +xkbcommon-dl = "0.4.2" +smol_str = "0.2.2" +memmap2 = "0.9.4" diff --git a/iced_sessionlock/src/conversion.rs b/iced_sessionlock/src/conversion.rs index 4e88554..ce9d651 100644 --- a/iced_sessionlock/src/conversion.rs +++ b/iced_sessionlock/src/conversion.rs @@ -1,14 +1,23 @@ mod keymap; use crate::event::IcedButtonState; -use crate::event::IcedKeyState; use crate::event::WindowEvent as SessionLockEvent; -use keymap::{key_from_u32, text_from_key}; +use iced_core::SmolStr; +use keymap::key; +use sessionlockev::keyboard::KeyLocation; +use sessionlockev::xkb_keyboard::ElementState; +use sessionlockev::xkb_keyboard::KeyEvent as SessionLockKeyEvent; use iced_core::{keyboard, mouse, Event as IcedEvent}; +use sessionlockev::keyboard::ModifiersState; #[allow(unused)] -pub fn window_event(id: iced_core::window::Id, layerevent: &SessionLockEvent) -> Option { +pub fn window_event( + id: iced_core::window::Id, + layerevent: &SessionLockEvent, + + modifiers: ModifiersState, +) -> Option { match layerevent { SessionLockEvent::CursorLeft => Some(IcedEvent::Mouse(mouse::Event::CursorLeft)), SessionLockEvent::CursorMoved { x, y } => { @@ -33,27 +42,39 @@ pub fn window_event(id: iced_core::window::Id, layerevent: &SessionLockEvent) -> delta: mouse::ScrollDelta::Pixels { x: *x, y: *y }, })) } - SessionLockEvent::Keyboard { - state, - key, - modifiers, - } => { - let key = key_from_u32(*key); - let text = text_from_key(&key); + + SessionLockEvent::KeyBoardInput { event, .. } => Some(IcedEvent::Keyboard({ + let logical_key = event.key_without_modifiers(); + let text = event + .text_with_all_modifiers() + .map(SmolStr::new) + .filter(|text| !text.as_str().chars().any(is_private_use)); + let SessionLockKeyEvent { + state, location, .. + } = event; + let key = key(logical_key); + let modifiers = keymap::modifiers(modifiers); + + let location = match location { + KeyLocation::Standard => keyboard::Location::Standard, + KeyLocation::Left => keyboard::Location::Left, + KeyLocation::Right => keyboard::Location::Right, + KeyLocation::Numpad => keyboard::Location::Numpad, + }; match state { - IcedKeyState::Pressed => Some(IcedEvent::Keyboard(keyboard::Event::KeyPressed { + ElementState::Pressed => keyboard::Event::KeyPressed { key, - location: keyboard::Location::Standard, - modifiers: *modifiers, + location, + modifiers, text, - })), - IcedKeyState::Released => Some(IcedEvent::Keyboard(keyboard::Event::KeyReleased { + }, + ElementState::Released => keyboard::Event::KeyReleased { key, - location: keyboard::Location::Standard, - modifiers: *modifiers, - })), + location, + modifiers, + }, } - } + })), _ => None, } } @@ -75,7 +96,6 @@ pub(crate) fn mouse_interaction(interaction: mouse::Interaction) -> String { } } -#[allow(unused)] fn is_private_use(c: char) -> bool { ('\u{E000}'..='\u{F8FF}').contains(&c) } diff --git a/iced_sessionlock/src/conversion/keymap.rs b/iced_sessionlock/src/conversion/keymap.rs index 0d5e469..81b5cbf 100644 --- a/iced_sessionlock/src/conversion/keymap.rs +++ b/iced_sessionlock/src/conversion/keymap.rs @@ -1,100 +1,336 @@ -use iced_core::SmolStr; - -use iced_core::keyboard::Key as IcedKey; - -// TODO: modifier -pub fn key_from_u32(value: u32) -> IcedKey { +use sessionlockev::keyboard::ModifiersState; +/// Converts a `VirtualKeyCode` from [`winit`] to an [`iced`] key code. +/// +/// [`winit`]: https://github.com/rust-windowing/winit +/// [`iced`]: https://github.com/iced-rs/iced/tree/0.12 +pub fn key(key: sessionlockev::keyboard::Key) -> iced_core::keyboard::Key { + use iced_core::keyboard; use iced_core::keyboard::key::Named; - match value { - 0 => IcedKey::Unidentified, - 1 => IcedKey::Named(Named::Escape), - code @ 2..=10 => IcedKey::Character(SmolStr::new((code - 1).to_string())), - 11 => IcedKey::Character(SmolStr::new("0")), - 12 => IcedKey::Character(SmolStr::new("-")), - 13 => IcedKey::Character(SmolStr::new("=")), - 14 => IcedKey::Named(Named::Backspace), - 15 => IcedKey::Named(Named::Tab), - 16 => IcedKey::Character(SmolStr::new("q")), - 17 => IcedKey::Character(SmolStr::new("w")), - 18 => IcedKey::Character(SmolStr::new("e")), - 19 => IcedKey::Character(SmolStr::new("r")), - 20 => IcedKey::Character(SmolStr::new("t")), - 21 => IcedKey::Character(SmolStr::new("y")), - 22 => IcedKey::Character(SmolStr::new("u")), - 23 => IcedKey::Character(SmolStr::new("i")), - 24 => IcedKey::Character(SmolStr::new("o")), - 25 => IcedKey::Character(SmolStr::new("p")), - 26 => IcedKey::Character(SmolStr::new("[")), - 27 => IcedKey::Character(SmolStr::new("]")), - 28 => IcedKey::Named(Named::Enter), - 29 | 97 => IcedKey::Named(Named::Control), - 30 => IcedKey::Character(SmolStr::new("a")), - 31 => IcedKey::Character(SmolStr::new("s")), - 32 => IcedKey::Character(SmolStr::new("d")), - 33 => IcedKey::Character(SmolStr::new("f")), - 34 => IcedKey::Character(SmolStr::new("g")), - 35 => IcedKey::Character(SmolStr::new("h")), - 36 => IcedKey::Character(SmolStr::new("j")), - 37 => IcedKey::Character(SmolStr::new("k")), - 38 => IcedKey::Character(SmolStr::new("l")), - 39 => IcedKey::Character(SmolStr::new(";")), - 40 => IcedKey::Character(SmolStr::new("'")), - 41 => IcedKey::Character(SmolStr::new("'")), - 42 | 54 => IcedKey::Named(Named::Shift), - 43 => IcedKey::Character(SmolStr::new("\\")), - 44 => IcedKey::Character(SmolStr::new("z")), - 45 => IcedKey::Character(SmolStr::new("x")), - 46 => IcedKey::Character(SmolStr::new("c")), - 47 => IcedKey::Character(SmolStr::new("v")), - 48 => IcedKey::Character(SmolStr::new("b")), - 49 => IcedKey::Character(SmolStr::new("n")), - 50 => IcedKey::Character(SmolStr::new("m")), - 51 => IcedKey::Character(SmolStr::new(",")), - 52 => IcedKey::Character(SmolStr::new(".")), - 53 => IcedKey::Character(SmolStr::new("/")), - // TODO: 55 - 56 | 100 => IcedKey::Named(Named::Alt), - 57 => IcedKey::Named(Named::Space), - 58 => IcedKey::Named(Named::CapsLock), - 59 => IcedKey::Named(Named::F1), - 60 => IcedKey::Named(Named::F2), - 61 => IcedKey::Named(Named::F3), - 62 => IcedKey::Named(Named::F4), - 63 => IcedKey::Named(Named::F5), - 64 => IcedKey::Named(Named::F6), - 65 => IcedKey::Named(Named::F7), - 66 => IcedKey::Named(Named::F8), - 67 => IcedKey::Named(Named::F9), - 68 => IcedKey::Named(Named::F10), - 69 => IcedKey::Named(Named::NumLock), - 70 => IcedKey::Named(Named::ScrollLock), - 71 => IcedKey::Character(SmolStr::new("7")), - 72 => IcedKey::Character(SmolStr::new("8")), - 73 => IcedKey::Character(SmolStr::new("9")), - 74 => IcedKey::Character(SmolStr::new("-")), - 75 => IcedKey::Character(SmolStr::new("4")), - 76 => IcedKey::Character(SmolStr::new("5")), - 77 => IcedKey::Character(SmolStr::new("6")), - 78 => IcedKey::Character(SmolStr::new("+")), - 79 => IcedKey::Character(SmolStr::new("1")), - 80 => IcedKey::Character(SmolStr::new("2")), - 81 => IcedKey::Character(SmolStr::new("3")), - 82 => IcedKey::Character(SmolStr::new("0")), - 103 => IcedKey::Named(Named::ArrowUp), - 104 => IcedKey::Named(Named::PageUp), - 105 => IcedKey::Named(Named::ArrowLeft), - 106 => IcedKey::Named(Named::ArrowRight), - 107 => IcedKey::Named(Named::End), - 108 => IcedKey::Named(Named::ArrowDown), - _ => IcedKey::Unidentified, - } -} + use sessionlockev::keyboard::NamedKey; -pub fn text_from_key(key: &IcedKey) -> Option { - use iced_core::keyboard::key::Named; match key { - IcedKey::Character(c) => Some(c.clone()), - IcedKey::Named(Named::Space) => Some(SmolStr::new(" ")), - _ => None, + sessionlockev::keyboard::Key::Character(c) => keyboard::Key::Character(c), + sessionlockev::keyboard::Key::Named(named_key) => keyboard::Key::Named(match named_key { + NamedKey::Alt => Named::Alt, + NamedKey::AltGraph => Named::AltGraph, + NamedKey::CapsLock => Named::CapsLock, + NamedKey::Control => Named::Control, + NamedKey::Fn => Named::Fn, + NamedKey::FnLock => Named::FnLock, + NamedKey::NumLock => Named::NumLock, + NamedKey::ScrollLock => Named::ScrollLock, + NamedKey::Shift => Named::Shift, + NamedKey::Symbol => Named::Symbol, + NamedKey::SymbolLock => Named::SymbolLock, + NamedKey::Meta => Named::Meta, + NamedKey::Hyper => Named::Hyper, + NamedKey::Super => Named::Super, + NamedKey::Enter => Named::Enter, + NamedKey::Tab => Named::Tab, + NamedKey::Space => Named::Space, + NamedKey::ArrowDown => Named::ArrowDown, + NamedKey::ArrowLeft => Named::ArrowLeft, + NamedKey::ArrowRight => Named::ArrowRight, + NamedKey::ArrowUp => Named::ArrowUp, + NamedKey::End => Named::End, + NamedKey::Home => Named::Home, + NamedKey::PageDown => Named::PageDown, + NamedKey::PageUp => Named::PageUp, + NamedKey::Backspace => Named::Backspace, + NamedKey::Clear => Named::Clear, + NamedKey::Copy => Named::Copy, + NamedKey::CrSel => Named::CrSel, + NamedKey::Cut => Named::Cut, + NamedKey::Delete => Named::Delete, + NamedKey::EraseEof => Named::EraseEof, + NamedKey::ExSel => Named::ExSel, + NamedKey::Insert => Named::Insert, + NamedKey::Paste => Named::Paste, + NamedKey::Redo => Named::Redo, + NamedKey::Undo => Named::Undo, + NamedKey::Accept => Named::Accept, + NamedKey::Again => Named::Again, + NamedKey::Attn => Named::Attn, + NamedKey::Cancel => Named::Cancel, + NamedKey::ContextMenu => Named::ContextMenu, + NamedKey::Escape => Named::Escape, + NamedKey::Execute => Named::Execute, + NamedKey::Find => Named::Find, + NamedKey::Help => Named::Help, + NamedKey::Pause => Named::Pause, + NamedKey::Play => Named::Play, + NamedKey::Props => Named::Props, + NamedKey::Select => Named::Select, + NamedKey::ZoomIn => Named::ZoomIn, + NamedKey::ZoomOut => Named::ZoomOut, + NamedKey::BrightnessDown => Named::BrightnessDown, + NamedKey::BrightnessUp => Named::BrightnessUp, + NamedKey::Eject => Named::Eject, + NamedKey::LogOff => Named::LogOff, + NamedKey::Power => Named::Power, + NamedKey::PowerOff => Named::PowerOff, + NamedKey::PrintScreen => Named::PrintScreen, + NamedKey::Hibernate => Named::Hibernate, + NamedKey::Standby => Named::Standby, + NamedKey::WakeUp => Named::WakeUp, + NamedKey::AllCandidates => Named::AllCandidates, + NamedKey::Alphanumeric => Named::Alphanumeric, + NamedKey::CodeInput => Named::CodeInput, + NamedKey::Compose => Named::Compose, + NamedKey::Convert => Named::Convert, + NamedKey::FinalMode => Named::FinalMode, + NamedKey::GroupFirst => Named::GroupFirst, + NamedKey::GroupLast => Named::GroupLast, + NamedKey::GroupNext => Named::GroupNext, + NamedKey::GroupPrevious => Named::GroupPrevious, + NamedKey::ModeChange => Named::ModeChange, + NamedKey::NextCandidate => Named::NextCandidate, + NamedKey::NonConvert => Named::NonConvert, + NamedKey::PreviousCandidate => Named::PreviousCandidate, + NamedKey::Process => Named::Process, + NamedKey::SingleCandidate => Named::SingleCandidate, + NamedKey::HangulMode => Named::HangulMode, + NamedKey::HanjaMode => Named::HanjaMode, + NamedKey::JunjaMode => Named::JunjaMode, + NamedKey::Eisu => Named::Eisu, + NamedKey::Hankaku => Named::Hankaku, + NamedKey::Hiragana => Named::Hiragana, + NamedKey::HiraganaKatakana => Named::HiraganaKatakana, + NamedKey::KanaMode => Named::KanaMode, + NamedKey::KanjiMode => Named::KanjiMode, + NamedKey::Katakana => Named::Katakana, + NamedKey::Romaji => Named::Romaji, + NamedKey::Zenkaku => Named::Zenkaku, + NamedKey::ZenkakuHankaku => Named::ZenkakuHankaku, + NamedKey::Soft1 => Named::Soft1, + NamedKey::Soft2 => Named::Soft2, + NamedKey::Soft3 => Named::Soft3, + NamedKey::Soft4 => Named::Soft4, + NamedKey::ChannelDown => Named::ChannelDown, + NamedKey::ChannelUp => Named::ChannelUp, + NamedKey::Close => Named::Close, + NamedKey::MailForward => Named::MailForward, + NamedKey::MailReply => Named::MailReply, + NamedKey::MailSend => Named::MailSend, + NamedKey::MediaClose => Named::MediaClose, + NamedKey::MediaFastForward => Named::MediaFastForward, + NamedKey::MediaPause => Named::MediaPause, + NamedKey::MediaPlay => Named::MediaPlay, + NamedKey::MediaPlayPause => Named::MediaPlayPause, + NamedKey::MediaRecord => Named::MediaRecord, + NamedKey::MediaRewind => Named::MediaRewind, + NamedKey::MediaStop => Named::MediaStop, + NamedKey::MediaTrackNext => Named::MediaTrackNext, + NamedKey::MediaTrackPrevious => Named::MediaTrackPrevious, + NamedKey::New => Named::New, + NamedKey::Open => Named::Open, + NamedKey::Print => Named::Print, + NamedKey::Save => Named::Save, + NamedKey::SpellCheck => Named::SpellCheck, + NamedKey::Key11 => Named::Key11, + NamedKey::Key12 => Named::Key12, + NamedKey::AudioBalanceLeft => Named::AudioBalanceLeft, + NamedKey::AudioBalanceRight => Named::AudioBalanceRight, + NamedKey::AudioBassBoostDown => Named::AudioBassBoostDown, + NamedKey::AudioBassBoostToggle => Named::AudioBassBoostToggle, + NamedKey::AudioBassBoostUp => Named::AudioBassBoostUp, + NamedKey::AudioFaderFront => Named::AudioFaderFront, + NamedKey::AudioFaderRear => Named::AudioFaderRear, + NamedKey::AudioSurroundModeNext => Named::AudioSurroundModeNext, + NamedKey::AudioTrebleDown => Named::AudioTrebleDown, + NamedKey::AudioTrebleUp => Named::AudioTrebleUp, + NamedKey::AudioVolumeDown => Named::AudioVolumeDown, + NamedKey::AudioVolumeUp => Named::AudioVolumeUp, + NamedKey::AudioVolumeMute => Named::AudioVolumeMute, + NamedKey::MicrophoneToggle => Named::MicrophoneToggle, + NamedKey::MicrophoneVolumeDown => Named::MicrophoneVolumeDown, + NamedKey::MicrophoneVolumeUp => Named::MicrophoneVolumeUp, + NamedKey::MicrophoneVolumeMute => Named::MicrophoneVolumeMute, + NamedKey::SpeechCorrectionList => Named::SpeechCorrectionList, + NamedKey::SpeechInputToggle => Named::SpeechInputToggle, + NamedKey::LaunchApplication1 => Named::LaunchApplication1, + NamedKey::LaunchApplication2 => Named::LaunchApplication2, + NamedKey::LaunchCalendar => Named::LaunchCalendar, + NamedKey::LaunchContacts => Named::LaunchContacts, + NamedKey::LaunchMail => Named::LaunchMail, + NamedKey::LaunchMediaPlayer => Named::LaunchMediaPlayer, + NamedKey::LaunchMusicPlayer => Named::LaunchMusicPlayer, + NamedKey::LaunchPhone => Named::LaunchPhone, + NamedKey::LaunchScreenSaver => Named::LaunchScreenSaver, + NamedKey::LaunchSpreadsheet => Named::LaunchSpreadsheet, + NamedKey::LaunchWebBrowser => Named::LaunchWebBrowser, + NamedKey::LaunchWebCam => Named::LaunchWebCam, + NamedKey::LaunchWordProcessor => Named::LaunchWordProcessor, + NamedKey::BrowserBack => Named::BrowserBack, + NamedKey::BrowserFavorites => Named::BrowserFavorites, + NamedKey::BrowserForward => Named::BrowserForward, + NamedKey::BrowserHome => Named::BrowserHome, + NamedKey::BrowserRefresh => Named::BrowserRefresh, + NamedKey::BrowserSearch => Named::BrowserSearch, + NamedKey::BrowserStop => Named::BrowserStop, + NamedKey::AppSwitch => Named::AppSwitch, + NamedKey::Call => Named::Call, + NamedKey::Camera => Named::Camera, + NamedKey::CameraFocus => Named::CameraFocus, + NamedKey::EndCall => Named::EndCall, + NamedKey::GoBack => Named::GoBack, + NamedKey::GoHome => Named::GoHome, + NamedKey::HeadsetHook => Named::HeadsetHook, + NamedKey::LastNumberRedial => Named::LastNumberRedial, + NamedKey::Notification => Named::Notification, + NamedKey::MannerMode => Named::MannerMode, + NamedKey::VoiceDial => Named::VoiceDial, + NamedKey::TV => Named::TV, + NamedKey::TV3DMode => Named::TV3DMode, + NamedKey::TVAntennaCable => Named::TVAntennaCable, + NamedKey::TVAudioDescription => Named::TVAudioDescription, + NamedKey::TVAudioDescriptionMixDown => Named::TVAudioDescriptionMixDown, + NamedKey::TVAudioDescriptionMixUp => Named::TVAudioDescriptionMixUp, + NamedKey::TVContentsMenu => Named::TVContentsMenu, + NamedKey::TVDataService => Named::TVDataService, + NamedKey::TVInput => Named::TVInput, + NamedKey::TVInputComponent1 => Named::TVInputComponent1, + NamedKey::TVInputComponent2 => Named::TVInputComponent2, + NamedKey::TVInputComposite1 => Named::TVInputComposite1, + NamedKey::TVInputComposite2 => Named::TVInputComposite2, + NamedKey::TVInputHDMI1 => Named::TVInputHDMI1, + NamedKey::TVInputHDMI2 => Named::TVInputHDMI2, + NamedKey::TVInputHDMI3 => Named::TVInputHDMI3, + NamedKey::TVInputHDMI4 => Named::TVInputHDMI4, + NamedKey::TVInputVGA1 => Named::TVInputVGA1, + NamedKey::TVMediaContext => Named::TVMediaContext, + NamedKey::TVNetwork => Named::TVNetwork, + NamedKey::TVNumberEntry => Named::TVNumberEntry, + NamedKey::TVPower => Named::TVPower, + NamedKey::TVRadioService => Named::TVRadioService, + NamedKey::TVSatellite => Named::TVSatellite, + NamedKey::TVSatelliteBS => Named::TVSatelliteBS, + NamedKey::TVSatelliteCS => Named::TVSatelliteCS, + NamedKey::TVSatelliteToggle => Named::TVSatelliteToggle, + NamedKey::TVTerrestrialAnalog => Named::TVTerrestrialAnalog, + NamedKey::TVTerrestrialDigital => Named::TVTerrestrialDigital, + NamedKey::TVTimer => Named::TVTimer, + NamedKey::AVRInput => Named::AVRInput, + NamedKey::AVRPower => Named::AVRPower, + NamedKey::ColorF0Red => Named::ColorF0Red, + NamedKey::ColorF1Green => Named::ColorF1Green, + NamedKey::ColorF2Yellow => Named::ColorF2Yellow, + NamedKey::ColorF3Blue => Named::ColorF3Blue, + NamedKey::ColorF4Grey => Named::ColorF4Grey, + NamedKey::ColorF5Brown => Named::ColorF5Brown, + NamedKey::ClosedCaptionToggle => Named::ClosedCaptionToggle, + NamedKey::Dimmer => Named::Dimmer, + NamedKey::DisplaySwap => Named::DisplaySwap, + NamedKey::DVR => Named::DVR, + NamedKey::Exit => Named::Exit, + NamedKey::FavoriteClear0 => Named::FavoriteClear0, + NamedKey::FavoriteClear1 => Named::FavoriteClear1, + NamedKey::FavoriteClear2 => Named::FavoriteClear2, + NamedKey::FavoriteClear3 => Named::FavoriteClear3, + NamedKey::FavoriteRecall0 => Named::FavoriteRecall0, + NamedKey::FavoriteRecall1 => Named::FavoriteRecall1, + NamedKey::FavoriteRecall2 => Named::FavoriteRecall2, + NamedKey::FavoriteRecall3 => Named::FavoriteRecall3, + NamedKey::FavoriteStore0 => Named::FavoriteStore0, + NamedKey::FavoriteStore1 => Named::FavoriteStore1, + NamedKey::FavoriteStore2 => Named::FavoriteStore2, + NamedKey::FavoriteStore3 => Named::FavoriteStore3, + NamedKey::Guide => Named::Guide, + NamedKey::GuideNextDay => Named::GuideNextDay, + NamedKey::GuidePreviousDay => Named::GuidePreviousDay, + NamedKey::Info => Named::Info, + NamedKey::InstantReplay => Named::InstantReplay, + NamedKey::Link => Named::Link, + NamedKey::ListProgram => Named::ListProgram, + NamedKey::LiveContent => Named::LiveContent, + NamedKey::Lock => Named::Lock, + NamedKey::MediaApps => Named::MediaApps, + NamedKey::MediaAudioTrack => Named::MediaAudioTrack, + NamedKey::MediaLast => Named::MediaLast, + NamedKey::MediaSkipBackward => Named::MediaSkipBackward, + NamedKey::MediaSkipForward => Named::MediaSkipForward, + NamedKey::MediaStepBackward => Named::MediaStepBackward, + NamedKey::MediaStepForward => Named::MediaStepForward, + NamedKey::MediaTopMenu => Named::MediaTopMenu, + NamedKey::NavigateIn => Named::NavigateIn, + NamedKey::NavigateNext => Named::NavigateNext, + NamedKey::NavigateOut => Named::NavigateOut, + NamedKey::NavigatePrevious => Named::NavigatePrevious, + NamedKey::NextFavoriteChannel => Named::NextFavoriteChannel, + NamedKey::NextUserProfile => Named::NextUserProfile, + NamedKey::OnDemand => Named::OnDemand, + NamedKey::Pairing => Named::Pairing, + NamedKey::PinPDown => Named::PinPDown, + NamedKey::PinPMove => Named::PinPMove, + NamedKey::PinPToggle => Named::PinPToggle, + NamedKey::PinPUp => Named::PinPUp, + NamedKey::PlaySpeedDown => Named::PlaySpeedDown, + NamedKey::PlaySpeedReset => Named::PlaySpeedReset, + NamedKey::PlaySpeedUp => Named::PlaySpeedUp, + NamedKey::RandomToggle => Named::RandomToggle, + NamedKey::RcLowBattery => Named::RcLowBattery, + NamedKey::RecordSpeedNext => Named::RecordSpeedNext, + NamedKey::RfBypass => Named::RfBypass, + NamedKey::ScanChannelsToggle => Named::ScanChannelsToggle, + NamedKey::ScreenModeNext => Named::ScreenModeNext, + NamedKey::Settings => Named::Settings, + NamedKey::SplitScreenToggle => Named::SplitScreenToggle, + NamedKey::STBInput => Named::STBInput, + NamedKey::STBPower => Named::STBPower, + NamedKey::Subtitle => Named::Subtitle, + NamedKey::Teletext => Named::Teletext, + NamedKey::VideoModeNext => Named::VideoModeNext, + NamedKey::Wink => Named::Wink, + NamedKey::ZoomToggle => Named::ZoomToggle, + NamedKey::F1 => Named::F1, + NamedKey::F2 => Named::F2, + NamedKey::F3 => Named::F3, + NamedKey::F4 => Named::F4, + NamedKey::F5 => Named::F5, + NamedKey::F6 => Named::F6, + NamedKey::F7 => Named::F7, + NamedKey::F8 => Named::F8, + NamedKey::F9 => Named::F9, + NamedKey::F10 => Named::F10, + NamedKey::F11 => Named::F11, + NamedKey::F12 => Named::F12, + NamedKey::F13 => Named::F13, + NamedKey::F14 => Named::F14, + NamedKey::F15 => Named::F15, + NamedKey::F16 => Named::F16, + NamedKey::F17 => Named::F17, + NamedKey::F18 => Named::F18, + NamedKey::F19 => Named::F19, + NamedKey::F20 => Named::F20, + NamedKey::F21 => Named::F21, + NamedKey::F22 => Named::F22, + NamedKey::F23 => Named::F23, + NamedKey::F24 => Named::F24, + NamedKey::F25 => Named::F25, + NamedKey::F26 => Named::F26, + NamedKey::F27 => Named::F27, + NamedKey::F28 => Named::F28, + NamedKey::F29 => Named::F29, + NamedKey::F30 => Named::F30, + NamedKey::F31 => Named::F31, + NamedKey::F32 => Named::F32, + NamedKey::F33 => Named::F33, + NamedKey::F34 => Named::F34, + NamedKey::F35 => Named::F35, + _ => return keyboard::Key::Unidentified, + }), + _ => keyboard::Key::Unidentified, } } + +pub fn modifiers(modifiers: ModifiersState) -> iced_core::keyboard::Modifiers { + use iced_core::keyboard; + let mut result = keyboard::Modifiers::empty(); + + result.set(keyboard::Modifiers::SHIFT, modifiers.shift_key()); + result.set(keyboard::Modifiers::CTRL, modifiers.control_key()); + result.set(keyboard::Modifiers::ALT, modifiers.alt_key()); + result.set(keyboard::Modifiers::LOGO, modifiers.super_key()); + + result +} diff --git a/iced_sessionlock/src/event.rs b/iced_sessionlock/src/event.rs index 23bf97d..7031c8f 100644 --- a/iced_sessionlock/src/event.rs +++ b/iced_sessionlock/src/event.rs @@ -1,6 +1,7 @@ use sessionlockev::id::Id; -use sessionlockev::key::KeyModifierType; +use sessionlockev::keyboard::ModifiersState; use sessionlockev::reexport::wayland_client::{ButtonState, KeyState, WEnum}; +use sessionlockev::xkb_keyboard::KeyEvent as SessionLockEvent; use sessionlockev::{DispatchMessage, WindowWrapper}; use iced_core::keyboard::Modifiers as IcedModifiers; @@ -26,11 +27,7 @@ impl From> for IcedKeyState { } } -fn modifier_from_layershell_to_iced(modifier: KeyModifierType) -> IcedModifiers { - IcedModifiers::from_bits(modifier.bits()).unwrap_or(IcedModifiers::empty()) -} - -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum WindowEvent { ScaleChanged(u32), CursorEnter { @@ -48,6 +45,11 @@ pub enum WindowEvent { key: u32, modifiers: IcedModifiers, }, + KeyBoardInput { + event: SessionLockEvent, + is_synthetic: bool, + }, + ModifiersChanged(ModifiersState), Axis { x: f32, y: f32, @@ -124,16 +126,16 @@ impl From<&DispatchMessage> for IcedSessionLockEvent DispatchMessage::PrefredScale(scale) => { IcedSessionLockEvent::Window(WindowEvent::ScaleChanged(*scale)) } - DispatchMessage::KeyBoard { - state, - key, - modifier, - .. - } => IcedSessionLockEvent::Window(WindowEvent::Keyboard { - state: (*state).into(), - key: *key, - modifiers: modifier_from_layershell_to_iced(*modifier), + DispatchMessage::KeyboardInput { + event, + is_synthetic, + } => IcedSessionLockEvent::Window(WindowEvent::KeyBoardInput { + event: event.clone(), + is_synthetic: *is_synthetic, }), + DispatchMessage::ModifiersChanged(modifiers) => { + IcedSessionLockEvent::Window(WindowEvent::ModifiersChanged(*modifiers)) + } DispatchMessage::Axis { horizontal, vertical, diff --git a/iced_sessionlock/src/multi_window.rs b/iced_sessionlock/src/multi_window.rs index 9c9b509..c00b520 100644 --- a/iced_sessionlock/src/multi_window.rs +++ b/iced_sessionlock/src/multi_window.rs @@ -16,10 +16,7 @@ use iced_style::application::StyleSheet; use iced_futures::{Executor, Runtime, Subscription}; -use sessionlockev::{ - reexport::wayland_client::{KeyState, WEnum}, - ReturnData, SessionLockEvent, WindowState, -}; +use sessionlockev::{ReturnData, SessionLockEvent, WindowState}; use futures::{channel::mpsc, SinkExt, StreamExt}; @@ -185,8 +182,6 @@ where let mut context = task::Context::from_waker(task::noop_waker_ref()); let mut pointer_serial: u32 = 0; - let mut key_event: Option> = None; - let mut key_ping_count: u32 = 400; let _ = ev.running_with_proxy(message_receiver, move |event, ev, index| { use sessionlockev::DispatchMessage; @@ -213,14 +208,6 @@ where DispatchMessage::MouseEnter { serial, .. } => { pointer_serial = *serial; } - DispatchMessage::KeyBoard { state, .. } => { - if let WEnum::Value(KeyState::Pressed) = state { - key_event = Some(message.into()); - } else { - key_event = None; - key_ping_count = 400; - } - } _ => {} } @@ -228,39 +215,14 @@ where .start_send(MultiWindowIcedSessionLockEvent(id, message.into())) .expect("Cannot send"); } - SessionLockEvent::NormalDispatch => match &key_event { - Some(keyevent) => { - if let IcedSessionLockEvent::Window(windowevent) = keyevent { - let event = IcedSessionLockEvent::Window(*windowevent); - if key_ping_count > 70 && key_ping_count < 74 { - event_sender - .start_send(MultiWindowIcedSessionLockEvent(id, event)) - .expect("Cannot send"); - key_ping_count = 0; - } else { - event_sender - .start_send(MultiWindowIcedSessionLockEvent( - id, - IcedSessionLockEvent::NormalUpdate, - )) - .expect("Cannot send"); - } - if key_ping_count >= 74 { - key_ping_count -= 1; - } else { - key_ping_count += 1; - } - } - } - None => { - event_sender - .start_send(MultiWindowIcedSessionLockEvent( - id, - IcedSessionLockEvent::NormalUpdate, - )) - .expect("Cannot send"); - } - }, + SessionLockEvent::NormalDispatch => { + event_sender + .start_send(MultiWindowIcedSessionLockEvent( + id, + IcedSessionLockEvent::NormalUpdate, + )) + .expect("Cannot send"); + } SessionLockEvent::UserEvent(event) => { event_sender .start_send(MultiWindowIcedSessionLockEvent( @@ -500,7 +462,8 @@ async fn run_instance( continue; }; window.state.update(&event); - if let Some(event) = conversion::window_event(id, &event) { + if let Some(event) = conversion::window_event(id, &event, window.state.modifiers()) + { events.push((Some(id), event)); } } diff --git a/iced_sessionlock/src/multi_window/state.rs b/iced_sessionlock/src/multi_window/state.rs index c428b2a..8041624 100644 --- a/iced_sessionlock/src/multi_window/state.rs +++ b/iced_sessionlock/src/multi_window/state.rs @@ -2,6 +2,7 @@ use crate::multi_window::Application; use iced_core::{mouse as IcedMouse, Color, Point, Size}; use iced_graphics::Viewport; use iced_style::application::{self, StyleSheet}; +use sessionlockev::keyboard::ModifiersState; use crate::event::WindowEvent; use iced::window; @@ -17,6 +18,7 @@ where theme: A::Theme, appearance: application::Appearance, mouse_position: Option, + modifiers: ModifiersState, } impl State @@ -38,9 +40,12 @@ where theme, appearance, mouse_position: None, + modifiers: ModifiersState::default(), } } - + pub fn modifiers(&self) -> ModifiersState { + self.modifiers + } pub fn update_view_port(&mut self, width: u32, height: u32) { self.viewport = Viewport::with_physical_size( iced_core::Size::new(width, height), diff --git a/layershellev/Cargo.toml b/layershellev/Cargo.toml index d9a9b06..a3704ec 100644 --- a/layershellev/Cargo.toml +++ b/layershellev/Cargo.toml @@ -34,6 +34,6 @@ sctk.workspace = true log.workspace = true -memmap2 = "0.9.4" -xkbcommon-dl = "0.4.2" -smol_str = "0.2.2" +xkbcommon-dl.workspace = true +smol_str.workspace = true +memmap2.workspace = true diff --git a/sessionlockev/Cargo.toml b/sessionlockev/Cargo.toml index 23cba00..5c7f3cd 100644 --- a/sessionlockev/Cargo.toml +++ b/sessionlockev/Cargo.toml @@ -32,3 +32,7 @@ rwh_06.workspace = true sctk.workspace = true log.workspace = true + +xkbcommon-dl.workspace = true +smol_str.workspace = true +memmap2.workspace = true diff --git a/sessionlockev/src/events.rs b/sessionlockev/src/events.rs index dc98d9e..d9216ae 100644 --- a/sessionlockev/src/events.rs +++ b/sessionlockev/src/events.rs @@ -2,7 +2,6 @@ use wayland_client::{ globals::GlobalList, protocol::{ wl_buffer::WlBuffer, - wl_keyboard::KeyState, wl_output::WlOutput, wl_pointer::{self, ButtonState, WlPointer}, wl_shm::WlShm, @@ -10,8 +9,11 @@ use wayland_client::{ QueueHandle, WEnum, }; -use crate::{id::Id, key::KeyModifierType}; +use crate::id::Id; +use crate::xkb_keyboard::KeyEvent; + +use crate::keyboard::ModifiersState; use super::WindowState; use std::{fmt::Debug, fs::File}; @@ -131,12 +133,21 @@ pub(crate) enum DispatchMessageInner { x: f64, y: f64, }, - KeyBoard { - state: WEnum, - modifier: KeyModifierType, - serial: u32, - key: u32, - time: u32, + + ModifiersChanged(ModifiersState), + KeyboardInput { + event: KeyEvent, + + /// If `true`, the event was generated synthetically by winit + /// in one of the following circumstances: + /// + /// * Synthetic key press events are generated for all keys pressed when a window gains + /// focus. Likewise, synthetic key release events are generated for all keys pressed when + /// a window goes out of focus. ***Currently, this is only functional on X11 and + /// Windows*** + /// + /// Otherwise, this value is always `false`. + is_synthetic: bool, }, RefreshSurface { width: u32, @@ -200,13 +211,21 @@ pub enum DispatchMessage { x: f64, y: f64, }, - /// forward the event of wayland-keyboard - KeyBoard { - state: WEnum, - modifier: KeyModifierType, - serial: u32, - key: u32, - time: u32, + + ModifiersChanged(ModifiersState), + KeyboardInput { + event: KeyEvent, + + /// If `true`, the event was generated synthetically by winit + /// in one of the following circumstances: + /// + /// * Synthetic key press events are generated for all keys pressed when a window gains + /// focus. Likewise, synthetic key release events are generated for all keys pressed when + /// a window goes out of focus. ***Currently, this is only functional on X11 and + /// Windows*** + /// + /// Otherwise, this value is always `false`. + is_synthetic: bool, }, /// this will request to do refresh the whole screen, because the layershell tell that a new /// configure happened @@ -272,19 +291,7 @@ impl From for DispatchMessage { DispatchMessageInner::TouchMotion { time, id, x, y } => { DispatchMessage::TouchMotion { time, id, x, y } } - DispatchMessageInner::KeyBoard { - state, - modifier, - serial, - key, - time, - } => DispatchMessage::KeyBoard { - state, - modifier, - serial, - key, - time, - }, + DispatchMessageInner::RequestRefresh { width, height } => { DispatchMessage::RequestRefresh { width, height } } @@ -299,6 +306,16 @@ impl From for DispatchMessage { vertical, source, }, + DispatchMessageInner::ModifiersChanged(modifier) => { + DispatchMessage::ModifiersChanged(modifier) + } + DispatchMessageInner::KeyboardInput { + event, + is_synthetic, + } => DispatchMessage::KeyboardInput { + event, + is_synthetic, + }, DispatchMessageInner::PrefredScale(scale) => DispatchMessage::PrefredScale(scale), DispatchMessageInner::RefreshSurface { .. } => unimplemented!(), } diff --git a/sessionlockev/src/key.rs b/sessionlockev/src/key.rs deleted file mode 100644 index ffde80a..0000000 --- a/sessionlockev/src/key.rs +++ /dev/null @@ -1,59 +0,0 @@ -use bitflags::bitflags; - -#[allow(unused)] -mod otherkeys { - pub const LEFT: u32 = 105; - pub const RIGHT: u32 = 106; - pub const DOWN: u32 = 108; - pub const UP: u32 = 103; - pub const ESC: u32 = 1; - pub const SHIFT_LEFT: u32 = 42; - pub const SHIFT_RIGHT: u32 = 54; - pub const MENU: u32 = 139; - pub const CAPS_LOCK: u32 = 58; - pub const CTRL_LEFT: u32 = 29; - pub const CTRL_RIGHT: u32 = 97; - pub const ALT_LEFT: u32 = 56; - pub const ALT_RIGHT: u32 = 100; - - pub const MIN_KEYBOARD: u32 = 999; - pub const CLOSE_KEYBOARD: u32 = 1000; - - pub fn is_unique_key(key: u32) -> bool { - key == MIN_KEYBOARD || key == CLOSE_KEYBOARD - } -} - -bitflags! { - #[allow(unused)] - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] - pub struct KeyModifierType : u32 { - const NoMod = 0; - const Shift = 1; - const CapsLock = 2; - const Ctrl = 4; - const Alt = 8; - const Super = 64; - const AltGr = 128; - } -} - -impl From for KeyModifierType { - fn from(value: u32) -> Self { - match value { - otherkeys::CAPS_LOCK => KeyModifierType::CapsLock, - otherkeys::SHIFT_LEFT | otherkeys::SHIFT_RIGHT => KeyModifierType::Shift, - otherkeys::MENU => KeyModifierType::Super, - otherkeys::CTRL_LEFT | otherkeys::CTRL_RIGHT => KeyModifierType::Ctrl, - otherkeys::ALT_LEFT | otherkeys::ALT_RIGHT => KeyModifierType::Alt, - _ => KeyModifierType::NoMod, - } - } -} - -impl From for KeyModifierType { - fn from(value: usize) -> Self { - let value = value as u32; - value.into() - } -} diff --git a/sessionlockev/src/keyboard.rs b/sessionlockev/src/keyboard.rs new file mode 100644 index 0000000..eba59fd --- /dev/null +++ b/sessionlockev/src/keyboard.rs @@ -0,0 +1,1730 @@ +//! Types related to the keyboard. + +// This file contains a substantial portion of the UI Events Specification by the W3C. In +// particular, the variant names within `Key` and `KeyCode` and their documentation are modified +// versions of contents of the aforementioned specification. +// +// The original documents are: +// +// ### For `Key` +// UI Events KeyboardEvent key Values +// https://www.w3.org/TR/2017/CR-uievents-key-20170601/ +// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). +// +// ### For `KeyCode` +// UI Events KeyboardEvent code Values +// https://www.w3.org/TR/2017/CR-uievents-code-20170601/ +// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). +// +// These documents were used under the terms of the following license. This W3C license as well as +// the W3C short notice apply to the `Key` and `KeyCode` enums and their variants and the +// documentation attached to their variants. + +// --------- BEGINNING OF W3C LICENSE -------------------------------------------------------------- +// +// License +// +// By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, +// and will comply with the following terms and conditions. +// +// Permission to copy, modify, and distribute this work, with or without modification, for any +// purpose and without fee or royalty is hereby granted, provided that you include the following on +// ALL copies of the work or portions thereof, including modifications: +// +// - The full text of this NOTICE in a location viewable to users of the redistributed or derivative +// work. +// - Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none +// exist, the W3C Software and Document Short Notice should be included. +// - Notice of any changes or modifications, through a copyright statement on the new code or +// document such as "This software or document includes material copied from or derived from +// [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." +// +// Disclaimers +// +// THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR +// ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD +// PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +// +// COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES +// ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. +// +// The name and trademarks of copyright holders may NOT be used in advertising or publicity +// pertaining to the work without specific, written prior permission. Title to copyright in this +// work will at all times remain with copyright holders. +// +// --------- END OF W3C LICENSE -------------------------------------------------------------------- + +// --------- BEGINNING OF W3C SHORT NOTICE --------------------------------------------------------- +// +// winit: https://github.com/rust-windowing/winit +// +// Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology, European +// Research Consortium for Informatics and Mathematics, Keio University, Beihang). All Rights +// Reserved. This work is distributed under the W3C® Software License [1] in the hope that it will +// be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// [1] http://www.w3.org/Consortium/Legal/copyright-software +// +// --------- END OF W3C SHORT NOTICE --------------------------------------------------------------- + +use bitflags::bitflags; +pub use smol_str::SmolStr; + +/// Contains the platform-native physical key identifier +/// +/// The exact values vary from platform to platform (which is part of why this is a per-platform +/// enum), but the values are primarily tied to the key's physical location on the keyboard. +/// +/// This enum is primarily used to store raw keycodes when Winit doesn't map a given native +/// physical key identifier to a meaningful [`KeyCode`] variant. In the presence of identifiers we +/// haven't mapped for you yet, this lets you use use [`KeyCode`] to: +/// +/// - Correctly match key press and release events. +/// - On non-Web platforms, support assigning keybinds to virtually any key through a UI. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum NativeKeyCode { + Unidentified, + /// An Android "scancode". + Android(u32), + /// A macOS "scancode". + MacOS(u16), + /// A Windows "scancode". + Windows(u16), + /// An XKB "keycode". + Xkb(u32), +} + +impl std::fmt::Debug for NativeKeyCode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use NativeKeyCode::{Android, MacOS, Unidentified, Windows, Xkb}; + let mut debug_tuple; + match self { + Unidentified => { + debug_tuple = f.debug_tuple("Unidentified"); + }, + Android(code) => { + debug_tuple = f.debug_tuple("Android"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + MacOS(code) => { + debug_tuple = f.debug_tuple("MacOS"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Windows(code) => { + debug_tuple = f.debug_tuple("Windows"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Xkb(code) => { + debug_tuple = f.debug_tuple("Xkb"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + } + debug_tuple.finish() + } +} + +/// Contains the platform-native logical key identifier +/// +/// Exactly what that means differs from platform to platform, but the values are to some degree +/// tied to the currently active keyboard layout. The same key on the same keyboard may also report +/// different values on different platforms, which is one of the reasons this is a per-platform +/// enum. +/// +/// This enum is primarily used to store raw keysym when Winit doesn't map a given native logical +/// key identifier to a meaningful [`Key`] variant. This lets you use [`Key`], and let the user +/// define keybinds which work in the presence of identifiers we haven't mapped for you yet. +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum NativeKey { + Unidentified, + /// An Android "keycode", which is similar to a "virtual-key code" on Windows. + Android(u32), + /// A macOS "scancode". There does not appear to be any direct analogue to either keysyms or + /// "virtual-key" codes in macOS, so we report the scancode instead. + MacOS(u16), + /// A Windows "virtual-key code". + Windows(u16), + /// An XKB "keysym". + Xkb(u32), + /// A "key value string". + Web(SmolStr), +} + +impl std::fmt::Debug for NativeKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use NativeKey::{Android, MacOS, Unidentified, Web, Windows, Xkb}; + let mut debug_tuple; + match self { + Unidentified => { + debug_tuple = f.debug_tuple("Unidentified"); + }, + Android(code) => { + debug_tuple = f.debug_tuple("Android"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + MacOS(code) => { + debug_tuple = f.debug_tuple("MacOS"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Windows(code) => { + debug_tuple = f.debug_tuple("Windows"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Xkb(code) => { + debug_tuple = f.debug_tuple("Xkb"); + debug_tuple.field(&format_args!("0x{code:04X}")); + }, + Web(code) => { + debug_tuple = f.debug_tuple("Web"); + debug_tuple.field(code); + }, + } + debug_tuple.finish() + } +} + +impl From for NativeKey { + #[inline] + fn from(code: NativeKeyCode) -> Self { + match code { + NativeKeyCode::Unidentified => NativeKey::Unidentified, + NativeKeyCode::Android(x) => NativeKey::Android(x), + NativeKeyCode::MacOS(x) => NativeKey::MacOS(x), + NativeKeyCode::Windows(x) => NativeKey::Windows(x), + NativeKeyCode::Xkb(x) => NativeKey::Xkb(x), + } + } +} + +impl PartialEq for NativeKeyCode { + #[allow(clippy::cmp_owned)] // uses less code than direct match; target is stack allocated + #[inline] + fn eq(&self, rhs: &NativeKey) -> bool { + NativeKey::from(*self) == *rhs + } +} + +impl PartialEq for NativeKey { + #[inline] + fn eq(&self, rhs: &NativeKeyCode) -> bool { + rhs == self + } +} + +/// Represents the location of a physical key. +/// +/// This type is a superset of [`KeyCode`], including an [`Unidentified`][Self::Unidentified] +/// variant. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PhysicalKey { + /// A known key code + Code(KeyCode), + /// This variant is used when the key cannot be translated to a [`KeyCode`] + /// + /// The native keycode is provided (if available) so you're able to more reliably match + /// key-press and key-release events by hashing the [`PhysicalKey`]. It is also possible to use + /// this for keybinds for non-standard keys, but such keybinds are tied to a given platform. + Unidentified(NativeKeyCode), +} + +impl From for PhysicalKey { + #[inline] + fn from(code: KeyCode) -> Self { + PhysicalKey::Code(code) + } +} + +impl From for PhysicalKey { + #[inline] + fn from(code: NativeKeyCode) -> Self { + PhysicalKey::Unidentified(code) + } +} + +impl PartialEq for PhysicalKey { + #[inline] + fn eq(&self, rhs: &KeyCode) -> bool { + match self { + PhysicalKey::Code(ref code) => code == rhs, + _ => false, + } + } +} + +impl PartialEq for KeyCode { + #[inline] + fn eq(&self, rhs: &PhysicalKey) -> bool { + rhs == self + } +} + +impl PartialEq for PhysicalKey { + #[inline] + fn eq(&self, rhs: &NativeKeyCode) -> bool { + match self { + PhysicalKey::Unidentified(ref code) => code == rhs, + _ => false, + } + } +} + +impl PartialEq for NativeKeyCode { + #[inline] + fn eq(&self, rhs: &PhysicalKey) -> bool { + rhs == self + } +} + +/// Code representing the location of a physical key +/// +/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few +/// exceptions: +/// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and +/// "SuperRight" here. +/// - The key that the specification calls "Super" is reported as `Unidentified` here. +/// +/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum KeyCode { + /// ` on a US keyboard. This is also called a backtick or grave. + /// This is the 半角/全角/漢字 + /// (hankaku/zenkaku/kanji) key on Japanese keyboards + Backquote, + /// Used for both the US \\ (on the 101-key layout) and also for the key + /// located between the " and Enter keys on row C of the 102-, + /// 104- and 106-key layouts. + /// Labeled # on a UK (102) keyboard. + Backslash, + /// [ on a US keyboard. + BracketLeft, + /// ] on a US keyboard. + BracketRight, + /// , on a US keyboard. + Comma, + /// 0 on a US keyboard. + Digit0, + /// 1 on a US keyboard. + Digit1, + /// 2 on a US keyboard. + Digit2, + /// 3 on a US keyboard. + Digit3, + /// 4 on a US keyboard. + Digit4, + /// 5 on a US keyboard. + Digit5, + /// 6 on a US keyboard. + Digit6, + /// 7 on a US keyboard. + Digit7, + /// 8 on a US keyboard. + Digit8, + /// 9 on a US keyboard. + Digit9, + /// = on a US keyboard. + Equal, + /// Located between the left Shift and Z keys. + /// Labeled \\ on a UK keyboard. + IntlBackslash, + /// Located between the / and right Shift keys. + /// Labeled \\ (ro) on a Japanese keyboard. + IntlRo, + /// Located between the = and Backspace keys. + /// Labeled ¥ (yen) on a Japanese keyboard. \\ on a + /// Russian keyboard. + IntlYen, + /// a on a US keyboard. + /// Labeled q on an AZERTY (e.g., French) keyboard. + KeyA, + /// b on a US keyboard. + KeyB, + /// c on a US keyboard. + KeyC, + /// d on a US keyboard. + KeyD, + /// e on a US keyboard. + KeyE, + /// f on a US keyboard. + KeyF, + /// g on a US keyboard. + KeyG, + /// h on a US keyboard. + KeyH, + /// i on a US keyboard. + KeyI, + /// j on a US keyboard. + KeyJ, + /// k on a US keyboard. + KeyK, + /// l on a US keyboard. + KeyL, + /// m on a US keyboard. + KeyM, + /// n on a US keyboard. + KeyN, + /// o on a US keyboard. + KeyO, + /// p on a US keyboard. + KeyP, + /// q on a US keyboard. + /// Labeled a on an AZERTY (e.g., French) keyboard. + KeyQ, + /// r on a US keyboard. + KeyR, + /// s on a US keyboard. + KeyS, + /// t on a US keyboard. + KeyT, + /// u on a US keyboard. + KeyU, + /// v on a US keyboard. + KeyV, + /// w on a US keyboard. + /// Labeled z on an AZERTY (e.g., French) keyboard. + KeyW, + /// x on a US keyboard. + KeyX, + /// y on a US keyboard. + /// Labeled z on a QWERTZ (e.g., German) keyboard. + KeyY, + /// z on a US keyboard. + /// Labeled w on an AZERTY (e.g., French) keyboard, and y on a + /// QWERTZ (e.g., German) keyboard. + KeyZ, + /// - on a US keyboard. + Minus, + /// . on a US keyboard. + Period, + /// ' on a US keyboard. + Quote, + /// ; on a US keyboard. + Semicolon, + /// / on a US keyboard. + Slash, + /// Alt, Option, or . + AltLeft, + /// Alt, Option, or . + /// This is labeled AltGr on many keyboard layouts. + AltRight, + /// Backspace or . + /// Labeled Delete on Apple keyboards. + Backspace, + /// CapsLock or + CapsLock, + /// The application context menu key, which is typically found between the right + /// Super key and the right Control key. + ContextMenu, + /// Control or + ControlLeft, + /// Control or + ControlRight, + /// Enter or . Labeled Return on Apple keyboards. + Enter, + /// The Windows, , Command, or other OS symbol key. + SuperLeft, + /// The Windows, , Command, or other OS symbol key. + SuperRight, + /// Shift or + ShiftLeft, + /// Shift or + ShiftRight, + ///   (space) + Space, + /// Tab or + Tab, + /// Japanese: (henkan) + Convert, + /// Japanese: カタカナ/ひらがな/ローマ字 + /// (katakana/hiragana/romaji) + KanaMode, + /// Korean: HangulMode 한/영 (han/yeong) + /// + /// Japanese (Mac keyboard): (kana) + Lang1, + /// Korean: Hanja (hanja) + /// + /// Japanese (Mac keyboard): (eisu) + Lang2, + /// Japanese (word-processing keyboard): Katakana + Lang3, + /// Japanese (word-processing keyboard): Hiragana + Lang4, + /// Japanese (word-processing keyboard): Zenkaku/Hankaku + Lang5, + /// Japanese: 無変換 (muhenkan) + NonConvert, + /// . The forward delete key. + /// Note that on Apple keyboards, the key labelled Delete on the main part of + /// the keyboard is encoded as [`Backspace`]. + /// + /// [`Backspace`]: Self::Backspace + Delete, + /// Page Down, End, or + End, + /// Help. Not present on standard PC keyboards. + Help, + /// Home or + Home, + /// Insert or Ins. Not present on Apple keyboards. + Insert, + /// Page Down, PgDn, or + PageDown, + /// Page Up, PgUp, or + PageUp, + /// + ArrowDown, + /// + ArrowLeft, + /// + ArrowRight, + /// + ArrowUp, + /// On the Mac, this is used for the numpad Clear key. + NumLock, + /// 0 Ins on a keyboard. 0 on a phone or remote control + Numpad0, + /// 1 End on a keyboard. 1 or 1 QZ on a phone or remote + /// control + Numpad1, + /// 2 ↓ on a keyboard. 2 ABC on a phone or remote control + Numpad2, + /// 3 PgDn on a keyboard. 3 DEF on a phone or remote control + Numpad3, + /// 4 ← on a keyboard. 4 GHI on a phone or remote control + Numpad4, + /// 5 on a keyboard. 5 JKL on a phone or remote control + Numpad5, + /// 6 → on a keyboard. 6 MNO on a phone or remote control + Numpad6, + /// 7 Home on a keyboard. 7 PQRS or 7 PRS on a phone + /// or remote control + Numpad7, + /// 8 ↑ on a keyboard. 8 TUV on a phone or remote control + Numpad8, + /// 9 PgUp on a keyboard. 9 WXYZ or 9 WXY on a phone + /// or remote control + Numpad9, + /// + + NumpadAdd, + /// Found on the Microsoft Natural Keyboard. + NumpadBackspace, + /// C or A (All Clear). Also for use with numpads that have a + /// Clear key that is separate from the NumLock key. On the Mac, the + /// numpad Clear key is encoded as [`NumLock`]. + /// + /// [`NumLock`]: Self::NumLock + NumpadClear, + /// C (Clear Entry) + NumpadClearEntry, + /// , (thousands separator). For locales where the thousands separator + /// is a "." (e.g., Brazil), this key may generate a .. + NumpadComma, + /// . Del. For locales where the decimal separator is "," (e.g., + /// Brazil), this key may generate a ,. + NumpadDecimal, + /// / + NumpadDivide, + NumpadEnter, + /// = + NumpadEqual, + /// # on a phone or remote control device. This key is typically found + /// below the 9 key and to the right of the 0 key. + NumpadHash, + /// M Add current entry to the value stored in memory. + NumpadMemoryAdd, + /// M Clear the value stored in memory. + NumpadMemoryClear, + /// M Replace the current entry with the value stored in memory. + NumpadMemoryRecall, + /// M Replace the value stored in memory with the current entry. + NumpadMemoryStore, + /// M Subtract current entry from the value stored in memory. + NumpadMemorySubtract, + /// * on a keyboard. For use with numpads that provide mathematical + /// operations (+, - * and /). + /// + /// Use `NumpadStar` for the * key on phones and remote controls. + NumpadMultiply, + /// ( Found on the Microsoft Natural Keyboard. + NumpadParenLeft, + /// ) Found on the Microsoft Natural Keyboard. + NumpadParenRight, + /// * on a phone or remote control device. + /// + /// This key is typically found below the 7 key and to the left of + /// the 0 key. + /// + /// Use "NumpadMultiply" for the * key on + /// numeric keypads. + NumpadStar, + /// - + NumpadSubtract, + /// Esc or + Escape, + /// Fn This is typically a hardware key that does not generate a separate code. + Fn, + /// FLock or FnLock. Function Lock key. Found on the Microsoft + /// Natural Keyboard. + FnLock, + /// PrtScr SysRq or Print Screen + PrintScreen, + /// Scroll Lock + ScrollLock, + /// Pause Break + Pause, + /// Some laptops place this key to the left of the key. + /// + /// This also the "back" button (triangle) on Android. + BrowserBack, + BrowserFavorites, + /// Some laptops place this key to the right of the key. + BrowserForward, + /// The "home" button on Android. + BrowserHome, + BrowserRefresh, + BrowserSearch, + BrowserStop, + /// Eject or . This key is placed in the function section on some Apple + /// keyboards. + Eject, + /// Sometimes labelled My Computer on the keyboard + LaunchApp1, + /// Sometimes labelled Calculator on the keyboard + LaunchApp2, + LaunchMail, + MediaPlayPause, + MediaSelect, + MediaStop, + MediaTrackNext, + MediaTrackPrevious, + /// This key is placed in the function section on some Apple keyboards, replacing the + /// Eject key. + Power, + Sleep, + AudioVolumeDown, + AudioVolumeMute, + AudioVolumeUp, + WakeUp, + // Legacy modifier key. Also called "Super" in certain places. + Meta, + // Legacy modifier key. + Hyper, + Turbo, + Abort, + Resume, + Suspend, + /// Found on Sun’s USB keyboard. + Again, + /// Found on Sun’s USB keyboard. + Copy, + /// Found on Sun’s USB keyboard. + Cut, + /// Found on Sun’s USB keyboard. + Find, + /// Found on Sun’s USB keyboard. + Open, + /// Found on Sun’s USB keyboard. + Paste, + /// Found on Sun’s USB keyboard. + Props, + /// Found on Sun’s USB keyboard. + Select, + /// Found on Sun’s USB keyboard. + Undo, + /// Use for dedicated ひらがな key found on some Japanese word processing keyboards. + Hiragana, + /// Use for dedicated カタカナ key found on some Japanese word processing keyboards. + Katakana, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F1, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F2, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F3, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F4, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F5, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F6, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F7, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F8, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F9, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F10, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F11, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F12, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F13, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F14, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F15, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F16, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F17, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F18, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F19, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F20, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F21, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F22, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F23, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F24, + /// General-purpose function key. + F25, + /// General-purpose function key. + F26, + /// General-purpose function key. + F27, + /// General-purpose function key. + F28, + /// General-purpose function key. + F29, + /// General-purpose function key. + F30, + /// General-purpose function key. + F31, + /// General-purpose function key. + F32, + /// General-purpose function key. + F33, + /// General-purpose function key. + F34, + /// General-purpose function key. + F35, +} + +/// A [`Key::Named`] value +/// +/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few +/// exceptions: +/// - The `Super` variant here, is named `Meta` in the aforementioned specification. (There's +/// another key which the specification calls `Super`. That does not exist here.) +/// - The `Space` variant here, can be identified by the character it generates in the +/// specification. +/// +/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum NamedKey { + /// The `Alt` (Alternative) key. + /// + /// This key enables the alternate modifier function for interpreting concurrent or subsequent + /// keyboard input. This key value is also used for the Apple Option key. + Alt, + /// The Alternate Graphics (AltGr or AltGraph) key. + /// + /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the + /// level 2 modifier). + AltGraph, + /// The `Caps Lock` (Capital) key. + /// + /// Toggle capital character lock function for interpreting subsequent keyboard input event. + CapsLock, + /// The `Control` or `Ctrl` key. + /// + /// Used to enable control modifier function for interpreting concurrent or subsequent keyboard + /// input. + Control, + /// The Function switch `Fn` key. Activating this key simultaneously with another key changes + /// that key’s value to an alternate character or function. This key is often handled directly + /// in the keyboard hardware and does not usually generate key events. + Fn, + /// The Function-Lock (`FnLock` or `F-Lock`) key. Activating this key switches the mode of the + /// keyboard to changes some keys' values to an alternate character or function. This key is + /// often handled directly in the keyboard hardware and does not usually generate key events. + FnLock, + /// The `NumLock` or Number Lock key. Used to toggle numpad mode function for interpreting + /// subsequent keyboard input. + NumLock, + /// Toggle between scrolling and cursor movement modes. + ScrollLock, + /// Used to enable shift modifier function for interpreting concurrent or subsequent keyboard + /// input. + Shift, + /// The Symbol modifier key (used on some virtual keyboards). + Symbol, + SymbolLock, + // Legacy modifier key. Also called "Super" in certain places. + Meta, + // Legacy modifier key. + Hyper, + /// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard + /// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` + /// key. + /// + /// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key. + Super, + /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This + /// key value is also used for the `Return` (Macintosh numpad) key. This key value is also + /// used for the Android `KEYCODE_DPAD_CENTER`. + Enter, + /// The Horizontal Tabulation `Tab` key. + Tab, + /// Used in text to insert a space between words. Usually located below the character keys. + Space, + /// Navigate or traverse downward. (`KEYCODE_DPAD_DOWN`) + ArrowDown, + /// Navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`) + ArrowLeft, + /// Navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`) + ArrowRight, + /// Navigate or traverse upward. (`KEYCODE_DPAD_UP`) + ArrowUp, + /// The End key, used with keyboard entry to go to the end of content (`KEYCODE_MOVE_END`). + End, + /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`). + /// For the mobile phone `Home` key (which goes to the phone’s main screen), use [`GoHome`]. + /// + /// [`GoHome`]: Self::GoHome + Home, + /// Scroll down or display next page of content. + PageDown, + /// Scroll up or display previous page of content. + PageUp, + /// Used to remove the character to the left of the cursor. This key value is also used for + /// the key labeled `Delete` on MacOS keyboards. + Backspace, + /// Remove the currently selected input. + Clear, + /// Copy the current selection. (`APPCOMMAND_COPY`) + Copy, + /// The Cursor Select key. + CrSel, + /// Cut the current selection. (`APPCOMMAND_CUT`) + Cut, + /// Used to delete the character to the right of the cursor. This key value is also used for + /// the key labeled `Delete` on MacOS keyboards when `Fn` is active. + Delete, + /// The Erase to End of Field key. This key deletes all characters from the current cursor + /// position to the end of the current field. + EraseEof, + /// The Extend Selection (Exsel) key. + ExSel, + /// Toggle between text modes for insertion or overtyping. + /// (`KEYCODE_INSERT`) + Insert, + /// The Paste key. (`APPCOMMAND_PASTE`) + Paste, + /// Redo the last action. (`APPCOMMAND_REDO`) + Redo, + /// Undo the last action. (`APPCOMMAND_UNDO`) + Undo, + /// The Accept (Commit, OK) key. Accept current option or input method sequence conversion. + Accept, + /// Redo or repeat an action. + Again, + /// The Attention (Attn) key. + Attn, + Cancel, + /// Show the application’s context menu. + /// This key is commonly found between the right `Super` key and the right `Control` key. + ContextMenu, + /// The `Esc` key. This key was originally used to initiate an escape sequence, but is + /// now more generally used to exit or "escape" the current context, such as closing a dialog + /// or exiting full screen mode. + Escape, + Execute, + /// Open the Find dialog. (`APPCOMMAND_FIND`) + Find, + /// Open a help dialog or toggle display of help information. (`APPCOMMAND_HELP`, + /// `KEYCODE_HELP`) + Help, + /// Pause the current state or application (as appropriate). + /// + /// Note: Do not use this value for the `Pause` button on media controllers. Use `"MediaPause"` + /// instead. + Pause, + /// Play or resume the current state or application (as appropriate). + /// + /// Note: Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"` + /// instead. + Play, + /// The properties (Props) key. + Props, + Select, + /// The ZoomIn key. (`KEYCODE_ZOOM_IN`) + ZoomIn, + /// The ZoomOut key. (`KEYCODE_ZOOM_OUT`) + ZoomOut, + /// The Brightness Down key. Typically controls the display brightness. + /// (`KEYCODE_BRIGHTNESS_DOWN`) + BrightnessDown, + /// The Brightness Up key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_UP`) + BrightnessUp, + /// Toggle removable media to eject (open) and insert (close) state. (`KEYCODE_MEDIA_EJECT`) + Eject, + LogOff, + /// Toggle power state. (`KEYCODE_POWER`) + /// Note: Note: Some devices might not expose this key to the operating environment. + Power, + /// The `PowerOff` key. Sometime called `PowerDown`. + PowerOff, + /// Initiate print-screen function. + PrintScreen, + /// The Hibernate key. This key saves the current state of the computer to disk so that it can + /// be restored. The computer will then shutdown. + Hibernate, + /// The Standby key. This key turns off the display and places the computer into a low-power + /// mode without completely shutting down. It is sometimes labelled `Suspend` or `Sleep` key. + /// (`KEYCODE_SLEEP`) + Standby, + /// The WakeUp key. (`KEYCODE_WAKEUP`) + WakeUp, + /// Initiate the multi-candidate mode. + AllCandidates, + Alphanumeric, + /// Initiate the Code Input mode to allow characters to be entered by + /// their code points. + CodeInput, + /// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a + /// manner similar to a dead key, triggering a mode where subsequent key presses are combined + /// to produce a different character. + Compose, + /// Convert the current input method sequence. + Convert, + /// The Final Mode `Final` key used on some Asian keyboards, to enable the final mode for IMEs. + FinalMode, + /// Switch to the first character group. (ISO/IEC 9995) + GroupFirst, + /// Switch to the last character group. (ISO/IEC 9995) + GroupLast, + /// Switch to the next character group. (ISO/IEC 9995) + GroupNext, + /// Switch to the previous character group. (ISO/IEC 9995) + GroupPrevious, + /// Toggle between or cycle through input modes of IMEs. + ModeChange, + NextCandidate, + /// Accept current input method sequence without + /// conversion in IMEs. + NonConvert, + PreviousCandidate, + Process, + SingleCandidate, + /// Toggle between Hangul and English modes. + HangulMode, + HanjaMode, + JunjaMode, + /// The Eisu key. This key may close the IME, but its purpose is defined by the current IME. + /// (`KEYCODE_EISU`) + Eisu, + /// The (Half-Width) Characters key. + Hankaku, + /// The Hiragana (Japanese Kana characters) key. + Hiragana, + /// The Hiragana/Katakana toggle key. (`KEYCODE_KATAKANA_HIRAGANA`) + HiraganaKatakana, + /// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from + /// romaji mode). + KanaMode, + /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key + /// is typically used to switch to a hiragana keyboard for the purpose of converting input + /// into kanji. (`KEYCODE_KANA`) + KanjiMode, + /// The Katakana (Japanese Kana characters) key. + Katakana, + /// The Roman characters function key. + Romaji, + /// The Zenkaku (Full-Width) Characters key. + Zenkaku, + /// The Zenkaku/Hankaku (full-width/half-width) toggle key. (`KEYCODE_ZENKAKU_HANKAKU`) + ZenkakuHankaku, + /// General purpose virtual function key, as index 1. + Soft1, + /// General purpose virtual function key, as index 2. + Soft2, + /// General purpose virtual function key, as index 3. + Soft3, + /// General purpose virtual function key, as index 4. + Soft4, + /// Select next (numerically or logically) lower channel. (`APPCOMMAND_MEDIA_CHANNEL_DOWN`, + /// `KEYCODE_CHANNEL_DOWN`) + ChannelDown, + /// Select next (numerically or logically) higher channel. (`APPCOMMAND_MEDIA_CHANNEL_UP`, + /// `KEYCODE_CHANNEL_UP`) + ChannelUp, + /// Close the current document or message (Note: This doesn’t close the application). + /// (`APPCOMMAND_CLOSE`) + Close, + /// Open an editor to forward the current message. (`APPCOMMAND_FORWARD_MAIL`) + MailForward, + /// Open an editor to reply to the current message. (`APPCOMMAND_REPLY_TO_MAIL`) + MailReply, + /// Send the current message. (`APPCOMMAND_SEND_MAIL`) + MailSend, + /// Close the current media, for example to close a CD or DVD tray. (`KEYCODE_MEDIA_CLOSE`) + MediaClose, + /// Initiate or continue forward playback at faster than normal speed, or increase speed if + /// already fast forwarding. (`APPCOMMAND_MEDIA_FAST_FORWARD`, `KEYCODE_MEDIA_FAST_FORWARD`) + MediaFastForward, + /// Pause the currently playing media. (`APPCOMMAND_MEDIA_PAUSE`, `KEYCODE_MEDIA_PAUSE`) + /// + /// Note: Media controller devices should use this value rather than `"Pause"` for their pause + /// keys. + MediaPause, + /// Initiate or continue media playback at normal speed, if not currently playing at normal + /// speed. (`APPCOMMAND_MEDIA_PLAY`, `KEYCODE_MEDIA_PLAY`) + MediaPlay, + /// Toggle media between play and pause states. (`APPCOMMAND_MEDIA_PLAY_PAUSE`, + /// `KEYCODE_MEDIA_PLAY_PAUSE`) + MediaPlayPause, + /// Initiate or resume recording of currently selected media. (`APPCOMMAND_MEDIA_RECORD`, + /// `KEYCODE_MEDIA_RECORD`) + MediaRecord, + /// Initiate or continue reverse playback at faster than normal speed, or increase speed if + /// already rewinding. (`APPCOMMAND_MEDIA_REWIND`, `KEYCODE_MEDIA_REWIND`) + MediaRewind, + /// Stop media playing, pausing, forwarding, rewinding, or recording, if not already stopped. + /// (`APPCOMMAND_MEDIA_STOP`, `KEYCODE_MEDIA_STOP`) + MediaStop, + /// Seek to next media or program track. (`APPCOMMAND_MEDIA_NEXTTRACK`, `KEYCODE_MEDIA_NEXT`) + MediaTrackNext, + /// Seek to previous media or program track. (`APPCOMMAND_MEDIA_PREVIOUSTRACK`, + /// `KEYCODE_MEDIA_PREVIOUS`) + MediaTrackPrevious, + /// Open a new document or message. (`APPCOMMAND_NEW`) + New, + /// Open an existing document or message. (`APPCOMMAND_OPEN`) + Open, + /// Print the current document or message. (`APPCOMMAND_PRINT`) + Print, + /// Save the current document or message. (`APPCOMMAND_SAVE`) + Save, + /// Spellcheck the current document or selection. (`APPCOMMAND_SPELL_CHECK`) + SpellCheck, + /// The `11` key found on media numpads that + /// have buttons from `1` ... `12`. + Key11, + /// The `12` key found on media numpads that + /// have buttons from `1` ... `12`. + Key12, + /// Adjust audio balance leftward. (`VK_AUDIO_BALANCE_LEFT`) + AudioBalanceLeft, + /// Adjust audio balance rightward. (`VK_AUDIO_BALANCE_RIGHT`) + AudioBalanceRight, + /// Decrease audio bass boost or cycle down through bass boost states. (`APPCOMMAND_BASS_DOWN`, + /// `VK_BASS_BOOST_DOWN`) + AudioBassBoostDown, + /// Toggle bass boost on/off. (`APPCOMMAND_BASS_BOOST`) + AudioBassBoostToggle, + /// Increase audio bass boost or cycle up through bass boost states. (`APPCOMMAND_BASS_UP`, + /// `VK_BASS_BOOST_UP`) + AudioBassBoostUp, + /// Adjust audio fader towards front. (`VK_FADER_FRONT`) + AudioFaderFront, + /// Adjust audio fader towards rear. (`VK_FADER_REAR`) + AudioFaderRear, + /// Advance surround audio mode to next available mode. (`VK_SURROUND_MODE_NEXT`) + AudioSurroundModeNext, + /// Decrease treble. (`APPCOMMAND_TREBLE_DOWN`) + AudioTrebleDown, + /// Increase treble. (`APPCOMMAND_TREBLE_UP`) + AudioTrebleUp, + /// Decrease audio volume. (`APPCOMMAND_VOLUME_DOWN`, `KEYCODE_VOLUME_DOWN`) + AudioVolumeDown, + /// Increase audio volume. (`APPCOMMAND_VOLUME_UP`, `KEYCODE_VOLUME_UP`) + AudioVolumeUp, + /// Toggle between muted state and prior volume level. (`APPCOMMAND_VOLUME_MUTE`, + /// `KEYCODE_VOLUME_MUTE`) + AudioVolumeMute, + /// Toggle the microphone on/off. (`APPCOMMAND_MIC_ON_OFF_TOGGLE`) + MicrophoneToggle, + /// Decrease microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_DOWN`) + MicrophoneVolumeDown, + /// Increase microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_UP`) + MicrophoneVolumeUp, + /// Mute the microphone. (`APPCOMMAND_MICROPHONE_VOLUME_MUTE`, `KEYCODE_MUTE`) + MicrophoneVolumeMute, + /// Show correction list when a word is incorrectly identified. (`APPCOMMAND_CORRECTION_LIST`) + SpeechCorrectionList, + /// Toggle between dictation mode and command/control mode. + /// (`APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE`) + SpeechInputToggle, + /// The first generic "LaunchApplication" key. This is commonly associated with launching "My + /// Computer", and may have a computer symbol on the key. (`APPCOMMAND_LAUNCH_APP1`) + LaunchApplication1, + /// The second generic "LaunchApplication" key. This is commonly associated with launching + /// "Calculator", and may have a calculator symbol on the key. (`APPCOMMAND_LAUNCH_APP2`, + /// `KEYCODE_CALCULATOR`) + LaunchApplication2, + /// The "Calendar" key. (`KEYCODE_CALENDAR`) + LaunchCalendar, + /// The "Contacts" key. (`KEYCODE_CONTACTS`) + LaunchContacts, + /// The "Mail" key. (`APPCOMMAND_LAUNCH_MAIL`) + LaunchMail, + /// The "Media Player" key. (`APPCOMMAND_LAUNCH_MEDIA_SELECT`) + LaunchMediaPlayer, + LaunchMusicPlayer, + LaunchPhone, + LaunchScreenSaver, + LaunchSpreadsheet, + LaunchWebBrowser, + LaunchWebCam, + LaunchWordProcessor, + /// Navigate to previous content or page in current history. (`APPCOMMAND_BROWSER_BACKWARD`) + BrowserBack, + /// Open the list of browser favorites. (`APPCOMMAND_BROWSER_FAVORITES`) + BrowserFavorites, + /// Navigate to next content or page in current history. (`APPCOMMAND_BROWSER_FORWARD`) + BrowserForward, + /// Go to the user’s preferred home page. (`APPCOMMAND_BROWSER_HOME`) + BrowserHome, + /// Refresh the current page or content. (`APPCOMMAND_BROWSER_REFRESH`) + BrowserRefresh, + /// Call up the user’s preferred search page. (`APPCOMMAND_BROWSER_SEARCH`) + BrowserSearch, + /// Stop loading the current page or content. (`APPCOMMAND_BROWSER_STOP`) + BrowserStop, + /// The Application switch key, which provides a list of recent apps to switch between. + /// (`KEYCODE_APP_SWITCH`) + AppSwitch, + /// The Call key. (`KEYCODE_CALL`) + Call, + /// The Camera key. (`KEYCODE_CAMERA`) + Camera, + /// The Camera focus key. (`KEYCODE_FOCUS`) + CameraFocus, + /// The End Call key. (`KEYCODE_ENDCALL`) + EndCall, + /// The Back key. (`KEYCODE_BACK`) + GoBack, + /// The Home key, which goes to the phone’s main screen. (`KEYCODE_HOME`) + GoHome, + /// The Headset Hook key. (`KEYCODE_HEADSETHOOK`) + HeadsetHook, + LastNumberRedial, + /// The Notification key. (`KEYCODE_NOTIFICATION`) + Notification, + /// Toggle between manner mode state: silent, vibrate, ring, ... (`KEYCODE_MANNER_MODE`) + MannerMode, + VoiceDial, + /// Switch to viewing TV. (`KEYCODE_TV`) + TV, + /// TV 3D Mode. (`KEYCODE_3D_MODE`) + TV3DMode, + /// Toggle between antenna and cable input. (`KEYCODE_TV_ANTENNA_CABLE`) + TVAntennaCable, + /// Audio description. (`KEYCODE_TV_AUDIO_DESCRIPTION`) + TVAudioDescription, + /// Audio description mixing volume down. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN`) + TVAudioDescriptionMixDown, + /// Audio description mixing volume up. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP`) + TVAudioDescriptionMixUp, + /// Contents menu. (`KEYCODE_TV_CONTENTS_MENU`) + TVContentsMenu, + /// Contents menu. (`KEYCODE_TV_DATA_SERVICE`) + TVDataService, + /// Switch the input mode on an external TV. (`KEYCODE_TV_INPUT`) + TVInput, + /// Switch to component input #1. (`KEYCODE_TV_INPUT_COMPONENT_1`) + TVInputComponent1, + /// Switch to component input #2. (`KEYCODE_TV_INPUT_COMPONENT_2`) + TVInputComponent2, + /// Switch to composite input #1. (`KEYCODE_TV_INPUT_COMPOSITE_1`) + TVInputComposite1, + /// Switch to composite input #2. (`KEYCODE_TV_INPUT_COMPOSITE_2`) + TVInputComposite2, + /// Switch to HDMI input #1. (`KEYCODE_TV_INPUT_HDMI_1`) + TVInputHDMI1, + /// Switch to HDMI input #2. (`KEYCODE_TV_INPUT_HDMI_2`) + TVInputHDMI2, + /// Switch to HDMI input #3. (`KEYCODE_TV_INPUT_HDMI_3`) + TVInputHDMI3, + /// Switch to HDMI input #4. (`KEYCODE_TV_INPUT_HDMI_4`) + TVInputHDMI4, + /// Switch to VGA input #1. (`KEYCODE_TV_INPUT_VGA_1`) + TVInputVGA1, + /// Media context menu. (`KEYCODE_TV_MEDIA_CONTEXT_MENU`) + TVMediaContext, + /// Toggle network. (`KEYCODE_TV_NETWORK`) + TVNetwork, + /// Number entry. (`KEYCODE_TV_NUMBER_ENTRY`) + TVNumberEntry, + /// Toggle the power on an external TV. (`KEYCODE_TV_POWER`) + TVPower, + /// Radio. (`KEYCODE_TV_RADIO_SERVICE`) + TVRadioService, + /// Satellite. (`KEYCODE_TV_SATELLITE`) + TVSatellite, + /// Broadcast Satellite. (`KEYCODE_TV_SATELLITE_BS`) + TVSatelliteBS, + /// Communication Satellite. (`KEYCODE_TV_SATELLITE_CS`) + TVSatelliteCS, + /// Toggle between available satellites. (`KEYCODE_TV_SATELLITE_SERVICE`) + TVSatelliteToggle, + /// Analog Terrestrial. (`KEYCODE_TV_TERRESTRIAL_ANALOG`) + TVTerrestrialAnalog, + /// Digital Terrestrial. (`KEYCODE_TV_TERRESTRIAL_DIGITAL`) + TVTerrestrialDigital, + /// Timer programming. (`KEYCODE_TV_TIMER_PROGRAMMING`) + TVTimer, + /// Switch the input mode on an external AVR (audio/video receiver). (`KEYCODE_AVR_INPUT`) + AVRInput, + /// Toggle the power on an external AVR (audio/video receiver). (`KEYCODE_AVR_POWER`) + AVRPower, + /// General purpose color-coded media function key, as index 0 (red). (`VK_COLORED_KEY_0`, + /// `KEYCODE_PROG_RED`) + ColorF0Red, + /// General purpose color-coded media function key, as index 1 (green). (`VK_COLORED_KEY_1`, + /// `KEYCODE_PROG_GREEN`) + ColorF1Green, + /// General purpose color-coded media function key, as index 2 (yellow). (`VK_COLORED_KEY_2`, + /// `KEYCODE_PROG_YELLOW`) + ColorF2Yellow, + /// General purpose color-coded media function key, as index 3 (blue). (`VK_COLORED_KEY_3`, + /// `KEYCODE_PROG_BLUE`) + ColorF3Blue, + /// General purpose color-coded media function key, as index 4 (grey). (`VK_COLORED_KEY_4`) + ColorF4Grey, + /// General purpose color-coded media function key, as index 5 (brown). (`VK_COLORED_KEY_5`) + ColorF5Brown, + /// Toggle the display of Closed Captions. (`VK_CC`, `KEYCODE_CAPTIONS`) + ClosedCaptionToggle, + /// Adjust brightness of device, by toggling between or cycling through states. (`VK_DIMMER`) + Dimmer, + /// Swap video sources. (`VK_DISPLAY_SWAP`) + DisplaySwap, + /// Select Digital Video Rrecorder. (`KEYCODE_DVR`) + DVR, + /// Exit the current application. (`VK_EXIT`) + Exit, + /// Clear program or content stored as favorite 0. (`VK_CLEAR_FAVORITE_0`) + FavoriteClear0, + /// Clear program or content stored as favorite 1. (`VK_CLEAR_FAVORITE_1`) + FavoriteClear1, + /// Clear program or content stored as favorite 2. (`VK_CLEAR_FAVORITE_2`) + FavoriteClear2, + /// Clear program or content stored as favorite 3. (`VK_CLEAR_FAVORITE_3`) + FavoriteClear3, + /// Select (recall) program or content stored as favorite 0. (`VK_RECALL_FAVORITE_0`) + FavoriteRecall0, + /// Select (recall) program or content stored as favorite 1. (`VK_RECALL_FAVORITE_1`) + FavoriteRecall1, + /// Select (recall) program or content stored as favorite 2. (`VK_RECALL_FAVORITE_2`) + FavoriteRecall2, + /// Select (recall) program or content stored as favorite 3. (`VK_RECALL_FAVORITE_3`) + FavoriteRecall3, + /// Store current program or content as favorite 0. (`VK_STORE_FAVORITE_0`) + FavoriteStore0, + /// Store current program or content as favorite 1. (`VK_STORE_FAVORITE_1`) + FavoriteStore1, + /// Store current program or content as favorite 2. (`VK_STORE_FAVORITE_2`) + FavoriteStore2, + /// Store current program or content as favorite 3. (`VK_STORE_FAVORITE_3`) + FavoriteStore3, + /// Toggle display of program or content guide. (`VK_GUIDE`, `KEYCODE_GUIDE`) + Guide, + /// If guide is active and displayed, then display next day’s content. (`VK_NEXT_DAY`) + GuideNextDay, + /// If guide is active and displayed, then display previous day’s content. (`VK_PREV_DAY`) + GuidePreviousDay, + /// Toggle display of information about currently selected context or media. (`VK_INFO`, + /// `KEYCODE_INFO`) + Info, + /// Toggle instant replay. (`VK_INSTANT_REPLAY`) + InstantReplay, + /// Launch linked content, if available and appropriate. (`VK_LINK`) + Link, + /// List the current program. (`VK_LIST`) + ListProgram, + /// Toggle display listing of currently available live content or programs. (`VK_LIVE`) + LiveContent, + /// Lock or unlock current content or program. (`VK_LOCK`) + Lock, + /// Show a list of media applications: audio/video players and image viewers. (`VK_APPS`) + /// + /// Note: Do not confuse this key value with the Windows' `VK_APPS` / `VK_CONTEXT_MENU` key, + /// which is encoded as `"ContextMenu"`. + MediaApps, + /// Audio track key. (`KEYCODE_MEDIA_AUDIO_TRACK`) + MediaAudioTrack, + /// Select previously selected channel or media. (`VK_LAST`, `KEYCODE_LAST_CHANNEL`) + MediaLast, + /// Skip backward to next content or program. (`KEYCODE_MEDIA_SKIP_BACKWARD`) + MediaSkipBackward, + /// Skip forward to next content or program. (`VK_SKIP`, `KEYCODE_MEDIA_SKIP_FORWARD`) + MediaSkipForward, + /// Step backward to next content or program. (`KEYCODE_MEDIA_STEP_BACKWARD`) + MediaStepBackward, + /// Step forward to next content or program. (`KEYCODE_MEDIA_STEP_FORWARD`) + MediaStepForward, + /// Media top menu. (`KEYCODE_MEDIA_TOP_MENU`) + MediaTopMenu, + /// Navigate in. (`KEYCODE_NAVIGATE_IN`) + NavigateIn, + /// Navigate to next key. (`KEYCODE_NAVIGATE_NEXT`) + NavigateNext, + /// Navigate out. (`KEYCODE_NAVIGATE_OUT`) + NavigateOut, + /// Navigate to previous key. (`KEYCODE_NAVIGATE_PREVIOUS`) + NavigatePrevious, + /// Cycle to next favorite channel (in favorites list). (`VK_NEXT_FAVORITE_CHANNEL`) + NextFavoriteChannel, + /// Cycle to next user profile (if there are multiple user profiles). (`VK_USER`) + NextUserProfile, + /// Access on-demand content or programs. (`VK_ON_DEMAND`) + OnDemand, + /// Pairing key to pair devices. (`KEYCODE_PAIRING`) + Pairing, + /// Move picture-in-picture window down. (`VK_PINP_DOWN`) + PinPDown, + /// Move picture-in-picture window. (`VK_PINP_MOVE`) + PinPMove, + /// Toggle display of picture-in-picture window. (`VK_PINP_TOGGLE`) + PinPToggle, + /// Move picture-in-picture window up. (`VK_PINP_UP`) + PinPUp, + /// Decrease media playback speed. (`VK_PLAY_SPEED_DOWN`) + PlaySpeedDown, + /// Reset playback to normal speed. (`VK_PLAY_SPEED_RESET`) + PlaySpeedReset, + /// Increase media playback speed. (`VK_PLAY_SPEED_UP`) + PlaySpeedUp, + /// Toggle random media or content shuffle mode. (`VK_RANDOM_TOGGLE`) + RandomToggle, + /// Not a physical key, but this key code is sent when the remote control battery is low. + /// (`VK_RC_LOW_BATTERY`) + RcLowBattery, + /// Toggle or cycle between media recording speeds. (`VK_RECORD_SPEED_NEXT`) + RecordSpeedNext, + /// Toggle RF (radio frequency) input bypass mode (pass RF input directly to the RF output). + /// (`VK_RF_BYPASS`) + RfBypass, + /// Toggle scan channels mode. (`VK_SCAN_CHANNELS_TOGGLE`) + ScanChannelsToggle, + /// Advance display screen mode to next available mode. (`VK_SCREEN_MODE_NEXT`) + ScreenModeNext, + /// Toggle display of device settings screen. (`VK_SETTINGS`, `KEYCODE_SETTINGS`) + Settings, + /// Toggle split screen mode. (`VK_SPLIT_SCREEN_TOGGLE`) + SplitScreenToggle, + /// Switch the input mode on an external STB (set top box). (`KEYCODE_STB_INPUT`) + STBInput, + /// Toggle the power on an external STB (set top box). (`KEYCODE_STB_POWER`) + STBPower, + /// Toggle display of subtitles, if available. (`VK_SUBTITLE`) + Subtitle, + /// Toggle display of teletext, if available (`VK_TELETEXT`, `KEYCODE_TV_TELETEXT`). + Teletext, + /// Advance video mode to next available mode. (`VK_VIDEO_MODE_NEXT`) + VideoModeNext, + /// Cause device to identify itself in some manner, e.g., audibly or visibly. (`VK_WINK`) + Wink, + /// Toggle between full-screen and scaled content, or alter magnification level. (`VK_ZOOM`, + /// `KEYCODE_TV_ZOOM_MODE`) + ZoomToggle, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F1, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F2, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F3, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F4, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F5, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F6, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F7, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F8, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F9, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F10, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F11, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F12, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F13, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F14, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F15, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F16, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F17, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F18, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F19, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F20, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F21, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F22, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F23, + /// General-purpose function key. + /// Usually found at the top of the keyboard. + F24, + /// General-purpose function key. + F25, + /// General-purpose function key. + F26, + /// General-purpose function key. + F27, + /// General-purpose function key. + F28, + /// General-purpose function key. + F29, + /// General-purpose function key. + F30, + /// General-purpose function key. + F31, + /// General-purpose function key. + F32, + /// General-purpose function key. + F33, + /// General-purpose function key. + F34, + /// General-purpose function key. + F35, +} + +/// Key represents the meaning of a keypress. +/// +/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with +/// additions: +/// - All simple variants are wrapped under the `Named` variant +/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`. +/// - The `Dead` variant here, can specify the character which is inserted when pressing the +/// dead-key twice. +/// +/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ + +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum Key { + /// A simple (unparameterised) action + Named(NamedKey), + + /// A key string that corresponds to the character typed by the user, taking into account the + /// user’s current locale setting, and any system-level keyboard mapping overrides that are in + /// effect. + Character(Str), + + /// This variant is used when the key cannot be translated to any other variant. + /// + /// The native key is provided (if available) in order to allow the user to specify keybindings + /// for keys which are not defined by this API, mainly through some sort of UI. + Unidentified(NativeKey), + + /// Contains the text representation of the dead-key when available. + /// + /// ## Platform-specific + /// - **Web:** Always contains `None` + Dead(Option), +} + +impl From for Key { + #[inline] + fn from(action: NamedKey) -> Self { + Key::Named(action) + } +} + +impl From for Key { + #[inline] + fn from(code: NativeKey) -> Self { + Key::Unidentified(code) + } +} + +impl PartialEq for Key { + #[inline] + fn eq(&self, rhs: &NamedKey) -> bool { + match self { + Key::Named(ref a) => a == rhs, + _ => false, + } + } +} + +impl> PartialEq for Key { + #[inline] + fn eq(&self, rhs: &str) -> bool { + match self { + Key::Character(ref s) => s == rhs, + _ => false, + } + } +} + +impl> PartialEq<&str> for Key { + #[inline] + fn eq(&self, rhs: &&str) -> bool { + self == *rhs + } +} + +impl PartialEq for Key { + #[inline] + fn eq(&self, rhs: &NativeKey) -> bool { + match self { + Key::Unidentified(ref code) => code == rhs, + _ => false, + } + } +} + +impl PartialEq> for NativeKey { + #[inline] + fn eq(&self, rhs: &Key) -> bool { + rhs == self + } +} + +impl Key { + /// Convert `Key::Character(SmolStr)` to `Key::Character(&str)` so you can more easily match on + /// `Key`. All other variants remain unchanged. + pub fn as_ref(&self) -> Key<&str> { + match self { + Key::Named(a) => Key::Named(*a), + Key::Character(ch) => Key::Character(ch.as_str()), + Key::Dead(d) => Key::Dead(*d), + Key::Unidentified(u) => Key::Unidentified(u.clone()), + } + } +} + +impl NamedKey { + pub fn to_text(&self) -> Option<&str> { + match self { + NamedKey::Enter => Some("\r"), + NamedKey::Backspace => Some("\x08"), + NamedKey::Tab => Some("\t"), + NamedKey::Space => Some(" "), + NamedKey::Escape => Some("\x1b"), + _ => None, + } + } +} + +impl Key { + pub fn to_text(&self) -> Option<&str> { + match self { + Key::Named(action) => action.to_text(), + Key::Character(ch) => Some(ch.as_str()), + _ => None, + } + } +} + +/// The location of the key on the keyboard. +/// +/// Certain physical keys on the keyboard can have the same value, but are in different locations. +/// For instance, the Shift key can be on the left or right side of the keyboard, or the number +/// keys can be above the letters or on the numpad. This enum allows the user to differentiate +/// them. +/// +/// See the documentation for the [`location`] field on the [`KeyEvent`] struct for more +/// information. +/// +/// [`location`]: ../event/struct.KeyEvent.html#structfield.location +/// [`KeyEvent`]: crate::event::KeyEvent +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum KeyLocation { + /// The key is in its "normal" location on the keyboard. + /// + /// For instance, the "1" key above the "Q" key on a QWERTY keyboard will use this location. + /// This invariant is also returned when the location of the key cannot be identified. + /// + /// ![Standard 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_standard_1_key.svg) + /// + /// + /// For image attribution, see the + /// + /// ATTRIBUTION.md + /// + /// file. + /// + Standard, + + /// The key is on the left side of the keyboard. + /// + /// For instance, the left Shift key below the Caps Lock key on a QWERTY keyboard will use this + /// location. + /// + /// ![Left Shift key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_left_shift_key.svg) + /// + /// + /// For image attribution, see the + /// + /// ATTRIBUTION.md + /// + /// file. + /// + Left, + + /// The key is on the right side of the keyboard. + /// + /// For instance, the right Shift key below the Enter key on a QWERTY keyboard will use this + /// location. + /// + /// ![Right Shift key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_right_shift_key.svg) + /// + /// + /// For image attribution, see the + /// + /// ATTRIBUTION.md + /// + /// file. + /// + Right, + + /// The key is on the numpad. + /// + /// For instance, the "1" key on the numpad will use this location. + /// + /// ![Numpad 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_numpad_1_key.svg) + /// + /// + /// For image attribution, see the + /// + /// ATTRIBUTION.md + /// + /// file. + /// + Numpad, +} + +bitflags! { + /// Represents the current state of the keyboard modifiers + /// + /// Each flag represents a modifier and is set if this modifier is active. + #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct ModifiersState: u32 { + /// The "shift" key. + const SHIFT = 0b100; + /// The "control" key. + const CONTROL = 0b100 << 3; + /// The "alt" key. + const ALT = 0b100 << 6; + /// This is the "windows" key on PC and "command" key on Mac. + const SUPER = 0b100 << 9; + } +} + +impl ModifiersState { + /// Returns `true` if the shift key is pressed. + pub fn shift_key(&self) -> bool { + self.intersects(Self::SHIFT) + } + + /// Returns `true` if the control key is pressed. + pub fn control_key(&self) -> bool { + self.intersects(Self::CONTROL) + } + + /// Returns `true` if the alt key is pressed. + pub fn alt_key(&self) -> bool { + self.intersects(Self::ALT) + } + + /// Returns `true` if the super key is pressed. + pub fn super_key(&self) -> bool { + self.intersects(Self::SUPER) + } +} + +/// The state of the particular modifiers key. +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub enum ModifiersKeyState { + /// The particular key is pressed. + Pressed, + /// The state of the key is unknown. + #[default] + Unknown, +} + +// NOTE: the exact modifier key is not used to represent modifiers state in the +// first place due to a fact that modifiers state could be changed without any +// key being pressed and on some platforms like Wayland/X11 which key resulted +// in modifiers change is hidden, also, not that it really matters. +// +// The reason this API is even exposed is mostly to provide a way for users +// to treat modifiers differently based on their position, which is required +// on macOS due to their AltGr/Option situation. +bitflags! { + #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub(crate) struct ModifiersKeys: u8 { + const LSHIFT = 0b0000_0001; + const RSHIFT = 0b0000_0010; + const LCONTROL = 0b0000_0100; + const RCONTROL = 0b0000_1000; + const LALT = 0b0001_0000; + const RALT = 0b0010_0000; + const LSUPER = 0b0100_0000; + const RSUPER = 0b1000_0000; + } +} + + diff --git a/sessionlockev/src/keymap.rs b/sessionlockev/src/keymap.rs new file mode 100644 index 0000000..59d022d --- /dev/null +++ b/sessionlockev/src/keymap.rs @@ -0,0 +1,894 @@ +//! XKB keymap. + +use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey}; + +/// Map the raw X11-style keycode to the `KeyCode` enum. +/// +/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses. +pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey { + scancode_to_physicalkey(keycode.saturating_sub(8)) +} + +/// Map the linux scancode to Keycode. +/// +/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode. +pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { + // The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as + // libxkbcommon's documentation seems to suggest that the keycode values we're interested in + // are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes, + // I can only hope they agree on what the keycodes mean. + // + // Some of the keycodes are likely superfluous for our purposes, and some are ones which are + // difficult to test the correctness of, or discover the purpose of. Because of this, they've + // either been commented out here, or not included at all. + PhysicalKey::Code(match scancode { + 0 => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(0)), + 1 => KeyCode::Escape, + 2 => KeyCode::Digit1, + 3 => KeyCode::Digit2, + 4 => KeyCode::Digit3, + 5 => KeyCode::Digit4, + 6 => KeyCode::Digit5, + 7 => KeyCode::Digit6, + 8 => KeyCode::Digit7, + 9 => KeyCode::Digit8, + 10 => KeyCode::Digit9, + 11 => KeyCode::Digit0, + 12 => KeyCode::Minus, + 13 => KeyCode::Equal, + 14 => KeyCode::Backspace, + 15 => KeyCode::Tab, + 16 => KeyCode::KeyQ, + 17 => KeyCode::KeyW, + 18 => KeyCode::KeyE, + 19 => KeyCode::KeyR, + 20 => KeyCode::KeyT, + 21 => KeyCode::KeyY, + 22 => KeyCode::KeyU, + 23 => KeyCode::KeyI, + 24 => KeyCode::KeyO, + 25 => KeyCode::KeyP, + 26 => KeyCode::BracketLeft, + 27 => KeyCode::BracketRight, + 28 => KeyCode::Enter, + 29 => KeyCode::ControlLeft, + 30 => KeyCode::KeyA, + 31 => KeyCode::KeyS, + 32 => KeyCode::KeyD, + 33 => KeyCode::KeyF, + 34 => KeyCode::KeyG, + 35 => KeyCode::KeyH, + 36 => KeyCode::KeyJ, + 37 => KeyCode::KeyK, + 38 => KeyCode::KeyL, + 39 => KeyCode::Semicolon, + 40 => KeyCode::Quote, + 41 => KeyCode::Backquote, + 42 => KeyCode::ShiftLeft, + 43 => KeyCode::Backslash, + 44 => KeyCode::KeyZ, + 45 => KeyCode::KeyX, + 46 => KeyCode::KeyC, + 47 => KeyCode::KeyV, + 48 => KeyCode::KeyB, + 49 => KeyCode::KeyN, + 50 => KeyCode::KeyM, + 51 => KeyCode::Comma, + 52 => KeyCode::Period, + 53 => KeyCode::Slash, + 54 => KeyCode::ShiftRight, + 55 => KeyCode::NumpadMultiply, + 56 => KeyCode::AltLeft, + 57 => KeyCode::Space, + 58 => KeyCode::CapsLock, + 59 => KeyCode::F1, + 60 => KeyCode::F2, + 61 => KeyCode::F3, + 62 => KeyCode::F4, + 63 => KeyCode::F5, + 64 => KeyCode::F6, + 65 => KeyCode::F7, + 66 => KeyCode::F8, + 67 => KeyCode::F9, + 68 => KeyCode::F10, + 69 => KeyCode::NumLock, + 70 => KeyCode::ScrollLock, + 71 => KeyCode::Numpad7, + 72 => KeyCode::Numpad8, + 73 => KeyCode::Numpad9, + 74 => KeyCode::NumpadSubtract, + 75 => KeyCode::Numpad4, + 76 => KeyCode::Numpad5, + 77 => KeyCode::Numpad6, + 78 => KeyCode::NumpadAdd, + 79 => KeyCode::Numpad1, + 80 => KeyCode::Numpad2, + 81 => KeyCode::Numpad3, + 82 => KeyCode::Numpad0, + 83 => KeyCode::NumpadDecimal, + 85 => KeyCode::Lang5, + 86 => KeyCode::IntlBackslash, + 87 => KeyCode::F11, + 88 => KeyCode::F12, + 89 => KeyCode::IntlRo, + 90 => KeyCode::Lang3, + 91 => KeyCode::Lang4, + 92 => KeyCode::Convert, + 93 => KeyCode::KanaMode, + 94 => KeyCode::NonConvert, + // 95 => KeyCode::KPJPCOMMA, + 96 => KeyCode::NumpadEnter, + 97 => KeyCode::ControlRight, + 98 => KeyCode::NumpadDivide, + 99 => KeyCode::PrintScreen, + 100 => KeyCode::AltRight, + // 101 => KeyCode::LINEFEED, + 102 => KeyCode::Home, + 103 => KeyCode::ArrowUp, + 104 => KeyCode::PageUp, + 105 => KeyCode::ArrowLeft, + 106 => KeyCode::ArrowRight, + 107 => KeyCode::End, + 108 => KeyCode::ArrowDown, + 109 => KeyCode::PageDown, + 110 => KeyCode::Insert, + 111 => KeyCode::Delete, + // 112 => KeyCode::MACRO, + 113 => KeyCode::AudioVolumeMute, + 114 => KeyCode::AudioVolumeDown, + 115 => KeyCode::AudioVolumeUp, + // 116 => KeyCode::POWER, + 117 => KeyCode::NumpadEqual, + // 118 => KeyCode::KPPLUSMINUS, + 119 => KeyCode::Pause, + // 120 => KeyCode::SCALE, + 121 => KeyCode::NumpadComma, + 122 => KeyCode::Lang1, + 123 => KeyCode::Lang2, + 124 => KeyCode::IntlYen, + 125 => KeyCode::SuperLeft, + 126 => KeyCode::SuperRight, + 127 => KeyCode::ContextMenu, + // 128 => KeyCode::STOP, + // 129 => KeyCode::AGAIN, + // 130 => KeyCode::PROPS, + // 131 => KeyCode::UNDO, + // 132 => KeyCode::FRONT, + // 133 => KeyCode::COPY, + // 134 => KeyCode::OPEN, + // 135 => KeyCode::PASTE, + // 136 => KeyCode::FIND, + // 137 => KeyCode::CUT, + // 138 => KeyCode::HELP, + // 139 => KeyCode::MENU, + // 140 => KeyCode::CALC, + // 141 => KeyCode::SETUP, + // 142 => KeyCode::SLEEP, + // 143 => KeyCode::WAKEUP, + // 144 => KeyCode::FILE, + // 145 => KeyCode::SENDFILE, + // 146 => KeyCode::DELETEFILE, + // 147 => KeyCode::XFER, + // 148 => KeyCode::PROG1, + // 149 => KeyCode::PROG2, + // 150 => KeyCode::WWW, + // 151 => KeyCode::MSDOS, + // 152 => KeyCode::COFFEE, + // 153 => KeyCode::ROTATE_DISPLAY, + // 154 => KeyCode::CYCLEWINDOWS, + // 155 => KeyCode::MAIL, + // 156 => KeyCode::BOOKMARKS, + // 157 => KeyCode::COMPUTER, + // 158 => KeyCode::BACK, + // 159 => KeyCode::FORWARD, + // 160 => KeyCode::CLOSECD, + // 161 => KeyCode::EJECTCD, + // 162 => KeyCode::EJECTCLOSECD, + 163 => KeyCode::MediaTrackNext, + 164 => KeyCode::MediaPlayPause, + 165 => KeyCode::MediaTrackPrevious, + 166 => KeyCode::MediaStop, + // 167 => KeyCode::RECORD, + // 168 => KeyCode::REWIND, + // 169 => KeyCode::PHONE, + // 170 => KeyCode::ISO, + // 171 => KeyCode::CONFIG, + // 172 => KeyCode::HOMEPAGE, + // 173 => KeyCode::REFRESH, + // 174 => KeyCode::EXIT, + // 175 => KeyCode::MOVE, + // 176 => KeyCode::EDIT, + // 177 => KeyCode::SCROLLUP, + // 178 => KeyCode::SCROLLDOWN, + // 179 => KeyCode::KPLEFTPAREN, + // 180 => KeyCode::KPRIGHTPAREN, + // 181 => KeyCode::NEW, + // 182 => KeyCode::REDO, + 183 => KeyCode::F13, + 184 => KeyCode::F14, + 185 => KeyCode::F15, + 186 => KeyCode::F16, + 187 => KeyCode::F17, + 188 => KeyCode::F18, + 189 => KeyCode::F19, + 190 => KeyCode::F20, + 191 => KeyCode::F21, + 192 => KeyCode::F22, + 193 => KeyCode::F23, + 194 => KeyCode::F24, + // 200 => KeyCode::PLAYCD, + // 201 => KeyCode::PAUSECD, + // 202 => KeyCode::PROG3, + // 203 => KeyCode::PROG4, + // 204 => KeyCode::DASHBOARD, + // 205 => KeyCode::SUSPEND, + // 206 => KeyCode::CLOSE, + // 207 => KeyCode::PLAY, + // 208 => KeyCode::FASTFORWARD, + // 209 => KeyCode::BASSBOOST, + // 210 => KeyCode::PRINT, + // 211 => KeyCode::HP, + // 212 => KeyCode::CAMERA, + // 213 => KeyCode::SOUND, + // 214 => KeyCode::QUESTION, + // 215 => KeyCode::EMAIL, + // 216 => KeyCode::CHAT, + // 217 => KeyCode::SEARCH, + // 218 => KeyCode::CONNECT, + // 219 => KeyCode::FINANCE, + // 220 => KeyCode::SPORT, + // 221 => KeyCode::SHOP, + // 222 => KeyCode::ALTERASE, + // 223 => KeyCode::CANCEL, + // 224 => KeyCode::BRIGHTNESSDOW, + // 225 => KeyCode::BRIGHTNESSU, + // 226 => KeyCode::MEDIA, + // 227 => KeyCode::SWITCHVIDEOMODE, + // 228 => KeyCode::KBDILLUMTOGGLE, + // 229 => KeyCode::KBDILLUMDOWN, + // 230 => KeyCode::KBDILLUMUP, + // 231 => KeyCode::SEND, + // 232 => KeyCode::REPLY, + // 233 => KeyCode::FORWARDMAIL, + // 234 => KeyCode::SAVE, + // 235 => KeyCode::DOCUMENTS, + // 236 => KeyCode::BATTERY, + // 237 => KeyCode::BLUETOOTH, + // 238 => KeyCode::WLAN, + // 239 => KeyCode::UWB, + 240 => return PhysicalKey::Unidentified(NativeKeyCode::Unidentified), + // 241 => KeyCode::VIDEO_NEXT, + // 242 => KeyCode::VIDEO_PREV, + // 243 => KeyCode::BRIGHTNESS_CYCLE, + // 244 => KeyCode::BRIGHTNESS_AUTO, + // 245 => KeyCode::DISPLAY_OFF, + // 246 => KeyCode::WWAN, + // 247 => KeyCode::RFKILL, + // 248 => KeyCode::KEY_MICMUTE, + _ => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(scancode)), + }) +} + + +// NOTE: maybe one day need it +#[allow(unused)] +pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option { + let code = match key { + PhysicalKey::Code(code) => code, + PhysicalKey::Unidentified(code) => { + return match code { + NativeKeyCode::Unidentified => Some(240), + NativeKeyCode::Xkb(raw) => Some(raw), + _ => None, + }; + } + }; + + match code { + KeyCode::Escape => Some(1), + KeyCode::Digit1 => Some(2), + KeyCode::Digit2 => Some(3), + KeyCode::Digit3 => Some(4), + KeyCode::Digit4 => Some(5), + KeyCode::Digit5 => Some(6), + KeyCode::Digit6 => Some(7), + KeyCode::Digit7 => Some(8), + KeyCode::Digit8 => Some(9), + KeyCode::Digit9 => Some(10), + KeyCode::Digit0 => Some(11), + KeyCode::Minus => Some(12), + KeyCode::Equal => Some(13), + KeyCode::Backspace => Some(14), + KeyCode::Tab => Some(15), + KeyCode::KeyQ => Some(16), + KeyCode::KeyW => Some(17), + KeyCode::KeyE => Some(18), + KeyCode::KeyR => Some(19), + KeyCode::KeyT => Some(20), + KeyCode::KeyY => Some(21), + KeyCode::KeyU => Some(22), + KeyCode::KeyI => Some(23), + KeyCode::KeyO => Some(24), + KeyCode::KeyP => Some(25), + KeyCode::BracketLeft => Some(26), + KeyCode::BracketRight => Some(27), + KeyCode::Enter => Some(28), + KeyCode::ControlLeft => Some(29), + KeyCode::KeyA => Some(30), + KeyCode::KeyS => Some(31), + KeyCode::KeyD => Some(32), + KeyCode::KeyF => Some(33), + KeyCode::KeyG => Some(34), + KeyCode::KeyH => Some(35), + KeyCode::KeyJ => Some(36), + KeyCode::KeyK => Some(37), + KeyCode::KeyL => Some(38), + KeyCode::Semicolon => Some(39), + KeyCode::Quote => Some(40), + KeyCode::Backquote => Some(41), + KeyCode::ShiftLeft => Some(42), + KeyCode::Backslash => Some(43), + KeyCode::KeyZ => Some(44), + KeyCode::KeyX => Some(45), + KeyCode::KeyC => Some(46), + KeyCode::KeyV => Some(47), + KeyCode::KeyB => Some(48), + KeyCode::KeyN => Some(49), + KeyCode::KeyM => Some(50), + KeyCode::Comma => Some(51), + KeyCode::Period => Some(52), + KeyCode::Slash => Some(53), + KeyCode::ShiftRight => Some(54), + KeyCode::NumpadMultiply => Some(55), + KeyCode::AltLeft => Some(56), + KeyCode::Space => Some(57), + KeyCode::CapsLock => Some(58), + KeyCode::F1 => Some(59), + KeyCode::F2 => Some(60), + KeyCode::F3 => Some(61), + KeyCode::F4 => Some(62), + KeyCode::F5 => Some(63), + KeyCode::F6 => Some(64), + KeyCode::F7 => Some(65), + KeyCode::F8 => Some(66), + KeyCode::F9 => Some(67), + KeyCode::F10 => Some(68), + KeyCode::NumLock => Some(69), + KeyCode::ScrollLock => Some(70), + KeyCode::Numpad7 => Some(71), + KeyCode::Numpad8 => Some(72), + KeyCode::Numpad9 => Some(73), + KeyCode::NumpadSubtract => Some(74), + KeyCode::Numpad4 => Some(75), + KeyCode::Numpad5 => Some(76), + KeyCode::Numpad6 => Some(77), + KeyCode::NumpadAdd => Some(78), + KeyCode::Numpad1 => Some(79), + KeyCode::Numpad2 => Some(80), + KeyCode::Numpad3 => Some(81), + KeyCode::Numpad0 => Some(82), + KeyCode::NumpadDecimal => Some(83), + KeyCode::Lang5 => Some(85), + KeyCode::IntlBackslash => Some(86), + KeyCode::F11 => Some(87), + KeyCode::F12 => Some(88), + KeyCode::IntlRo => Some(89), + KeyCode::Lang3 => Some(90), + KeyCode::Lang4 => Some(91), + KeyCode::Convert => Some(92), + KeyCode::KanaMode => Some(93), + KeyCode::NonConvert => Some(94), + KeyCode::NumpadEnter => Some(96), + KeyCode::ControlRight => Some(97), + KeyCode::NumpadDivide => Some(98), + KeyCode::PrintScreen => Some(99), + KeyCode::AltRight => Some(100), + KeyCode::Home => Some(102), + KeyCode::ArrowUp => Some(103), + KeyCode::PageUp => Some(104), + KeyCode::ArrowLeft => Some(105), + KeyCode::ArrowRight => Some(106), + KeyCode::End => Some(107), + KeyCode::ArrowDown => Some(108), + KeyCode::PageDown => Some(109), + KeyCode::Insert => Some(110), + KeyCode::Delete => Some(111), + KeyCode::AudioVolumeMute => Some(113), + KeyCode::AudioVolumeDown => Some(114), + KeyCode::AudioVolumeUp => Some(115), + KeyCode::NumpadEqual => Some(117), + KeyCode::Pause => Some(119), + KeyCode::NumpadComma => Some(121), + KeyCode::Lang1 => Some(122), + KeyCode::Lang2 => Some(123), + KeyCode::IntlYen => Some(124), + KeyCode::SuperLeft => Some(125), + KeyCode::SuperRight => Some(126), + KeyCode::ContextMenu => Some(127), + KeyCode::MediaTrackNext => Some(163), + KeyCode::MediaPlayPause => Some(164), + KeyCode::MediaTrackPrevious => Some(165), + KeyCode::MediaStop => Some(166), + KeyCode::F13 => Some(183), + KeyCode::F14 => Some(184), + KeyCode::F15 => Some(185), + KeyCode::F16 => Some(186), + KeyCode::F17 => Some(187), + KeyCode::F18 => Some(188), + KeyCode::F19 => Some(189), + KeyCode::F20 => Some(190), + KeyCode::F21 => Some(191), + KeyCode::F22 => Some(192), + KeyCode::F23 => Some(193), + KeyCode::F24 => Some(194), + _ => None, + } +} + +pub fn keysym_to_key(keysym: u32) -> Key { + use xkbcommon_dl::keysyms; + Key::Named(match keysym { + // TTY function keys + keysyms::BackSpace => NamedKey::Backspace, + keysyms::Tab => NamedKey::Tab, + // keysyms::Linefeed => NamedKey::Linefeed, + keysyms::Clear => NamedKey::Clear, + keysyms::Return => NamedKey::Enter, + keysyms::Pause => NamedKey::Pause, + keysyms::Scroll_Lock => NamedKey::ScrollLock, + keysyms::Sys_Req => NamedKey::PrintScreen, + keysyms::Escape => NamedKey::Escape, + keysyms::Delete => NamedKey::Delete, + + // IME keys + keysyms::Multi_key => NamedKey::Compose, + keysyms::Codeinput => NamedKey::CodeInput, + keysyms::SingleCandidate => NamedKey::SingleCandidate, + keysyms::MultipleCandidate => NamedKey::AllCandidates, + keysyms::PreviousCandidate => NamedKey::PreviousCandidate, + + // Japanese keys + keysyms::Kanji => NamedKey::KanjiMode, + keysyms::Muhenkan => NamedKey::NonConvert, + keysyms::Henkan_Mode => NamedKey::Convert, + keysyms::Romaji => NamedKey::Romaji, + keysyms::Hiragana => NamedKey::Hiragana, + keysyms::Hiragana_Katakana => NamedKey::HiraganaKatakana, + keysyms::Zenkaku => NamedKey::Zenkaku, + keysyms::Hankaku => NamedKey::Hankaku, + keysyms::Zenkaku_Hankaku => NamedKey::ZenkakuHankaku, + // keysyms::Touroku => NamedKey::Touroku, + // keysyms::Massyo => NamedKey::Massyo, + keysyms::Kana_Lock => NamedKey::KanaMode, + keysyms::Kana_Shift => NamedKey::KanaMode, + keysyms::Eisu_Shift => NamedKey::Alphanumeric, + keysyms::Eisu_toggle => NamedKey::Alphanumeric, + // NOTE: The next three items are aliases for values we've already mapped. + // keysyms::Kanji_Bangou => NamedKey::CodeInput, + // keysyms::Zen_Koho => NamedKey::AllCandidates, + // keysyms::Mae_Koho => NamedKey::PreviousCandidate, + + // Cursor control & motion + keysyms::Home => NamedKey::Home, + keysyms::Left => NamedKey::ArrowLeft, + keysyms::Up => NamedKey::ArrowUp, + keysyms::Right => NamedKey::ArrowRight, + keysyms::Down => NamedKey::ArrowDown, + // keysyms::Prior => NamedKey::PageUp, + keysyms::Page_Up => NamedKey::PageUp, + // keysyms::Next => NamedKey::PageDown, + keysyms::Page_Down => NamedKey::PageDown, + keysyms::End => NamedKey::End, + // keysyms::Begin => NamedKey::Begin, + + // Misc. functions + keysyms::Select => NamedKey::Select, + keysyms::Print => NamedKey::PrintScreen, + keysyms::Execute => NamedKey::Execute, + keysyms::Insert => NamedKey::Insert, + keysyms::Undo => NamedKey::Undo, + keysyms::Redo => NamedKey::Redo, + keysyms::Menu => NamedKey::ContextMenu, + keysyms::Find => NamedKey::Find, + keysyms::Cancel => NamedKey::Cancel, + keysyms::Help => NamedKey::Help, + keysyms::Break => NamedKey::Pause, + keysyms::Mode_switch => NamedKey::ModeChange, + // keysyms::script_switch => NamedKey::ModeChange, + keysyms::Num_Lock => NamedKey::NumLock, + + // Keypad keys + // keysyms::KP_Space => return Key::Character(" "), + keysyms::KP_Tab => NamedKey::Tab, + keysyms::KP_Enter => NamedKey::Enter, + keysyms::KP_F1 => NamedKey::F1, + keysyms::KP_F2 => NamedKey::F2, + keysyms::KP_F3 => NamedKey::F3, + keysyms::KP_F4 => NamedKey::F4, + keysyms::KP_Home => NamedKey::Home, + keysyms::KP_Left => NamedKey::ArrowLeft, + keysyms::KP_Up => NamedKey::ArrowUp, + keysyms::KP_Right => NamedKey::ArrowRight, + keysyms::KP_Down => NamedKey::ArrowDown, + // keysyms::KP_Prior => NamedKey::PageUp, + keysyms::KP_Page_Up => NamedKey::PageUp, + // keysyms::KP_Next => NamedKey::PageDown, + keysyms::KP_Page_Down => NamedKey::PageDown, + keysyms::KP_End => NamedKey::End, + // This is the key labeled "5" on the numpad when NumLock is off. + // keysyms::KP_Begin => NamedKey::Begin, + keysyms::KP_Insert => NamedKey::Insert, + keysyms::KP_Delete => NamedKey::Delete, + // keysyms::KP_Equal => NamedKey::Equal, + // keysyms::KP_Multiply => NamedKey::Multiply, + // keysyms::KP_Add => NamedKey::Add, + // keysyms::KP_Separator => NamedKey::Separator, + // keysyms::KP_Subtract => NamedKey::Subtract, + // keysyms::KP_Decimal => NamedKey::Decimal, + // keysyms::KP_Divide => NamedKey::Divide, + + // keysyms::KP_0 => return Key::Character("0"), + // keysyms::KP_1 => return Key::Character("1"), + // keysyms::KP_2 => return Key::Character("2"), + // keysyms::KP_3 => return Key::Character("3"), + // keysyms::KP_4 => return Key::Character("4"), + // keysyms::KP_5 => return Key::Character("5"), + // keysyms::KP_6 => return Key::Character("6"), + // keysyms::KP_7 => return Key::Character("7"), + // keysyms::KP_8 => return Key::Character("8"), + // keysyms::KP_9 => return Key::Character("9"), + + // Function keys + keysyms::F1 => NamedKey::F1, + keysyms::F2 => NamedKey::F2, + keysyms::F3 => NamedKey::F3, + keysyms::F4 => NamedKey::F4, + keysyms::F5 => NamedKey::F5, + keysyms::F6 => NamedKey::F6, + keysyms::F7 => NamedKey::F7, + keysyms::F8 => NamedKey::F8, + keysyms::F9 => NamedKey::F9, + keysyms::F10 => NamedKey::F10, + keysyms::F11 => NamedKey::F11, + keysyms::F12 => NamedKey::F12, + keysyms::F13 => NamedKey::F13, + keysyms::F14 => NamedKey::F14, + keysyms::F15 => NamedKey::F15, + keysyms::F16 => NamedKey::F16, + keysyms::F17 => NamedKey::F17, + keysyms::F18 => NamedKey::F18, + keysyms::F19 => NamedKey::F19, + keysyms::F20 => NamedKey::F20, + keysyms::F21 => NamedKey::F21, + keysyms::F22 => NamedKey::F22, + keysyms::F23 => NamedKey::F23, + keysyms::F24 => NamedKey::F24, + keysyms::F25 => NamedKey::F25, + keysyms::F26 => NamedKey::F26, + keysyms::F27 => NamedKey::F27, + keysyms::F28 => NamedKey::F28, + keysyms::F29 => NamedKey::F29, + keysyms::F30 => NamedKey::F30, + keysyms::F31 => NamedKey::F31, + keysyms::F32 => NamedKey::F32, + keysyms::F33 => NamedKey::F33, + keysyms::F34 => NamedKey::F34, + keysyms::F35 => NamedKey::F35, + + // Modifiers + keysyms::Shift_L => NamedKey::Shift, + keysyms::Shift_R => NamedKey::Shift, + keysyms::Control_L => NamedKey::Control, + keysyms::Control_R => NamedKey::Control, + keysyms::Caps_Lock => NamedKey::CapsLock, + // keysyms::Shift_Lock => NamedKey::ShiftLock, + + // keysyms::Meta_L => NamedKey::Meta, + // keysyms::Meta_R => NamedKey::Meta, + keysyms::Alt_L => NamedKey::Alt, + keysyms::Alt_R => NamedKey::Alt, + keysyms::Super_L => NamedKey::Super, + keysyms::Super_R => NamedKey::Super, + keysyms::Hyper_L => NamedKey::Hyper, + keysyms::Hyper_R => NamedKey::Hyper, + + // XKB function and modifier keys + // keysyms::ISO_Lock => NamedKey::IsoLock, + // keysyms::ISO_Level2_Latch => NamedKey::IsoLevel2Latch, + keysyms::ISO_Level3_Shift => NamedKey::AltGraph, + keysyms::ISO_Level3_Latch => NamedKey::AltGraph, + keysyms::ISO_Level3_Lock => NamedKey::AltGraph, + // keysyms::ISO_Level5_Shift => NamedKey::IsoLevel5Shift, + // keysyms::ISO_Level5_Latch => NamedKey::IsoLevel5Latch, + // keysyms::ISO_Level5_Lock => NamedKey::IsoLevel5Lock, + // keysyms::ISO_Group_Shift => NamedKey::IsoGroupShift, + // keysyms::ISO_Group_Latch => NamedKey::IsoGroupLatch, + // keysyms::ISO_Group_Lock => NamedKey::IsoGroupLock, + keysyms::ISO_Next_Group => NamedKey::GroupNext, + // keysyms::ISO_Next_Group_Lock => NamedKey::GroupNextLock, + keysyms::ISO_Prev_Group => NamedKey::GroupPrevious, + // keysyms::ISO_Prev_Group_Lock => NamedKey::GroupPreviousLock, + keysyms::ISO_First_Group => NamedKey::GroupFirst, + // keysyms::ISO_First_Group_Lock => NamedKey::GroupFirstLock, + keysyms::ISO_Last_Group => NamedKey::GroupLast, + // keysyms::ISO_Last_Group_Lock => NamedKey::GroupLastLock, + keysyms::ISO_Left_Tab => NamedKey::Tab, + // keysyms::ISO_Move_Line_Up => NamedKey::IsoMoveLineUp, + // keysyms::ISO_Move_Line_Down => NamedKey::IsoMoveLineDown, + // keysyms::ISO_Partial_Line_Up => NamedKey::IsoPartialLineUp, + // keysyms::ISO_Partial_Line_Down => NamedKey::IsoPartialLineDown, + // keysyms::ISO_Partial_Space_Left => NamedKey::IsoPartialSpaceLeft, + // keysyms::ISO_Partial_Space_Right => NamedKey::IsoPartialSpaceRight, + // keysyms::ISO_Set_Margin_Left => NamedKey::IsoSetMarginLeft, + // keysyms::ISO_Set_Margin_Right => NamedKey::IsoSetMarginRight, + // keysyms::ISO_Release_Margin_Left => NamedKey::IsoReleaseMarginLeft, + // keysyms::ISO_Release_Margin_Right => NamedKey::IsoReleaseMarginRight, + // keysyms::ISO_Release_Both_Margins => NamedKey::IsoReleaseBothMargins, + // keysyms::ISO_Fast_Cursor_Left => NamedKey::IsoFastCursorLeft, + // keysyms::ISO_Fast_Cursor_Right => NamedKey::IsoFastCursorRight, + // keysyms::ISO_Fast_Cursor_Up => NamedKey::IsoFastCursorUp, + // keysyms::ISO_Fast_Cursor_Down => NamedKey::IsoFastCursorDown, + // keysyms::ISO_Continuous_Underline => NamedKey::IsoContinuousUnderline, + // keysyms::ISO_Discontinuous_Underline => NamedKey::IsoDiscontinuousUnderline, + // keysyms::ISO_Emphasize => NamedKey::IsoEmphasize, + // keysyms::ISO_Center_Object => NamedKey::IsoCenterObject, + keysyms::ISO_Enter => NamedKey::Enter, + + // dead_grave..dead_currency + + // dead_lowline..dead_longsolidusoverlay + + // dead_a..dead_capital_schwa + + // dead_greek + + // First_Virtual_Screen..Terminate_Server + + // AccessX_Enable..AudibleBell_Enable + + // Pointer_Left..Pointer_Drag5 + + // Pointer_EnableKeys..Pointer_DfltBtnPrev + + // ch..C_H + + // 3270 terminal keys + // keysyms::3270_Duplicate => NamedKey::Duplicate, + // keysyms::3270_FieldMark => NamedKey::FieldMark, + // keysyms::3270_Right2 => NamedKey::Right2, + // keysyms::3270_Left2 => NamedKey::Left2, + // keysyms::3270_BackTab => NamedKey::BackTab, + keysyms::_3270_EraseEOF => NamedKey::EraseEof, + // keysyms::3270_EraseInput => NamedKey::EraseInput, + // keysyms::3270_Reset => NamedKey::Reset, + // keysyms::3270_Quit => NamedKey::Quit, + // keysyms::3270_PA1 => NamedKey::Pa1, + // keysyms::3270_PA2 => NamedKey::Pa2, + // keysyms::3270_PA3 => NamedKey::Pa3, + // keysyms::3270_Test => NamedKey::Test, + keysyms::_3270_Attn => NamedKey::Attn, + // keysyms::3270_CursorBlink => NamedKey::CursorBlink, + // keysyms::3270_AltCursor => NamedKey::AltCursor, + // keysyms::3270_KeyClick => NamedKey::KeyClick, + // keysyms::3270_Jump => NamedKey::Jump, + // keysyms::3270_Ident => NamedKey::Ident, + // keysyms::3270_Rule => NamedKey::Rule, + // keysyms::3270_Copy => NamedKey::Copy, + keysyms::_3270_Play => NamedKey::Play, + // keysyms::3270_Setup => NamedKey::Setup, + // keysyms::3270_Record => NamedKey::Record, + // keysyms::3270_ChangeScreen => NamedKey::ChangeScreen, + // keysyms::3270_DeleteWord => NamedKey::DeleteWord, + keysyms::_3270_ExSelect => NamedKey::ExSel, + keysyms::_3270_CursorSelect => NamedKey::CrSel, + keysyms::_3270_PrintScreen => NamedKey::PrintScreen, + keysyms::_3270_Enter => NamedKey::Enter, + + keysyms::space => NamedKey::Space, + // exclam..Sinh_kunddaliya + + // XFree86 + // keysyms::XF86_ModeLock => NamedKey::ModeLock, + + // XFree86 - Backlight controls + keysyms::XF86_MonBrightnessUp => NamedKey::BrightnessUp, + keysyms::XF86_MonBrightnessDown => NamedKey::BrightnessDown, + // keysyms::XF86_KbdLightOnOff => NamedKey::LightOnOff, + // keysyms::XF86_KbdBrightnessUp => NamedKey::KeyboardBrightnessUp, + // keysyms::XF86_KbdBrightnessDown => NamedKey::KeyboardBrightnessDown, + + // XFree86 - "Internet" + keysyms::XF86_Standby => NamedKey::Standby, + keysyms::XF86_AudioLowerVolume => NamedKey::AudioVolumeDown, + keysyms::XF86_AudioRaiseVolume => NamedKey::AudioVolumeUp, + keysyms::XF86_AudioPlay => NamedKey::MediaPlay, + keysyms::XF86_AudioStop => NamedKey::MediaStop, + keysyms::XF86_AudioPrev => NamedKey::MediaTrackPrevious, + keysyms::XF86_AudioNext => NamedKey::MediaTrackNext, + keysyms::XF86_HomePage => NamedKey::BrowserHome, + keysyms::XF86_Mail => NamedKey::LaunchMail, + // keysyms::XF86_Start => NamedKey::Start, + keysyms::XF86_Search => NamedKey::BrowserSearch, + keysyms::XF86_AudioRecord => NamedKey::MediaRecord, + + // XFree86 - PDA + keysyms::XF86_Calculator => NamedKey::LaunchApplication2, + // keysyms::XF86_Memo => NamedKey::Memo, + // keysyms::XF86_ToDoList => NamedKey::ToDoList, + keysyms::XF86_Calendar => NamedKey::LaunchCalendar, + keysyms::XF86_PowerDown => NamedKey::Power, + // keysyms::XF86_ContrastAdjust => NamedKey::AdjustContrast, + // keysyms::XF86_RockerUp => NamedKey::RockerUp, + // keysyms::XF86_RockerDown => NamedKey::RockerDown, + // keysyms::XF86_RockerEnter => NamedKey::RockerEnter, + + // XFree86 - More "Internet" + keysyms::XF86_Back => NamedKey::BrowserBack, + keysyms::XF86_Forward => NamedKey::BrowserForward, + // keysyms::XF86_Stop => NamedKey::Stop, + keysyms::XF86_Refresh => NamedKey::BrowserRefresh, + keysyms::XF86_PowerOff => NamedKey::Power, + keysyms::XF86_WakeUp => NamedKey::WakeUp, + keysyms::XF86_Eject => NamedKey::Eject, + keysyms::XF86_ScreenSaver => NamedKey::LaunchScreenSaver, + keysyms::XF86_WWW => NamedKey::LaunchWebBrowser, + keysyms::XF86_Sleep => NamedKey::Standby, + keysyms::XF86_Favorites => NamedKey::BrowserFavorites, + keysyms::XF86_AudioPause => NamedKey::MediaPause, + // keysyms::XF86_AudioMedia => NamedKey::AudioMedia, + keysyms::XF86_MyComputer => NamedKey::LaunchApplication1, + // keysyms::XF86_VendorHome => NamedKey::VendorHome, + // keysyms::XF86_LightBulb => NamedKey::LightBulb, + // keysyms::XF86_Shop => NamedKey::BrowserShop, + // keysyms::XF86_History => NamedKey::BrowserHistory, + // keysyms::XF86_OpenURL => NamedKey::OpenUrl, + // keysyms::XF86_AddFavorite => NamedKey::AddFavorite, + // keysyms::XF86_HotLinks => NamedKey::HotLinks, + // keysyms::XF86_BrightnessAdjust => NamedKey::BrightnessAdjust, + // keysyms::XF86_Finance => NamedKey::BrowserFinance, + // keysyms::XF86_Community => NamedKey::BrowserCommunity, + keysyms::XF86_AudioRewind => NamedKey::MediaRewind, + // keysyms::XF86_BackForward => Key::???, + // XF86_Launch0..XF86_LaunchF + + // XF86_ApplicationLeft..XF86_CD + keysyms::XF86_Calculater => NamedKey::LaunchApplication2, // Nice typo, libxkbcommon :) + // XF86_Clear + keysyms::XF86_Close => NamedKey::Close, + keysyms::XF86_Copy => NamedKey::Copy, + keysyms::XF86_Cut => NamedKey::Cut, + // XF86_Display..XF86_Documents + keysyms::XF86_Excel => NamedKey::LaunchSpreadsheet, + // XF86_Explorer..XF86iTouch + keysyms::XF86_LogOff => NamedKey::LogOff, + // XF86_Market..XF86_MenuPB + keysyms::XF86_MySites => NamedKey::BrowserFavorites, + keysyms::XF86_New => NamedKey::New, + // XF86_News..XF86_OfficeHome + keysyms::XF86_Open => NamedKey::Open, + // XF86_Option + keysyms::XF86_Paste => NamedKey::Paste, + keysyms::XF86_Phone => NamedKey::LaunchPhone, + // XF86_Q + keysyms::XF86_Reply => NamedKey::MailReply, + keysyms::XF86_Reload => NamedKey::BrowserRefresh, + // XF86_RotateWindows..XF86_RotationKB + keysyms::XF86_Save => NamedKey::Save, + // XF86_ScrollUp..XF86_ScrollClick + keysyms::XF86_Send => NamedKey::MailSend, + keysyms::XF86_Spell => NamedKey::SpellCheck, + keysyms::XF86_SplitScreen => NamedKey::SplitScreenToggle, + // XF86_Support..XF86_User2KB + keysyms::XF86_Video => NamedKey::LaunchMediaPlayer, + // XF86_WheelButton + keysyms::XF86_Word => NamedKey::LaunchWordProcessor, + // XF86_Xfer + keysyms::XF86_ZoomIn => NamedKey::ZoomIn, + keysyms::XF86_ZoomOut => NamedKey::ZoomOut, + + // XF86_Away..XF86_Messenger + keysyms::XF86_WebCam => NamedKey::LaunchWebCam, + keysyms::XF86_MailForward => NamedKey::MailForward, + // XF86_Pictures + keysyms::XF86_Music => NamedKey::LaunchMusicPlayer, + + // XF86_Battery..XF86_UWB + keysyms::XF86_AudioForward => NamedKey::MediaFastForward, + // XF86_AudioRepeat + keysyms::XF86_AudioRandomPlay => NamedKey::RandomToggle, + keysyms::XF86_Subtitle => NamedKey::Subtitle, + keysyms::XF86_AudioCycleTrack => NamedKey::MediaAudioTrack, + // XF86_CycleAngle..XF86_Blue + keysyms::XF86_Suspend => NamedKey::Standby, + keysyms::XF86_Hibernate => NamedKey::Hibernate, + // XF86_TouchpadToggle..XF86_TouchpadOff + keysyms::XF86_AudioMute => NamedKey::AudioVolumeMute, + + // XF86_Switch_VT_1..XF86_Switch_VT_12 + + // XF86_Ungrab..XF86_ClearGrab + keysyms::XF86_Next_VMode => NamedKey::VideoModeNext, + // keysyms::XF86_Prev_VMode => NamedKey::VideoModePrevious, + // XF86_LogWindowTree..XF86_LogGrabInfo + + // SunFA_Grave..SunFA_Cedilla + + // keysyms::SunF36 => NamedKey::F36 | NamedKey::F11, + // keysyms::SunF37 => NamedKey::F37 | NamedKey::F12, + + // keysyms::SunSys_Req => NamedKey::PrintScreen, + // The next couple of xkb (until SunStop) are already handled. + // SunPrint_Screen..SunPageDown + + // SunUndo..SunFront + keysyms::SUN_Copy => NamedKey::Copy, + keysyms::SUN_Open => NamedKey::Open, + keysyms::SUN_Paste => NamedKey::Paste, + keysyms::SUN_Cut => NamedKey::Cut, + + // SunPowerSwitch + keysyms::SUN_AudioLowerVolume => NamedKey::AudioVolumeDown, + keysyms::SUN_AudioMute => NamedKey::AudioVolumeMute, + keysyms::SUN_AudioRaiseVolume => NamedKey::AudioVolumeUp, + // SUN_VideoDegauss + keysyms::SUN_VideoLowerBrightness => NamedKey::BrightnessDown, + keysyms::SUN_VideoRaiseBrightness => NamedKey::BrightnessUp, + // SunPowerSwitchShift + 0 => return Key::Unidentified(NativeKey::Unidentified), + _ => return Key::Unidentified(NativeKey::Xkb(keysym)), + }) +} + +pub fn keysym_location(keysym: u32) -> KeyLocation { + use xkbcommon_dl::keysyms; + match keysym { + keysyms::Shift_L + | keysyms::Control_L + | keysyms::Meta_L + | keysyms::Alt_L + | keysyms::Super_L + | keysyms::Hyper_L => KeyLocation::Left, + keysyms::Shift_R + | keysyms::Control_R + | keysyms::Meta_R + | keysyms::Alt_R + | keysyms::Super_R + | keysyms::Hyper_R => KeyLocation::Right, + keysyms::KP_0 + | keysyms::KP_1 + | keysyms::KP_2 + | keysyms::KP_3 + | keysyms::KP_4 + | keysyms::KP_5 + | keysyms::KP_6 + | keysyms::KP_7 + | keysyms::KP_8 + | keysyms::KP_9 + | keysyms::KP_Space + | keysyms::KP_Tab + | keysyms::KP_Enter + | keysyms::KP_F1 + | keysyms::KP_F2 + | keysyms::KP_F3 + | keysyms::KP_F4 + | keysyms::KP_Home + | keysyms::KP_Left + | keysyms::KP_Up + | keysyms::KP_Right + | keysyms::KP_Down + | keysyms::KP_Page_Up + | keysyms::KP_Page_Down + | keysyms::KP_End + | keysyms::KP_Begin + | keysyms::KP_Insert + | keysyms::KP_Delete + | keysyms::KP_Equal + | keysyms::KP_Multiply + | keysyms::KP_Add + | keysyms::KP_Separator + | keysyms::KP_Subtract + | keysyms::KP_Decimal + | keysyms::KP_Divide => KeyLocation::Numpad, + _ => KeyLocation::Standard, + } +} diff --git a/sessionlockev/src/lib.rs b/sessionlockev/src/lib.rs index 692f6e9..af699bd 100644 --- a/sessionlockev/src/lib.rs +++ b/sessionlockev/src/lib.rs @@ -99,13 +99,15 @@ mod events; -mod strtoshape; +pub mod keyboard; +pub mod xkb_keyboard; + +mod keymap; -pub mod key; +mod strtoshape; pub mod id; -use key::KeyModifierType; use strtoshape::str_to_shape; use std::fmt::Debug; @@ -121,7 +123,7 @@ use wayland_client::{ wl_buffer::WlBuffer, wl_compositor::WlCompositor, wl_display::WlDisplay, - wl_keyboard::{self, WlKeyboard}, + wl_keyboard::{self, KeyState, KeymapFormat, WlKeyboard}, wl_output::{self, WlOutput}, wl_pointer::{self, WlPointer}, wl_registry, @@ -386,12 +388,11 @@ pub struct WindowState { // base managers seat: Option, - keyboard: Option, + keyboard_state: Option, pointer: Option, touch: Option, // keyboard - modifier: KeyModifierType, use_display_handle: bool, } @@ -403,7 +404,7 @@ impl WindowState { /// get the keyboard pub fn get_keyboard(&self) -> Option<&WlKeyboard> { - self.keyboard.as_ref() + Some(&self.keyboard_state.as_ref()?.keyboard) } /// get the pointer @@ -461,11 +462,10 @@ impl Default for WindowState { globals: None, seat: None, - keyboard: None, + keyboard_state: None, pointer: None, touch: None, - modifier: KeyModifierType::NoMod, use_display_handle: false, } } @@ -546,12 +546,13 @@ impl Dispatch for WindowState { _conn: &Connection, qh: &wayland_client::QueueHandle, ) { + use xkb_keyboard::KeyboardState; if let wl_seat::Event::Capabilities { capabilities: WEnum::Value(capabilities), } = event { if capabilities.contains(wl_seat::Capability::Keyboard) { - state.keyboard = Some(seat.get_keyboard(qh, ())); + state.keyboard_state = Some(KeyboardState::new(seat.get_keyboard(qh, ()))); } if capabilities.contains(wl_seat::Capability::Pointer) { state.pointer = Some(seat.get_pointer(qh, ())); @@ -570,33 +571,69 @@ impl Dispatch for WindowState { event: ::Event, _data: &(), _conn: &Connection, - _qhandle: &wayland_client::QueueHandle, + _qhandle: &QueueHandle, ) { + use keyboard::*; + use xkb_keyboard::ElementState; + let keyboard_state = state.keyboard_state.as_mut().unwrap(); match event { + wl_keyboard::Event::Keymap { format, fd, size } => match format { + WEnum::Value(KeymapFormat::XkbV1) => { + let context = &mut keyboard_state.xkb_context; + context.set_keymap_from_fd(fd, size as usize) + } + WEnum::Value(KeymapFormat::NoKeymap) => { + log::warn!("non-xkb compatible keymap") + } + _ => unreachable!(), + }, + wl_keyboard::Event::Leave { .. } => { + state.message.push(( + state.surface_pos(), + DispatchMessageInner::ModifiersChanged(ModifiersState::empty()), + )); + } wl_keyboard::Event::Key { state: keystate, - serial, key, - time, + .. } => { - state.message.push(( - state.surface_pos(), - DispatchMessageInner::KeyBoard { - state: keystate, - modifier: state.modifier, - serial, - key, - time, - }, - )); + let pressed_state = match keystate { + WEnum::Value(KeyState::Pressed) => ElementState::Pressed, + WEnum::Value(KeyState::Released) => ElementState::Released, + _ => { + return; + } + }; + let key = key + 8; + if let Some(mut key_context) = keyboard_state.xkb_context.key_context() { + let event = key_context.process_key_event(key, pressed_state, false); + let event = DispatchMessageInner::KeyboardInput { + event, + is_synthetic: false, + }; + state.message.push((state.surface_pos(), event)); + } } wl_keyboard::Event::Modifiers { mods_depressed, mods_locked, + mods_latched, + group, .. } => { - state.modifier = KeyModifierType::from_bits(mods_depressed | mods_locked) - .unwrap_or(KeyModifierType::empty()); + let xkb_context = &mut keyboard_state.xkb_context; + let xkb_state = match xkb_context.state_mut() { + Some(state) => state, + None => return, + }; + xkb_state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group); + let modifiers = xkb_state.modifiers(); + + state.message.push(( + state.surface_pos(), + DispatchMessageInner::ModifiersChanged(modifiers.into()), + )) } _ => {} } diff --git a/sessionlockev/src/xkb_keyboard.rs b/sessionlockev/src/xkb_keyboard.rs new file mode 100644 index 0000000..4597e80 --- /dev/null +++ b/sessionlockev/src/xkb_keyboard.rs @@ -0,0 +1,842 @@ +use memmap2::MmapOptions; +use smol_str::SmolStr; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::LazyLock; +use std::{ + env, + ffi::{c_char, CString}, + ops::Deref, + os::{fd::OwnedFd, unix::ffi::OsStringExt}, + ptr::{self, NonNull}, + time::Duration, +}; +use wayland_client::{protocol::wl_keyboard::WlKeyboard, Proxy}; + +use crate::keymap; + +use xkbcommon_dl::{ + self as xkb, xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, + xkb_compose_state_flags, xkb_compose_status, xkb_compose_table, xkb_keycode_t, xkb_keysym_t, + xkb_layout_index_t, xkbcommon_compose_handle, xkbcommon_handle, XkbCommon, XkbCommonCompose, +}; + +use crate::keyboard::ModifiersState; +use xkb::{ + xkb_context, xkb_context_flags, xkb_keymap, xkb_keymap_compile_flags, xkb_state, + xkb_state_component, +}; + +use crate::keyboard::{Key, KeyLocation, PhysicalKey}; + + + +static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); + +pub static XKBH: LazyLock<&'static XkbCommon> = LazyLock::new(xkbcommon_handle); +pub static XKBCH: LazyLock<&'static XkbCommonCompose> = LazyLock::new(xkbcommon_compose_handle); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RepeatInfo { + /// Keys will be repeated at the specified rate and delay. + Repeat { + /// The time between the key repeats. + gap: Duration, + + /// Delay (in milliseconds) between a key press and the start of repetition. + delay: Duration, + }, + + /// Keys should not be repeated. + Disable, +} + +impl Default for RepeatInfo { + /// The default repeat rate is 25 keys per second with the delay of 200ms. + /// + /// The values are picked based on the default in various compositors and Xorg. + fn default() -> Self { + Self::Repeat { + gap: Duration::from_millis(40), + delay: Duration::from_millis(200), + } + } +} + +#[derive(Debug)] +pub struct KeyboardState { + pub keyboard: WlKeyboard, + + pub xkb_context: Context, + pub repeat_info: RepeatInfo, + pub current_repeat: Option, +} + +impl KeyboardState { + pub fn new(keyboard: WlKeyboard) -> Self { + Self { + keyboard, + xkb_context: Context::new().unwrap(), + repeat_info: RepeatInfo::default(), + current_repeat: None, + } + } +} + +impl Drop for KeyboardState { + fn drop(&mut self) { + if self.keyboard.version() >= 3 { + self.keyboard.release(); + } + } +} + +#[derive(Debug)] +pub enum Error { + /// libxkbcommon is not available + XKBNotFound, +} + +#[derive(Debug)] +pub struct Context { + // NOTE: field order matters. + state: Option, + keymap: Option, + compose_state1: Option, + compose_state2: Option, + _compose_table: Option, + context: XkbContext, + scratch_buffer: Vec, +} + +impl Context { + pub fn new() -> Result { + if xkb::xkbcommon_option().is_none() { + return Err(Error::XKBNotFound); + } + + let context = XkbContext::new(); + let mut compose_table = XkbComposeTable::new(&context); + let mut compose_state1 = compose_table.as_ref().and_then(|table| table.new_state()); + let mut compose_state2 = compose_table.as_ref().and_then(|table| table.new_state()); + + // Disable compose if anything compose related failed to initialize. + if compose_table.is_none() || compose_state1.is_none() || compose_state2.is_none() { + compose_state2 = None; + compose_state1 = None; + compose_table = None; + } + + Ok(Self { + state: None, + keymap: None, + compose_state1, + compose_state2, + _compose_table: compose_table, + context, + scratch_buffer: Vec::with_capacity(8), + }) + } + pub fn set_keymap_from_fd(&mut self, fd: OwnedFd, size: usize) { + let keymap = XkbKeymap::from_fd(&self.context, fd, size); + let state = keymap.as_ref().and_then(XkbState::new_wayland); + if keymap.is_none() || state.is_none() { + log::warn!("failed to update xkb keymap"); + } + self.state = state; + self.keymap = keymap; + } + + pub fn state_mut(&mut self) -> Option<&mut XkbState> { + self.state.as_mut() + } + /// Key builder context with the user provided xkb state. + pub fn key_context(&mut self) -> Option> { + let state = self.state.as_mut()?; + let keymap = self.keymap.as_mut()?; + let compose_state1 = self.compose_state1.as_mut(); + let compose_state2 = self.compose_state2.as_mut(); + let scratch_buffer = &mut self.scratch_buffer; + Some(KeyContext { + state, + keymap, + compose_state1, + compose_state2, + scratch_buffer, + }) + } +} + +#[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 }) + } + + pub fn first_keysym_by_level( + &mut self, + layout: xkb_layout_index_t, + keycode: xkb_keycode_t, + ) -> xkb_keysym_t { + unsafe { + let mut keysyms = ptr::null(); + let count = (XKBH.xkb_keymap_key_get_syms_by_level)( + self.keymap.as_ptr(), + keycode, + layout, + // NOTE: The level should be zero to ignore modifiers. + 0, + &mut keysyms, + ); + + if count == 1 { + *keysyms + } else { + 0 + } + } + } +} + +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: ModifiersStateXkb, +} + +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 = ModifiersStateXkb::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 + } + } + pub fn modifiers(&self) -> ModifiersStateXkb { + self.modifiers + } + pub fn update_modifiers( + &mut self, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + depressed_group: u32, + latched_group: u32, + locked_group: u32, + ) { + let mask = unsafe { + (XKBH.xkb_state_update_mask)( + self.state.as_ptr(), + mods_depressed, + mods_latched, + mods_locked, + depressed_group, + latched_group, + locked_group, + ) + }; + + if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) { + // Effective value of mods have changed, we need to update our state. + self.reload_modifiers(); + } + } + + 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); + 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); + } + + pub fn get_one_sym_raw(&mut self, keycode: xkb_keycode_t) -> xkb_keysym_t { + unsafe { (XKBH.xkb_state_key_get_one_sym)(self.state.as_ptr(), keycode) } + } + + pub fn layout(&mut self, key: xkb_keycode_t) -> xkb_layout_index_t { + unsafe { (XKBH.xkb_state_key_get_layout)(self.state.as_ptr(), key) } + } + + pub fn get_utf8_raw( + &mut self, + keycode: xkb_keycode_t, + scratch_buffer: &mut Vec, + ) -> Option { + make_string_with(scratch_buffer, |ptr, len| unsafe { + (XKBH.xkb_state_key_get_utf8)(self.state.as_ptr(), keycode, ptr, len) + }) + } +} + +#[derive(Debug, Default, Clone, Copy)] +pub struct ModifiersStateXkb { + ctrl: bool, + alt: bool, + shift: bool, + caps_lock: bool, + logo: bool, + num_lock: bool, +} + +impl From for ModifiersState { + fn from(mods: ModifiersStateXkb) -> ModifiersState { + let mut to_mods = ModifiersState::empty(); + to_mods.set(ModifiersState::SHIFT, mods.shift); + to_mods.set(ModifiersState::CONTROL, mods.ctrl); + to_mods.set(ModifiersState::ALT, mods.alt); + to_mods.set(ModifiersState::SUPER, mods.logo); + to_mods + } +} + +#[derive(Debug)] +pub struct XkbComposeTable { + table: NonNull, +} + +impl XkbComposeTable { + pub fn new(context: &XkbContext) -> Option { + 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 table = unsafe { + (XKBCH.xkb_compose_table_new_from_locale)( + context.as_ptr(), + locale.as_ptr(), + xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS, + ) + }; + + let table = NonNull::new(table)?; + Some(Self { table }) + } + + /// Create new state with the given compose table. + pub fn new_state(&self) -> Option { + let state = unsafe { + (XKBCH.xkb_compose_state_new)( + self.table.as_ptr(), + xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, + ) + }; + + let state = NonNull::new(state)?; + Some(XkbComposeState { state }) + } +} + +impl Deref for XkbComposeTable { + type Target = NonNull; + + fn deref(&self) -> &Self::Target { + &self.table + } +} + +impl Drop for XkbComposeTable { + fn drop(&mut self) { + unsafe { + (XKBCH.xkb_compose_table_unref)(self.table.as_ptr()); + } + } +} + +#[derive(Debug)] +pub struct XkbComposeState { + state: NonNull, +} + +// NOTE: This is track_caller so we can have more informative line numbers when logging +#[track_caller] +fn byte_slice_to_smol_str(bytes: &[u8]) -> Option { + std::str::from_utf8(bytes) + .map(SmolStr::new) + .map_err(|e| { + log::warn!( + "UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", + bytes + ) + }) + .ok() +} + +/// Shared logic for constructing a string with `xkb_compose_state_get_utf8` and +/// `xkb_state_key_get_utf8`. +fn make_string_with(scratch_buffer: &mut Vec, mut f: F) -> Option +where + F: FnMut(*mut c_char, usize) -> i32, +{ + let size = f(ptr::null_mut(), 0); + if size == 0 { + return None; + } + let size = usize::try_from(size).unwrap(); + scratch_buffer.clear(); + // The allocated buffer must include space for the null-terminator. + scratch_buffer.reserve(size + 1); + unsafe { + let written = f( + scratch_buffer.as_mut_ptr().cast(), + scratch_buffer.capacity(), + ); + if usize::try_from(written).unwrap() != size { + // This will likely never happen. + return None; + } + scratch_buffer.set_len(size); + }; + + byte_slice_to_smol_str(scratch_buffer) +} + +impl XkbComposeState { + pub fn get_string(&mut self, scratch_buffer: &mut Vec) -> Option { + make_string_with(scratch_buffer, |ptr, len| unsafe { + (XKBCH.xkb_compose_state_get_utf8)(self.state.as_ptr(), ptr, len) + }) + } + + #[inline] + pub fn feed(&mut self, keysym: xkb_keysym_t) -> ComposeStatus { + let feed_result = unsafe { (XKBCH.xkb_compose_state_feed)(self.state.as_ptr(), keysym) }; + match feed_result { + xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED => ComposeStatus::Ignored, + xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED => { + ComposeStatus::Accepted(self.status()) + } + } + } + + #[inline] + pub fn reset(&mut self) { + unsafe { + (XKBCH.xkb_compose_state_reset)(self.state.as_ptr()); + } + } + + #[inline] + pub fn status(&mut self) -> xkb_compose_status { + unsafe { (XKBCH.xkb_compose_state_get_status)(self.state.as_ptr()) } + } +} + +impl Drop for XkbComposeState { + fn drop(&mut self) { + unsafe { + (XKBCH.xkb_compose_state_unref)(self.state.as_ptr()); + }; + } +} + +#[derive(Copy, Clone, Debug)] +pub enum ComposeStatus { + Accepted(xkb_compose_status), + Ignored, + None, +} + +pub struct KeyContext<'a> { + pub state: &'a mut XkbState, + pub keymap: &'a mut XkbKeymap, + compose_state1: Option<&'a mut XkbComposeState>, + compose_state2: Option<&'a mut XkbComposeState>, + scratch_buffer: &'a mut Vec, +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum ElementState { + Pressed, + Released, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEventExtra { + pub text_with_all_modifiers: Option, + pub key_without_modifiers: Key, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEvent { + /// Represents the position of a key independent of the currently active layout. + /// + /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). + /// The most prevalent use case for this is games. For example the default keys for the player + /// to move around might be the W, A, S, and D keys on a US layout. The position of these keys + /// is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY" + /// layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.) + /// + /// ## Caveats + /// + /// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that + /// implements DVORAK in hardware (or firmware) + /// - Your application will likely have to handle keyboards which are missing keys that your + /// own keyboard has. + /// - Certain `KeyCode`s will move between a couple of different positions depending on what + /// layout the keyboard was manufactured to support. + /// + /// **Because of these caveats, it is important that you provide users with a way to configure + /// most (if not all) keybinds in your application.** + /// + /// ## `Fn` and `FnLock` + /// + /// `Fn` and `FnLock` key events are *exceedingly unlikely* to be emitted by Winit. These keys + /// are usually handled at the hardware or OS level, and aren't surfaced to applications. If + /// you somehow see this in the wild, we'd like to know :) + pub physical_key: PhysicalKey, + + /// This value is affected by all modifiers except Ctrl. + /// + /// This has two use cases: + /// - Allows querying whether the current input is a Dead key. + /// - Allows handling key-bindings on platforms which don't + /// support [`key_without_modifiers`]. + /// + /// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard + /// shortcuts, **it is important that you provide users with a way to configure your + /// application's shortcuts so you don't render your application unusable for users with an + /// incompatible keyboard layout.** + /// + /// ## Platform-specific + /// - **Web:** Dead keys might be reported as the real key instead + /// of `Dead` depending on the browser/OS. + /// + /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers + pub logical_key: Key, + + /// Contains the text produced by this keypress. + /// + /// In most cases this is identical to the content + /// of the `Character` variant of `logical_key`. + /// However, on Windows when a dead key was pressed earlier + /// but cannot be combined with the character from this + /// keypress, the produced text will consist of two characters: + /// the dead-key-character followed by the character resulting + /// from this keypress. + /// + /// An additional difference from `logical_key` is that + /// this field stores the text representation of any key + /// that has such a representation. For example when + /// `logical_key` is `Key::Named(NamedKey::Enter)`, this field is `Some("\r")`. + /// + /// This is `None` if the current keypress cannot + /// be interpreted as text. + /// + /// See also: `text_with_all_modifiers()` + pub text: Option, + + /// Contains the location of this key on the keyboard. + /// + /// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" + /// key appears on the left side of the QWERTY keyboard as well as the right side. However, + /// both keys have the same symbolic value. Another example of this phenomenon is the "1" + /// key, which appears both above the "Q" key and as the "Keypad 1" key. + /// + /// This field allows the user to differentiate between keys like this that have the same + /// symbolic value but different locations on the keyboard. + /// + /// See the [`KeyLocation`] type for more details. + /// + /// [`KeyLocation`]: crate::keyboard::KeyLocation + pub location: KeyLocation, + + /// Whether the key is being pressed or released. + /// + /// See the [`ElementState`] type for more details. + pub state: ElementState, + + /// Whether or not this key is a key repeat event. + /// + /// On some systems, holding down a key for some period of time causes that key to be repeated + /// as though it were being pressed and released repeatedly. This field is `true` if and only + /// if this event is the result of one of those repeats. + /// + pub repeat: bool, + + /// Platform-specific key event information. + /// + /// On Windows, Linux and macOS, this type contains the key without modifiers and the text with + /// all modifiers applied. + /// + /// On Android, iOS, Redox and Web, this type is a no-op. + pub(crate) platform_specific: KeyEventExtra, +} + +impl KeyEvent { + #[inline] + pub fn text_with_all_modifiers(&self) -> Option<&str> { + self.platform_specific + .text_with_all_modifiers + .as_ref() + .map(|s| s.as_str()) + } + + #[inline] + pub fn key_without_modifiers(&self) -> Key { + self.platform_specific.key_without_modifiers.clone() + } +} + +impl<'a> KeyContext<'a> { + pub fn process_key_event( + &mut self, + keycode: u32, + state: ElementState, + repeat: bool, + ) -> KeyEvent { + let mut event = + KeyEventResults::new(self, keycode, !repeat && state == ElementState::Pressed); + let physical_key = keymap::raw_keycode_to_physicalkey(keycode); + let (logical_key, location) = event.key(); + let text = event.text(); + let (key_without_modifiers, _) = event.key_without_modifiers(); + let text_with_all_modifiers = event.text_with_all_modifiers(); + + let platform_specific = KeyEventExtra { + text_with_all_modifiers, + key_without_modifiers, + }; + + KeyEvent { + physical_key, + logical_key, + text, + location, + state, + repeat, + platform_specific, + } + } + + fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option { + self.scratch_buffer.clear(); + self.scratch_buffer.reserve(8); + loop { + let bytes_written = unsafe { + (XKBH.xkb_keysym_to_utf8)( + keysym, + self.scratch_buffer.as_mut_ptr().cast(), + self.scratch_buffer.capacity(), + ) + }; + if bytes_written == 0 { + return None; + } else if bytes_written == -1 { + self.scratch_buffer.reserve(8); + } else { + unsafe { + self.scratch_buffer + .set_len(bytes_written.try_into().unwrap()) + }; + break; + } + } + + // Remove the null-terminator + self.scratch_buffer.pop(); + byte_slice_to_smol_str(self.scratch_buffer) + } +} + +struct KeyEventResults<'a, 'b> { + context: &'a mut KeyContext<'b>, + keycode: u32, + keysym: u32, + compose: ComposeStatus, +} + +impl<'a, 'b> KeyEventResults<'a, 'b> { + fn new(context: &'a mut KeyContext<'b>, keycode: u32, compose: bool) -> Self { + let keysym = context.state.get_one_sym_raw(keycode); + + let compose = if let Some(state) = context.compose_state1.as_mut().filter(|_| compose) { + if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) { + state.reset(); + context.compose_state2.as_mut().unwrap().reset(); + } + state.feed(keysym) + } else { + ComposeStatus::None + }; + + KeyEventResults { + context, + keycode, + keysym, + compose, + } + } + + pub fn key(&mut self) -> (Key, KeyLocation) { + let (key, location) = match self.keysym_to_key(self.keysym) { + Ok(known) => return known, + Err(undefined) => undefined, + }; + + if let ComposeStatus::Accepted(xkb_compose_status::XKB_COMPOSE_COMPOSING) = self.compose { + let compose_state = self.context.compose_state2.as_mut().unwrap(); + // When pressing a dead key twice, the non-combining variant of that character will + // be produced. Since this function only concerns itself with a single keypress, we + // simulate this double press here by feeding the keysym to the compose state + // twice. + + compose_state.feed(self.keysym); + if matches!(compose_state.feed(self.keysym), ComposeStatus::Accepted(_)) { + // Extracting only a single `char` here *should* be fine, assuming that no + // dead key's non-combining variant ever occupies more than one `char`. + let text = compose_state.get_string(self.context.scratch_buffer); + let key = Key::Dead(text.and_then(|s| s.chars().next())); + (key, location) + } else { + (key, location) + } + } else { + let key = self + .composed_text() + .unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym)) + .map(Key::Character) + .unwrap_or(key); + (key, location) + } + } + + pub fn key_without_modifiers(&mut self) -> (Key, KeyLocation) { + // This will become a pointer to an array which libxkbcommon owns, so we don't need to + // deallocate it. + let layout = self.context.state.layout(self.keycode); + let keysym = self + .context + .keymap + .first_keysym_by_level(layout, self.keycode); + + match self.keysym_to_key(keysym) { + Ok((key, location)) => (key, location), + Err((key, location)) => { + let key = self + .context + .keysym_to_utf8_raw(keysym) + .map(Key::Character) + .unwrap_or(key); + (key, location) + } + } + } + + fn keysym_to_key(&self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> { + let location = keymap::keysym_location(keysym); + let key = keymap::keysym_to_key(keysym); + if matches!(key, Key::Unidentified(_)) { + Err((key, location)) + } else { + Ok((key, location)) + } + } + + pub fn text(&mut self) -> Option { + self.composed_text() + .unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym)) + } + + // The current behaviour makes it so composing a character overrides attempts to input a + // control character with the `Ctrl` key. We can potentially add a configuration option + // if someone specifically wants the oppsite behaviour. + pub fn text_with_all_modifiers(&mut self) -> Option { + match self.composed_text() { + Ok(text) => text, + Err(_) => self + .context + .state + .get_utf8_raw(self.keycode, self.context.scratch_buffer), + } + } + + fn composed_text(&mut self) -> Result, ()> { + match self.compose { + ComposeStatus::Accepted(status) => match status { + xkb_compose_status::XKB_COMPOSE_COMPOSED => { + let state = self.context.compose_state1.as_mut().unwrap(); + Ok(state.get_string(self.context.scratch_buffer)) + } + xkb_compose_status::XKB_COMPOSE_COMPOSING + | xkb_compose_status::XKB_COMPOSE_CANCELLED => Ok(None), + xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()), + }, + _ => Err(()), + } + } +} From 1768d2633a598ae9f79f49ce2f54aed501557cfa Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 22:26:05 +0859 Subject: [PATCH 11/16] fix: unit test --- sessionlockev/examples/simplelock.rs | 12 ++++++------ sessionlockev/src/lib.rs | 14 ++++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/sessionlockev/examples/simplelock.rs b/sessionlockev/examples/simplelock.rs index eccff60..e9ce64f 100644 --- a/sessionlockev/examples/simplelock.rs +++ b/sessionlockev/examples/simplelock.rs @@ -1,11 +1,10 @@ use std::fs::File; use std::os::fd::AsFd; +use sessionlockev::keyboard::{KeyCode, PhysicalKey}; use sessionlockev::reexport::*; use sessionlockev::*; -const ESC_KEY: u32 = 1; - fn main() { let mut ev: WindowState<()> = WindowState::new().build().unwrap(); @@ -61,11 +60,12 @@ fn main() { pointer.clone(), *serial, )), - SessionLockEvent::RequestMessages(DispatchMessage::KeyBoard { key, .. }) => { - if *key == ESC_KEY { - return ReturnData::RequestUnlockAndExist; + SessionLockEvent::RequestMessages(DispatchMessage::KeyboardInput { event, .. }) => { + if let PhysicalKey::Code(KeyCode::Escape) = event.physical_key { + ReturnData::RequestUnlockAndExist + } else { + ReturnData::None } - ReturnData::None } SessionLockEvent::RequestMessages(DispatchMessage::MouseMotion { time, diff --git a/sessionlockev/src/lib.rs b/sessionlockev/src/lib.rs index af699bd..c3887de 100644 --- a/sessionlockev/src/lib.rs +++ b/sessionlockev/src/lib.rs @@ -8,6 +8,7 @@ //! //! use sessionlockev::reexport::*; //! use sessionlockev::*; +//! use sessionlockev::keyboard::{KeyCode, PhysicalKey}; //! //! const ESC_KEY: u32 = 1; //! @@ -59,12 +60,13 @@ //! pointer.clone(), //! *serial, //! )), -//! SessionLockEvent::RequestMessages(DispatchMessage::KeyBoard { key, .. }) => { -//! if *key == ESC_KEY { -//! return ReturnData::RequestUnlockAndExist; -//! } -//! ReturnData::None -//! } +//! SessionLockEvent::RequestMessages(DispatchMessage::KeyboardInput { event, .. }) => { +//! if let PhysicalKey::Code(KeyCode::Escape) = event.physical_key { +//! ReturnData::RequestUnlockAndExist +//! } else { +//! ReturnData::None +//! } +//! } //! SessionLockEvent::RequestMessages(DispatchMessage::MouseMotion { //! time, //! surface_x, From 2a69156ffbe8ee2f4e8406786e9f6d33097e3a6c Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 22:27:14 +0859 Subject: [PATCH 12/16] chore: run fmt --- iced_layershell/src/conversion.rs | 2 +- iced_layershell/src/event.rs | 3 +-- layershellev/src/keyboard.rs | 24 +++++++++++------------- layershellev/src/keymap.rs | 1 - layershellev/src/xkb_keyboard.rs | 2 -- sessionlockev/src/events.rs | 2 +- sessionlockev/src/keyboard.rs | 24 +++++++++++------------- sessionlockev/src/keymap.rs | 1 - sessionlockev/src/xkb_keyboard.rs | 2 -- 9 files changed, 25 insertions(+), 36 deletions(-) diff --git a/iced_layershell/src/conversion.rs b/iced_layershell/src/conversion.rs index 70bf832..d3ccdcc 100644 --- a/iced_layershell/src/conversion.rs +++ b/iced_layershell/src/conversion.rs @@ -6,9 +6,9 @@ use iced_core::SmolStr; use iced_core::{keyboard, mouse, Event as IcedEvent}; use keymap::key; use layershellev::keyboard::KeyLocation; +use layershellev::keyboard::ModifiersState; use layershellev::xkb_keyboard::ElementState; use layershellev::xkb_keyboard::KeyEvent as LayerShellKeyEvent; -use layershellev::keyboard::ModifiersState; #[allow(unused)] pub fn window_event( diff --git a/iced_layershell/src/event.rs b/iced_layershell/src/event.rs index 3a7b7d3..a87586a 100644 --- a/iced_layershell/src/event.rs +++ b/iced_layershell/src/event.rs @@ -1,7 +1,7 @@ use layershellev::id::Id; +use layershellev::keyboard::ModifiersState; use layershellev::reexport::wayland_client::{ButtonState, KeyState, WEnum}; use layershellev::xkb_keyboard::KeyEvent as LayerShellKeyEvent; -use layershellev::keyboard::ModifiersState; use layershellev::{DispatchMessage, WindowWrapper}; use iced_core::keyboard::Modifiers as IcedModifiers; @@ -27,7 +27,6 @@ impl From> for IcedKeyState { } } - #[derive(Debug, Clone)] pub enum WindowEvent { ScaleChanged(u32), diff --git a/layershellev/src/keyboard.rs b/layershellev/src/keyboard.rs index eba59fd..2fe8bd5 100644 --- a/layershellev/src/keyboard.rs +++ b/layershellev/src/keyboard.rs @@ -103,23 +103,23 @@ impl std::fmt::Debug for NativeKeyCode { match self { Unidentified => { debug_tuple = f.debug_tuple("Unidentified"); - }, + } Android(code) => { debug_tuple = f.debug_tuple("Android"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } MacOS(code) => { debug_tuple = f.debug_tuple("MacOS"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Windows(code) => { debug_tuple = f.debug_tuple("Windows"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Xkb(code) => { debug_tuple = f.debug_tuple("Xkb"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } } debug_tuple.finish() } @@ -158,27 +158,27 @@ impl std::fmt::Debug for NativeKey { match self { Unidentified => { debug_tuple = f.debug_tuple("Unidentified"); - }, + } Android(code) => { debug_tuple = f.debug_tuple("Android"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } MacOS(code) => { debug_tuple = f.debug_tuple("MacOS"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Windows(code) => { debug_tuple = f.debug_tuple("Windows"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Xkb(code) => { debug_tuple = f.debug_tuple("Xkb"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Web(code) => { debug_tuple = f.debug_tuple("Web"); debug_tuple.field(code); - }, + } } debug_tuple.finish() } @@ -1726,5 +1726,3 @@ bitflags! { const RSUPER = 0b1000_0000; } } - - diff --git a/layershellev/src/keymap.rs b/layershellev/src/keymap.rs index 59d022d..432f6f8 100644 --- a/layershellev/src/keymap.rs +++ b/layershellev/src/keymap.rs @@ -269,7 +269,6 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { }) } - // NOTE: maybe one day need it #[allow(unused)] pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option { diff --git a/layershellev/src/xkb_keyboard.rs b/layershellev/src/xkb_keyboard.rs index 4597e80..dfe9b6d 100644 --- a/layershellev/src/xkb_keyboard.rs +++ b/layershellev/src/xkb_keyboard.rs @@ -28,8 +28,6 @@ use xkb::{ use crate::keyboard::{Key, KeyLocation, PhysicalKey}; - - static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); pub static XKBH: LazyLock<&'static XkbCommon> = LazyLock::new(xkbcommon_handle); diff --git a/sessionlockev/src/events.rs b/sessionlockev/src/events.rs index d9216ae..702eb01 100644 --- a/sessionlockev/src/events.rs +++ b/sessionlockev/src/events.rs @@ -13,8 +13,8 @@ use crate::id::Id; use crate::xkb_keyboard::KeyEvent; -use crate::keyboard::ModifiersState; use super::WindowState; +use crate::keyboard::ModifiersState; use std::{fmt::Debug, fs::File}; /// tell program what event is happened diff --git a/sessionlockev/src/keyboard.rs b/sessionlockev/src/keyboard.rs index eba59fd..2fe8bd5 100644 --- a/sessionlockev/src/keyboard.rs +++ b/sessionlockev/src/keyboard.rs @@ -103,23 +103,23 @@ impl std::fmt::Debug for NativeKeyCode { match self { Unidentified => { debug_tuple = f.debug_tuple("Unidentified"); - }, + } Android(code) => { debug_tuple = f.debug_tuple("Android"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } MacOS(code) => { debug_tuple = f.debug_tuple("MacOS"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Windows(code) => { debug_tuple = f.debug_tuple("Windows"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Xkb(code) => { debug_tuple = f.debug_tuple("Xkb"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } } debug_tuple.finish() } @@ -158,27 +158,27 @@ impl std::fmt::Debug for NativeKey { match self { Unidentified => { debug_tuple = f.debug_tuple("Unidentified"); - }, + } Android(code) => { debug_tuple = f.debug_tuple("Android"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } MacOS(code) => { debug_tuple = f.debug_tuple("MacOS"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Windows(code) => { debug_tuple = f.debug_tuple("Windows"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Xkb(code) => { debug_tuple = f.debug_tuple("Xkb"); debug_tuple.field(&format_args!("0x{code:04X}")); - }, + } Web(code) => { debug_tuple = f.debug_tuple("Web"); debug_tuple.field(code); - }, + } } debug_tuple.finish() } @@ -1726,5 +1726,3 @@ bitflags! { const RSUPER = 0b1000_0000; } } - - diff --git a/sessionlockev/src/keymap.rs b/sessionlockev/src/keymap.rs index 59d022d..432f6f8 100644 --- a/sessionlockev/src/keymap.rs +++ b/sessionlockev/src/keymap.rs @@ -269,7 +269,6 @@ pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { }) } - // NOTE: maybe one day need it #[allow(unused)] pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option { diff --git a/sessionlockev/src/xkb_keyboard.rs b/sessionlockev/src/xkb_keyboard.rs index 4597e80..dfe9b6d 100644 --- a/sessionlockev/src/xkb_keyboard.rs +++ b/sessionlockev/src/xkb_keyboard.rs @@ -28,8 +28,6 @@ use xkb::{ use crate::keyboard::{Key, KeyLocation, PhysicalKey}; - - static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); pub static XKBH: LazyLock<&'static XkbCommon> = LazyLock::new(xkbcommon_handle); From e51eba85a981de92d0a612e459724adc73e9c241 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 22:42:34 +0859 Subject: [PATCH 13/16] feat: move stolen code to common crate --- .github/workflows/rustci.yml | 1 + Cargo.lock | 21 +- Cargo.toml | 5 +- layershellev/Cargo.toml | 4 +- layershellev/src/lib.rs | 5 +- sessionlockev/Cargo.toml | 4 +- sessionlockev/src/keyboard.rs | 1728 ----------------- sessionlockev/src/keymap.rs | 893 --------- sessionlockev/src/lib.rs | 6 +- sessionlockev/src/xkb_keyboard.rs | 840 -------- waycrate_xkbkeycode/Cargo.toml | 19 + .../src/keyboard.rs | 0 .../src/keymap.rs | 0 waycrate_xkbkeycode/src/lib.rs | 3 + .../src/xkb_keyboard.rs | 0 15 files changed, 48 insertions(+), 3481 deletions(-) delete mode 100644 sessionlockev/src/keyboard.rs delete mode 100644 sessionlockev/src/keymap.rs delete mode 100644 sessionlockev/src/xkb_keyboard.rs create mode 100644 waycrate_xkbkeycode/Cargo.toml rename {layershellev => waycrate_xkbkeycode}/src/keyboard.rs (100%) rename {layershellev => waycrate_xkbkeycode}/src/keymap.rs (100%) create mode 100644 waycrate_xkbkeycode/src/lib.rs rename {layershellev => waycrate_xkbkeycode}/src/xkb_keyboard.rs (100%) diff --git a/.github/workflows/rustci.yml b/.github/workflows/rustci.yml index 0396c86..d8f1fdf 100644 --- a/.github/workflows/rustci.yml +++ b/.github/workflows/rustci.yml @@ -42,6 +42,7 @@ jobs: run: sudo apt install -y libxkbcommon-dev libpango1.0-dev libwayland-dev - name: Publish to crate run: | + cargo publish -p waycrate_xkbkeycode --token ${{ secrets.CRATES_TOKEN }} cargo publish -p layershellev --token ${{ secrets.CRATES_TOKEN }} cargo publish -p sessionlockev --token ${{ secrets.CRATES_TOKEN }} cargo publish -p iced_layershell --token ${{ secrets.CRATES_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index 5ca952e..0064115 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1740,19 +1740,17 @@ version = "0.2.7" dependencies = [ "bitflags 2.6.0", "log", - "memmap2 0.9.4", "raw-window-handle", "smithay-client-toolkit 0.18.1", - "smol_str", "tempfile", "thiserror", + "waycrate_xkbkeycode", "wayland-backend", "wayland-client", "wayland-cursor", "wayland-protocols 0.31.2", "wayland-protocols-misc", "wayland-protocols-wlr 0.2.0", - "xkbcommon-dl", ] [[package]] @@ -2800,19 +2798,17 @@ version = "0.2.7" dependencies = [ "bitflags 2.6.0", "log", - "memmap2 0.9.4", "raw-window-handle", "smithay-client-toolkit 0.18.1", - "smol_str", "tempfile", "thiserror", + "waycrate_xkbkeycode", "wayland-backend", "wayland-client", "wayland-cursor", "wayland-protocols 0.31.2", "wayland-protocols-misc", "wayland-protocols-wlr 0.2.0", - "xkbcommon-dl", ] [[package]] @@ -3574,6 +3570,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "waycrate_xkbkeycode" +version = "0.2.7" +dependencies = [ + "bitflags 2.6.0", + "log", + "memmap2 0.9.4", + "smol_str", + "wayland-backend", + "wayland-client", + "xkbcommon-dl", +] + [[package]] name = "wayland-backend" version = "0.3.6" diff --git a/Cargo.toml b/Cargo.toml index a112d80..ca6ba97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,9 @@ members = [ "iced_sessionlock", "starcolorkeyboard", "sessionlockev", + "waycrate_xkbkeycode", "iced_examples/*", + ] [workspace.package] @@ -27,7 +29,8 @@ layershellev = { version = "0.2.7", path = "./layershellev" } sessionlockev = { version = "0.2.7", path = "./sessionlockev" } iced_layershell = { version = "0.2.7", path = "./iced_layershell" } -iced_sessionlock = { version = "0.2.7", path = "./iced_sessionlock"} +iced_sessionlock = { version = "0.2.7", path = "./iced_sessionlock" } +waycrate_xkbkeycode = { version = "0.2.7", path = "./waycrate_xkbkeycode" } tempfile = "3.8.1" thiserror = "1.0.50" diff --git a/layershellev/Cargo.toml b/layershellev/Cargo.toml index a3704ec..d363736 100644 --- a/layershellev/Cargo.toml +++ b/layershellev/Cargo.toml @@ -34,6 +34,4 @@ sctk.workspace = true log.workspace = true -xkbcommon-dl.workspace = true -smol_str.workspace = true -memmap2.workspace = true +waycrate_xkbkeycode.workspace = true diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index f2a85ab..4f30843 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -111,12 +111,11 @@ //! } //! ``` //! +pub use waycrate_xkbkeycode::keyboard; +pub use waycrate_xkbkeycode::xkb_keyboard; mod events; -pub mod keyboard; -mod keymap; mod strtoshape; -pub mod xkb_keyboard; use std::fmt::Debug; diff --git a/sessionlockev/Cargo.toml b/sessionlockev/Cargo.toml index 5c7f3cd..c5bcbb8 100644 --- a/sessionlockev/Cargo.toml +++ b/sessionlockev/Cargo.toml @@ -33,6 +33,4 @@ rwh_06.workspace = true sctk.workspace = true log.workspace = true -xkbcommon-dl.workspace = true -smol_str.workspace = true -memmap2.workspace = true +waycrate_xkbkeycode.workspace = true diff --git a/sessionlockev/src/keyboard.rs b/sessionlockev/src/keyboard.rs deleted file mode 100644 index 2fe8bd5..0000000 --- a/sessionlockev/src/keyboard.rs +++ /dev/null @@ -1,1728 +0,0 @@ -//! Types related to the keyboard. - -// This file contains a substantial portion of the UI Events Specification by the W3C. In -// particular, the variant names within `Key` and `KeyCode` and their documentation are modified -// versions of contents of the aforementioned specification. -// -// The original documents are: -// -// ### For `Key` -// UI Events KeyboardEvent key Values -// https://www.w3.org/TR/2017/CR-uievents-key-20170601/ -// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). -// -// ### For `KeyCode` -// UI Events KeyboardEvent code Values -// https://www.w3.org/TR/2017/CR-uievents-code-20170601/ -// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang). -// -// These documents were used under the terms of the following license. This W3C license as well as -// the W3C short notice apply to the `Key` and `KeyCode` enums and their variants and the -// documentation attached to their variants. - -// --------- BEGINNING OF W3C LICENSE -------------------------------------------------------------- -// -// License -// -// By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, -// and will comply with the following terms and conditions. -// -// Permission to copy, modify, and distribute this work, with or without modification, for any -// purpose and without fee or royalty is hereby granted, provided that you include the following on -// ALL copies of the work or portions thereof, including modifications: -// -// - The full text of this NOTICE in a location viewable to users of the redistributed or derivative -// work. -// - Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none -// exist, the W3C Software and Document Short Notice should be included. -// - Notice of any changes or modifications, through a copyright statement on the new code or -// document such as "This software or document includes material copied from or derived from -// [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." -// -// Disclaimers -// -// THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR -// ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD -// PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. -// -// COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES -// ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. -// -// The name and trademarks of copyright holders may NOT be used in advertising or publicity -// pertaining to the work without specific, written prior permission. Title to copyright in this -// work will at all times remain with copyright holders. -// -// --------- END OF W3C LICENSE -------------------------------------------------------------------- - -// --------- BEGINNING OF W3C SHORT NOTICE --------------------------------------------------------- -// -// winit: https://github.com/rust-windowing/winit -// -// Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology, European -// Research Consortium for Informatics and Mathematics, Keio University, Beihang). All Rights -// Reserved. This work is distributed under the W3C® Software License [1] in the hope that it will -// be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. -// -// [1] http://www.w3.org/Consortium/Legal/copyright-software -// -// --------- END OF W3C SHORT NOTICE --------------------------------------------------------------- - -use bitflags::bitflags; -pub use smol_str::SmolStr; - -/// Contains the platform-native physical key identifier -/// -/// The exact values vary from platform to platform (which is part of why this is a per-platform -/// enum), but the values are primarily tied to the key's physical location on the keyboard. -/// -/// This enum is primarily used to store raw keycodes when Winit doesn't map a given native -/// physical key identifier to a meaningful [`KeyCode`] variant. In the presence of identifiers we -/// haven't mapped for you yet, this lets you use use [`KeyCode`] to: -/// -/// - Correctly match key press and release events. -/// - On non-Web platforms, support assigning keybinds to virtually any key through a UI. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum NativeKeyCode { - Unidentified, - /// An Android "scancode". - Android(u32), - /// A macOS "scancode". - MacOS(u16), - /// A Windows "scancode". - Windows(u16), - /// An XKB "keycode". - Xkb(u32), -} - -impl std::fmt::Debug for NativeKeyCode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use NativeKeyCode::{Android, MacOS, Unidentified, Windows, Xkb}; - let mut debug_tuple; - match self { - Unidentified => { - debug_tuple = f.debug_tuple("Unidentified"); - } - Android(code) => { - debug_tuple = f.debug_tuple("Android"); - debug_tuple.field(&format_args!("0x{code:04X}")); - } - MacOS(code) => { - debug_tuple = f.debug_tuple("MacOS"); - debug_tuple.field(&format_args!("0x{code:04X}")); - } - Windows(code) => { - debug_tuple = f.debug_tuple("Windows"); - debug_tuple.field(&format_args!("0x{code:04X}")); - } - Xkb(code) => { - debug_tuple = f.debug_tuple("Xkb"); - debug_tuple.field(&format_args!("0x{code:04X}")); - } - } - debug_tuple.finish() - } -} - -/// Contains the platform-native logical key identifier -/// -/// Exactly what that means differs from platform to platform, but the values are to some degree -/// tied to the currently active keyboard layout. The same key on the same keyboard may also report -/// different values on different platforms, which is one of the reasons this is a per-platform -/// enum. -/// -/// This enum is primarily used to store raw keysym when Winit doesn't map a given native logical -/// key identifier to a meaningful [`Key`] variant. This lets you use [`Key`], and let the user -/// define keybinds which work in the presence of identifiers we haven't mapped for you yet. -#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum NativeKey { - Unidentified, - /// An Android "keycode", which is similar to a "virtual-key code" on Windows. - Android(u32), - /// A macOS "scancode". There does not appear to be any direct analogue to either keysyms or - /// "virtual-key" codes in macOS, so we report the scancode instead. - MacOS(u16), - /// A Windows "virtual-key code". - Windows(u16), - /// An XKB "keysym". - Xkb(u32), - /// A "key value string". - Web(SmolStr), -} - -impl std::fmt::Debug for NativeKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use NativeKey::{Android, MacOS, Unidentified, Web, Windows, Xkb}; - let mut debug_tuple; - match self { - Unidentified => { - debug_tuple = f.debug_tuple("Unidentified"); - } - Android(code) => { - debug_tuple = f.debug_tuple("Android"); - debug_tuple.field(&format_args!("0x{code:04X}")); - } - MacOS(code) => { - debug_tuple = f.debug_tuple("MacOS"); - debug_tuple.field(&format_args!("0x{code:04X}")); - } - Windows(code) => { - debug_tuple = f.debug_tuple("Windows"); - debug_tuple.field(&format_args!("0x{code:04X}")); - } - Xkb(code) => { - debug_tuple = f.debug_tuple("Xkb"); - debug_tuple.field(&format_args!("0x{code:04X}")); - } - Web(code) => { - debug_tuple = f.debug_tuple("Web"); - debug_tuple.field(code); - } - } - debug_tuple.finish() - } -} - -impl From for NativeKey { - #[inline] - fn from(code: NativeKeyCode) -> Self { - match code { - NativeKeyCode::Unidentified => NativeKey::Unidentified, - NativeKeyCode::Android(x) => NativeKey::Android(x), - NativeKeyCode::MacOS(x) => NativeKey::MacOS(x), - NativeKeyCode::Windows(x) => NativeKey::Windows(x), - NativeKeyCode::Xkb(x) => NativeKey::Xkb(x), - } - } -} - -impl PartialEq for NativeKeyCode { - #[allow(clippy::cmp_owned)] // uses less code than direct match; target is stack allocated - #[inline] - fn eq(&self, rhs: &NativeKey) -> bool { - NativeKey::from(*self) == *rhs - } -} - -impl PartialEq for NativeKey { - #[inline] - fn eq(&self, rhs: &NativeKeyCode) -> bool { - rhs == self - } -} - -/// Represents the location of a physical key. -/// -/// This type is a superset of [`KeyCode`], including an [`Unidentified`][Self::Unidentified] -/// variant. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum PhysicalKey { - /// A known key code - Code(KeyCode), - /// This variant is used when the key cannot be translated to a [`KeyCode`] - /// - /// The native keycode is provided (if available) so you're able to more reliably match - /// key-press and key-release events by hashing the [`PhysicalKey`]. It is also possible to use - /// this for keybinds for non-standard keys, but such keybinds are tied to a given platform. - Unidentified(NativeKeyCode), -} - -impl From for PhysicalKey { - #[inline] - fn from(code: KeyCode) -> Self { - PhysicalKey::Code(code) - } -} - -impl From for PhysicalKey { - #[inline] - fn from(code: NativeKeyCode) -> Self { - PhysicalKey::Unidentified(code) - } -} - -impl PartialEq for PhysicalKey { - #[inline] - fn eq(&self, rhs: &KeyCode) -> bool { - match self { - PhysicalKey::Code(ref code) => code == rhs, - _ => false, - } - } -} - -impl PartialEq for KeyCode { - #[inline] - fn eq(&self, rhs: &PhysicalKey) -> bool { - rhs == self - } -} - -impl PartialEq for PhysicalKey { - #[inline] - fn eq(&self, rhs: &NativeKeyCode) -> bool { - match self { - PhysicalKey::Unidentified(ref code) => code == rhs, - _ => false, - } - } -} - -impl PartialEq for NativeKeyCode { - #[inline] - fn eq(&self, rhs: &PhysicalKey) -> bool { - rhs == self - } -} - -/// Code representing the location of a physical key -/// -/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few -/// exceptions: -/// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and -/// "SuperRight" here. -/// - The key that the specification calls "Super" is reported as `Unidentified` here. -/// -/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables -#[non_exhaustive] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum KeyCode { - /// ` on a US keyboard. This is also called a backtick or grave. - /// This is the 半角/全角/漢字 - /// (hankaku/zenkaku/kanji) key on Japanese keyboards - Backquote, - /// Used for both the US \\ (on the 101-key layout) and also for the key - /// located between the " and Enter keys on row C of the 102-, - /// 104- and 106-key layouts. - /// Labeled # on a UK (102) keyboard. - Backslash, - /// [ on a US keyboard. - BracketLeft, - /// ] on a US keyboard. - BracketRight, - /// , on a US keyboard. - Comma, - /// 0 on a US keyboard. - Digit0, - /// 1 on a US keyboard. - Digit1, - /// 2 on a US keyboard. - Digit2, - /// 3 on a US keyboard. - Digit3, - /// 4 on a US keyboard. - Digit4, - /// 5 on a US keyboard. - Digit5, - /// 6 on a US keyboard. - Digit6, - /// 7 on a US keyboard. - Digit7, - /// 8 on a US keyboard. - Digit8, - /// 9 on a US keyboard. - Digit9, - /// = on a US keyboard. - Equal, - /// Located between the left Shift and Z keys. - /// Labeled \\ on a UK keyboard. - IntlBackslash, - /// Located between the / and right Shift keys. - /// Labeled \\ (ro) on a Japanese keyboard. - IntlRo, - /// Located between the = and Backspace keys. - /// Labeled ¥ (yen) on a Japanese keyboard. \\ on a - /// Russian keyboard. - IntlYen, - /// a on a US keyboard. - /// Labeled q on an AZERTY (e.g., French) keyboard. - KeyA, - /// b on a US keyboard. - KeyB, - /// c on a US keyboard. - KeyC, - /// d on a US keyboard. - KeyD, - /// e on a US keyboard. - KeyE, - /// f on a US keyboard. - KeyF, - /// g on a US keyboard. - KeyG, - /// h on a US keyboard. - KeyH, - /// i on a US keyboard. - KeyI, - /// j on a US keyboard. - KeyJ, - /// k on a US keyboard. - KeyK, - /// l on a US keyboard. - KeyL, - /// m on a US keyboard. - KeyM, - /// n on a US keyboard. - KeyN, - /// o on a US keyboard. - KeyO, - /// p on a US keyboard. - KeyP, - /// q on a US keyboard. - /// Labeled a on an AZERTY (e.g., French) keyboard. - KeyQ, - /// r on a US keyboard. - KeyR, - /// s on a US keyboard. - KeyS, - /// t on a US keyboard. - KeyT, - /// u on a US keyboard. - KeyU, - /// v on a US keyboard. - KeyV, - /// w on a US keyboard. - /// Labeled z on an AZERTY (e.g., French) keyboard. - KeyW, - /// x on a US keyboard. - KeyX, - /// y on a US keyboard. - /// Labeled z on a QWERTZ (e.g., German) keyboard. - KeyY, - /// z on a US keyboard. - /// Labeled w on an AZERTY (e.g., French) keyboard, and y on a - /// QWERTZ (e.g., German) keyboard. - KeyZ, - /// - on a US keyboard. - Minus, - /// . on a US keyboard. - Period, - /// ' on a US keyboard. - Quote, - /// ; on a US keyboard. - Semicolon, - /// / on a US keyboard. - Slash, - /// Alt, Option, or . - AltLeft, - /// Alt, Option, or . - /// This is labeled AltGr on many keyboard layouts. - AltRight, - /// Backspace or . - /// Labeled Delete on Apple keyboards. - Backspace, - /// CapsLock or - CapsLock, - /// The application context menu key, which is typically found between the right - /// Super key and the right Control key. - ContextMenu, - /// Control or - ControlLeft, - /// Control or - ControlRight, - /// Enter or . Labeled Return on Apple keyboards. - Enter, - /// The Windows, , Command, or other OS symbol key. - SuperLeft, - /// The Windows, , Command, or other OS symbol key. - SuperRight, - /// Shift or - ShiftLeft, - /// Shift or - ShiftRight, - ///   (space) - Space, - /// Tab or - Tab, - /// Japanese: (henkan) - Convert, - /// Japanese: カタカナ/ひらがな/ローマ字 - /// (katakana/hiragana/romaji) - KanaMode, - /// Korean: HangulMode 한/영 (han/yeong) - /// - /// Japanese (Mac keyboard): (kana) - Lang1, - /// Korean: Hanja (hanja) - /// - /// Japanese (Mac keyboard): (eisu) - Lang2, - /// Japanese (word-processing keyboard): Katakana - Lang3, - /// Japanese (word-processing keyboard): Hiragana - Lang4, - /// Japanese (word-processing keyboard): Zenkaku/Hankaku - Lang5, - /// Japanese: 無変換 (muhenkan) - NonConvert, - /// . The forward delete key. - /// Note that on Apple keyboards, the key labelled Delete on the main part of - /// the keyboard is encoded as [`Backspace`]. - /// - /// [`Backspace`]: Self::Backspace - Delete, - /// Page Down, End, or - End, - /// Help. Not present on standard PC keyboards. - Help, - /// Home or - Home, - /// Insert or Ins. Not present on Apple keyboards. - Insert, - /// Page Down, PgDn, or - PageDown, - /// Page Up, PgUp, or - PageUp, - /// - ArrowDown, - /// - ArrowLeft, - /// - ArrowRight, - /// - ArrowUp, - /// On the Mac, this is used for the numpad Clear key. - NumLock, - /// 0 Ins on a keyboard. 0 on a phone or remote control - Numpad0, - /// 1 End on a keyboard. 1 or 1 QZ on a phone or remote - /// control - Numpad1, - /// 2 ↓ on a keyboard. 2 ABC on a phone or remote control - Numpad2, - /// 3 PgDn on a keyboard. 3 DEF on a phone or remote control - Numpad3, - /// 4 ← on a keyboard. 4 GHI on a phone or remote control - Numpad4, - /// 5 on a keyboard. 5 JKL on a phone or remote control - Numpad5, - /// 6 → on a keyboard. 6 MNO on a phone or remote control - Numpad6, - /// 7 Home on a keyboard. 7 PQRS or 7 PRS on a phone - /// or remote control - Numpad7, - /// 8 ↑ on a keyboard. 8 TUV on a phone or remote control - Numpad8, - /// 9 PgUp on a keyboard. 9 WXYZ or 9 WXY on a phone - /// or remote control - Numpad9, - /// + - NumpadAdd, - /// Found on the Microsoft Natural Keyboard. - NumpadBackspace, - /// C or A (All Clear). Also for use with numpads that have a - /// Clear key that is separate from the NumLock key. On the Mac, the - /// numpad Clear key is encoded as [`NumLock`]. - /// - /// [`NumLock`]: Self::NumLock - NumpadClear, - /// C (Clear Entry) - NumpadClearEntry, - /// , (thousands separator). For locales where the thousands separator - /// is a "." (e.g., Brazil), this key may generate a .. - NumpadComma, - /// . Del. For locales where the decimal separator is "," (e.g., - /// Brazil), this key may generate a ,. - NumpadDecimal, - /// / - NumpadDivide, - NumpadEnter, - /// = - NumpadEqual, - /// # on a phone or remote control device. This key is typically found - /// below the 9 key and to the right of the 0 key. - NumpadHash, - /// M Add current entry to the value stored in memory. - NumpadMemoryAdd, - /// M Clear the value stored in memory. - NumpadMemoryClear, - /// M Replace the current entry with the value stored in memory. - NumpadMemoryRecall, - /// M Replace the value stored in memory with the current entry. - NumpadMemoryStore, - /// M Subtract current entry from the value stored in memory. - NumpadMemorySubtract, - /// * on a keyboard. For use with numpads that provide mathematical - /// operations (+, - * and /). - /// - /// Use `NumpadStar` for the * key on phones and remote controls. - NumpadMultiply, - /// ( Found on the Microsoft Natural Keyboard. - NumpadParenLeft, - /// ) Found on the Microsoft Natural Keyboard. - NumpadParenRight, - /// * on a phone or remote control device. - /// - /// This key is typically found below the 7 key and to the left of - /// the 0 key. - /// - /// Use "NumpadMultiply" for the * key on - /// numeric keypads. - NumpadStar, - /// - - NumpadSubtract, - /// Esc or - Escape, - /// Fn This is typically a hardware key that does not generate a separate code. - Fn, - /// FLock or FnLock. Function Lock key. Found on the Microsoft - /// Natural Keyboard. - FnLock, - /// PrtScr SysRq or Print Screen - PrintScreen, - /// Scroll Lock - ScrollLock, - /// Pause Break - Pause, - /// Some laptops place this key to the left of the key. - /// - /// This also the "back" button (triangle) on Android. - BrowserBack, - BrowserFavorites, - /// Some laptops place this key to the right of the key. - BrowserForward, - /// The "home" button on Android. - BrowserHome, - BrowserRefresh, - BrowserSearch, - BrowserStop, - /// Eject or . This key is placed in the function section on some Apple - /// keyboards. - Eject, - /// Sometimes labelled My Computer on the keyboard - LaunchApp1, - /// Sometimes labelled Calculator on the keyboard - LaunchApp2, - LaunchMail, - MediaPlayPause, - MediaSelect, - MediaStop, - MediaTrackNext, - MediaTrackPrevious, - /// This key is placed in the function section on some Apple keyboards, replacing the - /// Eject key. - Power, - Sleep, - AudioVolumeDown, - AudioVolumeMute, - AudioVolumeUp, - WakeUp, - // Legacy modifier key. Also called "Super" in certain places. - Meta, - // Legacy modifier key. - Hyper, - Turbo, - Abort, - Resume, - Suspend, - /// Found on Sun’s USB keyboard. - Again, - /// Found on Sun’s USB keyboard. - Copy, - /// Found on Sun’s USB keyboard. - Cut, - /// Found on Sun’s USB keyboard. - Find, - /// Found on Sun’s USB keyboard. - Open, - /// Found on Sun’s USB keyboard. - Paste, - /// Found on Sun’s USB keyboard. - Props, - /// Found on Sun’s USB keyboard. - Select, - /// Found on Sun’s USB keyboard. - Undo, - /// Use for dedicated ひらがな key found on some Japanese word processing keyboards. - Hiragana, - /// Use for dedicated カタカナ key found on some Japanese word processing keyboards. - Katakana, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F1, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F2, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F3, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F4, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F5, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F6, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F7, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F8, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F9, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F10, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F11, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F12, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F13, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F14, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F15, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F16, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F17, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F18, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F19, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F20, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F21, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F22, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F23, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F24, - /// General-purpose function key. - F25, - /// General-purpose function key. - F26, - /// General-purpose function key. - F27, - /// General-purpose function key. - F28, - /// General-purpose function key. - F29, - /// General-purpose function key. - F30, - /// General-purpose function key. - F31, - /// General-purpose function key. - F32, - /// General-purpose function key. - F33, - /// General-purpose function key. - F34, - /// General-purpose function key. - F35, -} - -/// A [`Key::Named`] value -/// -/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few -/// exceptions: -/// - The `Super` variant here, is named `Meta` in the aforementioned specification. (There's -/// another key which the specification calls `Super`. That does not exist here.) -/// - The `Space` variant here, can be identified by the character it generates in the -/// specification. -/// -/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ -#[non_exhaustive] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum NamedKey { - /// The `Alt` (Alternative) key. - /// - /// This key enables the alternate modifier function for interpreting concurrent or subsequent - /// keyboard input. This key value is also used for the Apple Option key. - Alt, - /// The Alternate Graphics (AltGr or AltGraph) key. - /// - /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the - /// level 2 modifier). - AltGraph, - /// The `Caps Lock` (Capital) key. - /// - /// Toggle capital character lock function for interpreting subsequent keyboard input event. - CapsLock, - /// The `Control` or `Ctrl` key. - /// - /// Used to enable control modifier function for interpreting concurrent or subsequent keyboard - /// input. - Control, - /// The Function switch `Fn` key. Activating this key simultaneously with another key changes - /// that key’s value to an alternate character or function. This key is often handled directly - /// in the keyboard hardware and does not usually generate key events. - Fn, - /// The Function-Lock (`FnLock` or `F-Lock`) key. Activating this key switches the mode of the - /// keyboard to changes some keys' values to an alternate character or function. This key is - /// often handled directly in the keyboard hardware and does not usually generate key events. - FnLock, - /// The `NumLock` or Number Lock key. Used to toggle numpad mode function for interpreting - /// subsequent keyboard input. - NumLock, - /// Toggle between scrolling and cursor movement modes. - ScrollLock, - /// Used to enable shift modifier function for interpreting concurrent or subsequent keyboard - /// input. - Shift, - /// The Symbol modifier key (used on some virtual keyboards). - Symbol, - SymbolLock, - // Legacy modifier key. Also called "Super" in certain places. - Meta, - // Legacy modifier key. - Hyper, - /// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard - /// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` - /// key. - /// - /// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key. - Super, - /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This - /// key value is also used for the `Return` (Macintosh numpad) key. This key value is also - /// used for the Android `KEYCODE_DPAD_CENTER`. - Enter, - /// The Horizontal Tabulation `Tab` key. - Tab, - /// Used in text to insert a space between words. Usually located below the character keys. - Space, - /// Navigate or traverse downward. (`KEYCODE_DPAD_DOWN`) - ArrowDown, - /// Navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`) - ArrowLeft, - /// Navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`) - ArrowRight, - /// Navigate or traverse upward. (`KEYCODE_DPAD_UP`) - ArrowUp, - /// The End key, used with keyboard entry to go to the end of content (`KEYCODE_MOVE_END`). - End, - /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`). - /// For the mobile phone `Home` key (which goes to the phone’s main screen), use [`GoHome`]. - /// - /// [`GoHome`]: Self::GoHome - Home, - /// Scroll down or display next page of content. - PageDown, - /// Scroll up or display previous page of content. - PageUp, - /// Used to remove the character to the left of the cursor. This key value is also used for - /// the key labeled `Delete` on MacOS keyboards. - Backspace, - /// Remove the currently selected input. - Clear, - /// Copy the current selection. (`APPCOMMAND_COPY`) - Copy, - /// The Cursor Select key. - CrSel, - /// Cut the current selection. (`APPCOMMAND_CUT`) - Cut, - /// Used to delete the character to the right of the cursor. This key value is also used for - /// the key labeled `Delete` on MacOS keyboards when `Fn` is active. - Delete, - /// The Erase to End of Field key. This key deletes all characters from the current cursor - /// position to the end of the current field. - EraseEof, - /// The Extend Selection (Exsel) key. - ExSel, - /// Toggle between text modes for insertion or overtyping. - /// (`KEYCODE_INSERT`) - Insert, - /// The Paste key. (`APPCOMMAND_PASTE`) - Paste, - /// Redo the last action. (`APPCOMMAND_REDO`) - Redo, - /// Undo the last action. (`APPCOMMAND_UNDO`) - Undo, - /// The Accept (Commit, OK) key. Accept current option or input method sequence conversion. - Accept, - /// Redo or repeat an action. - Again, - /// The Attention (Attn) key. - Attn, - Cancel, - /// Show the application’s context menu. - /// This key is commonly found between the right `Super` key and the right `Control` key. - ContextMenu, - /// The `Esc` key. This key was originally used to initiate an escape sequence, but is - /// now more generally used to exit or "escape" the current context, such as closing a dialog - /// or exiting full screen mode. - Escape, - Execute, - /// Open the Find dialog. (`APPCOMMAND_FIND`) - Find, - /// Open a help dialog or toggle display of help information. (`APPCOMMAND_HELP`, - /// `KEYCODE_HELP`) - Help, - /// Pause the current state or application (as appropriate). - /// - /// Note: Do not use this value for the `Pause` button on media controllers. Use `"MediaPause"` - /// instead. - Pause, - /// Play or resume the current state or application (as appropriate). - /// - /// Note: Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"` - /// instead. - Play, - /// The properties (Props) key. - Props, - Select, - /// The ZoomIn key. (`KEYCODE_ZOOM_IN`) - ZoomIn, - /// The ZoomOut key. (`KEYCODE_ZOOM_OUT`) - ZoomOut, - /// The Brightness Down key. Typically controls the display brightness. - /// (`KEYCODE_BRIGHTNESS_DOWN`) - BrightnessDown, - /// The Brightness Up key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_UP`) - BrightnessUp, - /// Toggle removable media to eject (open) and insert (close) state. (`KEYCODE_MEDIA_EJECT`) - Eject, - LogOff, - /// Toggle power state. (`KEYCODE_POWER`) - /// Note: Note: Some devices might not expose this key to the operating environment. - Power, - /// The `PowerOff` key. Sometime called `PowerDown`. - PowerOff, - /// Initiate print-screen function. - PrintScreen, - /// The Hibernate key. This key saves the current state of the computer to disk so that it can - /// be restored. The computer will then shutdown. - Hibernate, - /// The Standby key. This key turns off the display and places the computer into a low-power - /// mode without completely shutting down. It is sometimes labelled `Suspend` or `Sleep` key. - /// (`KEYCODE_SLEEP`) - Standby, - /// The WakeUp key. (`KEYCODE_WAKEUP`) - WakeUp, - /// Initiate the multi-candidate mode. - AllCandidates, - Alphanumeric, - /// Initiate the Code Input mode to allow characters to be entered by - /// their code points. - CodeInput, - /// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a - /// manner similar to a dead key, triggering a mode where subsequent key presses are combined - /// to produce a different character. - Compose, - /// Convert the current input method sequence. - Convert, - /// The Final Mode `Final` key used on some Asian keyboards, to enable the final mode for IMEs. - FinalMode, - /// Switch to the first character group. (ISO/IEC 9995) - GroupFirst, - /// Switch to the last character group. (ISO/IEC 9995) - GroupLast, - /// Switch to the next character group. (ISO/IEC 9995) - GroupNext, - /// Switch to the previous character group. (ISO/IEC 9995) - GroupPrevious, - /// Toggle between or cycle through input modes of IMEs. - ModeChange, - NextCandidate, - /// Accept current input method sequence without - /// conversion in IMEs. - NonConvert, - PreviousCandidate, - Process, - SingleCandidate, - /// Toggle between Hangul and English modes. - HangulMode, - HanjaMode, - JunjaMode, - /// The Eisu key. This key may close the IME, but its purpose is defined by the current IME. - /// (`KEYCODE_EISU`) - Eisu, - /// The (Half-Width) Characters key. - Hankaku, - /// The Hiragana (Japanese Kana characters) key. - Hiragana, - /// The Hiragana/Katakana toggle key. (`KEYCODE_KATAKANA_HIRAGANA`) - HiraganaKatakana, - /// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from - /// romaji mode). - KanaMode, - /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key - /// is typically used to switch to a hiragana keyboard for the purpose of converting input - /// into kanji. (`KEYCODE_KANA`) - KanjiMode, - /// The Katakana (Japanese Kana characters) key. - Katakana, - /// The Roman characters function key. - Romaji, - /// The Zenkaku (Full-Width) Characters key. - Zenkaku, - /// The Zenkaku/Hankaku (full-width/half-width) toggle key. (`KEYCODE_ZENKAKU_HANKAKU`) - ZenkakuHankaku, - /// General purpose virtual function key, as index 1. - Soft1, - /// General purpose virtual function key, as index 2. - Soft2, - /// General purpose virtual function key, as index 3. - Soft3, - /// General purpose virtual function key, as index 4. - Soft4, - /// Select next (numerically or logically) lower channel. (`APPCOMMAND_MEDIA_CHANNEL_DOWN`, - /// `KEYCODE_CHANNEL_DOWN`) - ChannelDown, - /// Select next (numerically or logically) higher channel. (`APPCOMMAND_MEDIA_CHANNEL_UP`, - /// `KEYCODE_CHANNEL_UP`) - ChannelUp, - /// Close the current document or message (Note: This doesn’t close the application). - /// (`APPCOMMAND_CLOSE`) - Close, - /// Open an editor to forward the current message. (`APPCOMMAND_FORWARD_MAIL`) - MailForward, - /// Open an editor to reply to the current message. (`APPCOMMAND_REPLY_TO_MAIL`) - MailReply, - /// Send the current message. (`APPCOMMAND_SEND_MAIL`) - MailSend, - /// Close the current media, for example to close a CD or DVD tray. (`KEYCODE_MEDIA_CLOSE`) - MediaClose, - /// Initiate or continue forward playback at faster than normal speed, or increase speed if - /// already fast forwarding. (`APPCOMMAND_MEDIA_FAST_FORWARD`, `KEYCODE_MEDIA_FAST_FORWARD`) - MediaFastForward, - /// Pause the currently playing media. (`APPCOMMAND_MEDIA_PAUSE`, `KEYCODE_MEDIA_PAUSE`) - /// - /// Note: Media controller devices should use this value rather than `"Pause"` for their pause - /// keys. - MediaPause, - /// Initiate or continue media playback at normal speed, if not currently playing at normal - /// speed. (`APPCOMMAND_MEDIA_PLAY`, `KEYCODE_MEDIA_PLAY`) - MediaPlay, - /// Toggle media between play and pause states. (`APPCOMMAND_MEDIA_PLAY_PAUSE`, - /// `KEYCODE_MEDIA_PLAY_PAUSE`) - MediaPlayPause, - /// Initiate or resume recording of currently selected media. (`APPCOMMAND_MEDIA_RECORD`, - /// `KEYCODE_MEDIA_RECORD`) - MediaRecord, - /// Initiate or continue reverse playback at faster than normal speed, or increase speed if - /// already rewinding. (`APPCOMMAND_MEDIA_REWIND`, `KEYCODE_MEDIA_REWIND`) - MediaRewind, - /// Stop media playing, pausing, forwarding, rewinding, or recording, if not already stopped. - /// (`APPCOMMAND_MEDIA_STOP`, `KEYCODE_MEDIA_STOP`) - MediaStop, - /// Seek to next media or program track. (`APPCOMMAND_MEDIA_NEXTTRACK`, `KEYCODE_MEDIA_NEXT`) - MediaTrackNext, - /// Seek to previous media or program track. (`APPCOMMAND_MEDIA_PREVIOUSTRACK`, - /// `KEYCODE_MEDIA_PREVIOUS`) - MediaTrackPrevious, - /// Open a new document or message. (`APPCOMMAND_NEW`) - New, - /// Open an existing document or message. (`APPCOMMAND_OPEN`) - Open, - /// Print the current document or message. (`APPCOMMAND_PRINT`) - Print, - /// Save the current document or message. (`APPCOMMAND_SAVE`) - Save, - /// Spellcheck the current document or selection. (`APPCOMMAND_SPELL_CHECK`) - SpellCheck, - /// The `11` key found on media numpads that - /// have buttons from `1` ... `12`. - Key11, - /// The `12` key found on media numpads that - /// have buttons from `1` ... `12`. - Key12, - /// Adjust audio balance leftward. (`VK_AUDIO_BALANCE_LEFT`) - AudioBalanceLeft, - /// Adjust audio balance rightward. (`VK_AUDIO_BALANCE_RIGHT`) - AudioBalanceRight, - /// Decrease audio bass boost or cycle down through bass boost states. (`APPCOMMAND_BASS_DOWN`, - /// `VK_BASS_BOOST_DOWN`) - AudioBassBoostDown, - /// Toggle bass boost on/off. (`APPCOMMAND_BASS_BOOST`) - AudioBassBoostToggle, - /// Increase audio bass boost or cycle up through bass boost states. (`APPCOMMAND_BASS_UP`, - /// `VK_BASS_BOOST_UP`) - AudioBassBoostUp, - /// Adjust audio fader towards front. (`VK_FADER_FRONT`) - AudioFaderFront, - /// Adjust audio fader towards rear. (`VK_FADER_REAR`) - AudioFaderRear, - /// Advance surround audio mode to next available mode. (`VK_SURROUND_MODE_NEXT`) - AudioSurroundModeNext, - /// Decrease treble. (`APPCOMMAND_TREBLE_DOWN`) - AudioTrebleDown, - /// Increase treble. (`APPCOMMAND_TREBLE_UP`) - AudioTrebleUp, - /// Decrease audio volume. (`APPCOMMAND_VOLUME_DOWN`, `KEYCODE_VOLUME_DOWN`) - AudioVolumeDown, - /// Increase audio volume. (`APPCOMMAND_VOLUME_UP`, `KEYCODE_VOLUME_UP`) - AudioVolumeUp, - /// Toggle between muted state and prior volume level. (`APPCOMMAND_VOLUME_MUTE`, - /// `KEYCODE_VOLUME_MUTE`) - AudioVolumeMute, - /// Toggle the microphone on/off. (`APPCOMMAND_MIC_ON_OFF_TOGGLE`) - MicrophoneToggle, - /// Decrease microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_DOWN`) - MicrophoneVolumeDown, - /// Increase microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_UP`) - MicrophoneVolumeUp, - /// Mute the microphone. (`APPCOMMAND_MICROPHONE_VOLUME_MUTE`, `KEYCODE_MUTE`) - MicrophoneVolumeMute, - /// Show correction list when a word is incorrectly identified. (`APPCOMMAND_CORRECTION_LIST`) - SpeechCorrectionList, - /// Toggle between dictation mode and command/control mode. - /// (`APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE`) - SpeechInputToggle, - /// The first generic "LaunchApplication" key. This is commonly associated with launching "My - /// Computer", and may have a computer symbol on the key. (`APPCOMMAND_LAUNCH_APP1`) - LaunchApplication1, - /// The second generic "LaunchApplication" key. This is commonly associated with launching - /// "Calculator", and may have a calculator symbol on the key. (`APPCOMMAND_LAUNCH_APP2`, - /// `KEYCODE_CALCULATOR`) - LaunchApplication2, - /// The "Calendar" key. (`KEYCODE_CALENDAR`) - LaunchCalendar, - /// The "Contacts" key. (`KEYCODE_CONTACTS`) - LaunchContacts, - /// The "Mail" key. (`APPCOMMAND_LAUNCH_MAIL`) - LaunchMail, - /// The "Media Player" key. (`APPCOMMAND_LAUNCH_MEDIA_SELECT`) - LaunchMediaPlayer, - LaunchMusicPlayer, - LaunchPhone, - LaunchScreenSaver, - LaunchSpreadsheet, - LaunchWebBrowser, - LaunchWebCam, - LaunchWordProcessor, - /// Navigate to previous content or page in current history. (`APPCOMMAND_BROWSER_BACKWARD`) - BrowserBack, - /// Open the list of browser favorites. (`APPCOMMAND_BROWSER_FAVORITES`) - BrowserFavorites, - /// Navigate to next content or page in current history. (`APPCOMMAND_BROWSER_FORWARD`) - BrowserForward, - /// Go to the user’s preferred home page. (`APPCOMMAND_BROWSER_HOME`) - BrowserHome, - /// Refresh the current page or content. (`APPCOMMAND_BROWSER_REFRESH`) - BrowserRefresh, - /// Call up the user’s preferred search page. (`APPCOMMAND_BROWSER_SEARCH`) - BrowserSearch, - /// Stop loading the current page or content. (`APPCOMMAND_BROWSER_STOP`) - BrowserStop, - /// The Application switch key, which provides a list of recent apps to switch between. - /// (`KEYCODE_APP_SWITCH`) - AppSwitch, - /// The Call key. (`KEYCODE_CALL`) - Call, - /// The Camera key. (`KEYCODE_CAMERA`) - Camera, - /// The Camera focus key. (`KEYCODE_FOCUS`) - CameraFocus, - /// The End Call key. (`KEYCODE_ENDCALL`) - EndCall, - /// The Back key. (`KEYCODE_BACK`) - GoBack, - /// The Home key, which goes to the phone’s main screen. (`KEYCODE_HOME`) - GoHome, - /// The Headset Hook key. (`KEYCODE_HEADSETHOOK`) - HeadsetHook, - LastNumberRedial, - /// The Notification key. (`KEYCODE_NOTIFICATION`) - Notification, - /// Toggle between manner mode state: silent, vibrate, ring, ... (`KEYCODE_MANNER_MODE`) - MannerMode, - VoiceDial, - /// Switch to viewing TV. (`KEYCODE_TV`) - TV, - /// TV 3D Mode. (`KEYCODE_3D_MODE`) - TV3DMode, - /// Toggle between antenna and cable input. (`KEYCODE_TV_ANTENNA_CABLE`) - TVAntennaCable, - /// Audio description. (`KEYCODE_TV_AUDIO_DESCRIPTION`) - TVAudioDescription, - /// Audio description mixing volume down. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN`) - TVAudioDescriptionMixDown, - /// Audio description mixing volume up. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP`) - TVAudioDescriptionMixUp, - /// Contents menu. (`KEYCODE_TV_CONTENTS_MENU`) - TVContentsMenu, - /// Contents menu. (`KEYCODE_TV_DATA_SERVICE`) - TVDataService, - /// Switch the input mode on an external TV. (`KEYCODE_TV_INPUT`) - TVInput, - /// Switch to component input #1. (`KEYCODE_TV_INPUT_COMPONENT_1`) - TVInputComponent1, - /// Switch to component input #2. (`KEYCODE_TV_INPUT_COMPONENT_2`) - TVInputComponent2, - /// Switch to composite input #1. (`KEYCODE_TV_INPUT_COMPOSITE_1`) - TVInputComposite1, - /// Switch to composite input #2. (`KEYCODE_TV_INPUT_COMPOSITE_2`) - TVInputComposite2, - /// Switch to HDMI input #1. (`KEYCODE_TV_INPUT_HDMI_1`) - TVInputHDMI1, - /// Switch to HDMI input #2. (`KEYCODE_TV_INPUT_HDMI_2`) - TVInputHDMI2, - /// Switch to HDMI input #3. (`KEYCODE_TV_INPUT_HDMI_3`) - TVInputHDMI3, - /// Switch to HDMI input #4. (`KEYCODE_TV_INPUT_HDMI_4`) - TVInputHDMI4, - /// Switch to VGA input #1. (`KEYCODE_TV_INPUT_VGA_1`) - TVInputVGA1, - /// Media context menu. (`KEYCODE_TV_MEDIA_CONTEXT_MENU`) - TVMediaContext, - /// Toggle network. (`KEYCODE_TV_NETWORK`) - TVNetwork, - /// Number entry. (`KEYCODE_TV_NUMBER_ENTRY`) - TVNumberEntry, - /// Toggle the power on an external TV. (`KEYCODE_TV_POWER`) - TVPower, - /// Radio. (`KEYCODE_TV_RADIO_SERVICE`) - TVRadioService, - /// Satellite. (`KEYCODE_TV_SATELLITE`) - TVSatellite, - /// Broadcast Satellite. (`KEYCODE_TV_SATELLITE_BS`) - TVSatelliteBS, - /// Communication Satellite. (`KEYCODE_TV_SATELLITE_CS`) - TVSatelliteCS, - /// Toggle between available satellites. (`KEYCODE_TV_SATELLITE_SERVICE`) - TVSatelliteToggle, - /// Analog Terrestrial. (`KEYCODE_TV_TERRESTRIAL_ANALOG`) - TVTerrestrialAnalog, - /// Digital Terrestrial. (`KEYCODE_TV_TERRESTRIAL_DIGITAL`) - TVTerrestrialDigital, - /// Timer programming. (`KEYCODE_TV_TIMER_PROGRAMMING`) - TVTimer, - /// Switch the input mode on an external AVR (audio/video receiver). (`KEYCODE_AVR_INPUT`) - AVRInput, - /// Toggle the power on an external AVR (audio/video receiver). (`KEYCODE_AVR_POWER`) - AVRPower, - /// General purpose color-coded media function key, as index 0 (red). (`VK_COLORED_KEY_0`, - /// `KEYCODE_PROG_RED`) - ColorF0Red, - /// General purpose color-coded media function key, as index 1 (green). (`VK_COLORED_KEY_1`, - /// `KEYCODE_PROG_GREEN`) - ColorF1Green, - /// General purpose color-coded media function key, as index 2 (yellow). (`VK_COLORED_KEY_2`, - /// `KEYCODE_PROG_YELLOW`) - ColorF2Yellow, - /// General purpose color-coded media function key, as index 3 (blue). (`VK_COLORED_KEY_3`, - /// `KEYCODE_PROG_BLUE`) - ColorF3Blue, - /// General purpose color-coded media function key, as index 4 (grey). (`VK_COLORED_KEY_4`) - ColorF4Grey, - /// General purpose color-coded media function key, as index 5 (brown). (`VK_COLORED_KEY_5`) - ColorF5Brown, - /// Toggle the display of Closed Captions. (`VK_CC`, `KEYCODE_CAPTIONS`) - ClosedCaptionToggle, - /// Adjust brightness of device, by toggling between or cycling through states. (`VK_DIMMER`) - Dimmer, - /// Swap video sources. (`VK_DISPLAY_SWAP`) - DisplaySwap, - /// Select Digital Video Rrecorder. (`KEYCODE_DVR`) - DVR, - /// Exit the current application. (`VK_EXIT`) - Exit, - /// Clear program or content stored as favorite 0. (`VK_CLEAR_FAVORITE_0`) - FavoriteClear0, - /// Clear program or content stored as favorite 1. (`VK_CLEAR_FAVORITE_1`) - FavoriteClear1, - /// Clear program or content stored as favorite 2. (`VK_CLEAR_FAVORITE_2`) - FavoriteClear2, - /// Clear program or content stored as favorite 3. (`VK_CLEAR_FAVORITE_3`) - FavoriteClear3, - /// Select (recall) program or content stored as favorite 0. (`VK_RECALL_FAVORITE_0`) - FavoriteRecall0, - /// Select (recall) program or content stored as favorite 1. (`VK_RECALL_FAVORITE_1`) - FavoriteRecall1, - /// Select (recall) program or content stored as favorite 2. (`VK_RECALL_FAVORITE_2`) - FavoriteRecall2, - /// Select (recall) program or content stored as favorite 3. (`VK_RECALL_FAVORITE_3`) - FavoriteRecall3, - /// Store current program or content as favorite 0. (`VK_STORE_FAVORITE_0`) - FavoriteStore0, - /// Store current program or content as favorite 1. (`VK_STORE_FAVORITE_1`) - FavoriteStore1, - /// Store current program or content as favorite 2. (`VK_STORE_FAVORITE_2`) - FavoriteStore2, - /// Store current program or content as favorite 3. (`VK_STORE_FAVORITE_3`) - FavoriteStore3, - /// Toggle display of program or content guide. (`VK_GUIDE`, `KEYCODE_GUIDE`) - Guide, - /// If guide is active and displayed, then display next day’s content. (`VK_NEXT_DAY`) - GuideNextDay, - /// If guide is active and displayed, then display previous day’s content. (`VK_PREV_DAY`) - GuidePreviousDay, - /// Toggle display of information about currently selected context or media. (`VK_INFO`, - /// `KEYCODE_INFO`) - Info, - /// Toggle instant replay. (`VK_INSTANT_REPLAY`) - InstantReplay, - /// Launch linked content, if available and appropriate. (`VK_LINK`) - Link, - /// List the current program. (`VK_LIST`) - ListProgram, - /// Toggle display listing of currently available live content or programs. (`VK_LIVE`) - LiveContent, - /// Lock or unlock current content or program. (`VK_LOCK`) - Lock, - /// Show a list of media applications: audio/video players and image viewers. (`VK_APPS`) - /// - /// Note: Do not confuse this key value with the Windows' `VK_APPS` / `VK_CONTEXT_MENU` key, - /// which is encoded as `"ContextMenu"`. - MediaApps, - /// Audio track key. (`KEYCODE_MEDIA_AUDIO_TRACK`) - MediaAudioTrack, - /// Select previously selected channel or media. (`VK_LAST`, `KEYCODE_LAST_CHANNEL`) - MediaLast, - /// Skip backward to next content or program. (`KEYCODE_MEDIA_SKIP_BACKWARD`) - MediaSkipBackward, - /// Skip forward to next content or program. (`VK_SKIP`, `KEYCODE_MEDIA_SKIP_FORWARD`) - MediaSkipForward, - /// Step backward to next content or program. (`KEYCODE_MEDIA_STEP_BACKWARD`) - MediaStepBackward, - /// Step forward to next content or program. (`KEYCODE_MEDIA_STEP_FORWARD`) - MediaStepForward, - /// Media top menu. (`KEYCODE_MEDIA_TOP_MENU`) - MediaTopMenu, - /// Navigate in. (`KEYCODE_NAVIGATE_IN`) - NavigateIn, - /// Navigate to next key. (`KEYCODE_NAVIGATE_NEXT`) - NavigateNext, - /// Navigate out. (`KEYCODE_NAVIGATE_OUT`) - NavigateOut, - /// Navigate to previous key. (`KEYCODE_NAVIGATE_PREVIOUS`) - NavigatePrevious, - /// Cycle to next favorite channel (in favorites list). (`VK_NEXT_FAVORITE_CHANNEL`) - NextFavoriteChannel, - /// Cycle to next user profile (if there are multiple user profiles). (`VK_USER`) - NextUserProfile, - /// Access on-demand content or programs. (`VK_ON_DEMAND`) - OnDemand, - /// Pairing key to pair devices. (`KEYCODE_PAIRING`) - Pairing, - /// Move picture-in-picture window down. (`VK_PINP_DOWN`) - PinPDown, - /// Move picture-in-picture window. (`VK_PINP_MOVE`) - PinPMove, - /// Toggle display of picture-in-picture window. (`VK_PINP_TOGGLE`) - PinPToggle, - /// Move picture-in-picture window up. (`VK_PINP_UP`) - PinPUp, - /// Decrease media playback speed. (`VK_PLAY_SPEED_DOWN`) - PlaySpeedDown, - /// Reset playback to normal speed. (`VK_PLAY_SPEED_RESET`) - PlaySpeedReset, - /// Increase media playback speed. (`VK_PLAY_SPEED_UP`) - PlaySpeedUp, - /// Toggle random media or content shuffle mode. (`VK_RANDOM_TOGGLE`) - RandomToggle, - /// Not a physical key, but this key code is sent when the remote control battery is low. - /// (`VK_RC_LOW_BATTERY`) - RcLowBattery, - /// Toggle or cycle between media recording speeds. (`VK_RECORD_SPEED_NEXT`) - RecordSpeedNext, - /// Toggle RF (radio frequency) input bypass mode (pass RF input directly to the RF output). - /// (`VK_RF_BYPASS`) - RfBypass, - /// Toggle scan channels mode. (`VK_SCAN_CHANNELS_TOGGLE`) - ScanChannelsToggle, - /// Advance display screen mode to next available mode. (`VK_SCREEN_MODE_NEXT`) - ScreenModeNext, - /// Toggle display of device settings screen. (`VK_SETTINGS`, `KEYCODE_SETTINGS`) - Settings, - /// Toggle split screen mode. (`VK_SPLIT_SCREEN_TOGGLE`) - SplitScreenToggle, - /// Switch the input mode on an external STB (set top box). (`KEYCODE_STB_INPUT`) - STBInput, - /// Toggle the power on an external STB (set top box). (`KEYCODE_STB_POWER`) - STBPower, - /// Toggle display of subtitles, if available. (`VK_SUBTITLE`) - Subtitle, - /// Toggle display of teletext, if available (`VK_TELETEXT`, `KEYCODE_TV_TELETEXT`). - Teletext, - /// Advance video mode to next available mode. (`VK_VIDEO_MODE_NEXT`) - VideoModeNext, - /// Cause device to identify itself in some manner, e.g., audibly or visibly. (`VK_WINK`) - Wink, - /// Toggle between full-screen and scaled content, or alter magnification level. (`VK_ZOOM`, - /// `KEYCODE_TV_ZOOM_MODE`) - ZoomToggle, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F1, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F2, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F3, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F4, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F5, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F6, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F7, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F8, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F9, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F10, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F11, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F12, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F13, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F14, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F15, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F16, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F17, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F18, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F19, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F20, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F21, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F22, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F23, - /// General-purpose function key. - /// Usually found at the top of the keyboard. - F24, - /// General-purpose function key. - F25, - /// General-purpose function key. - F26, - /// General-purpose function key. - F27, - /// General-purpose function key. - F28, - /// General-purpose function key. - F29, - /// General-purpose function key. - F30, - /// General-purpose function key. - F31, - /// General-purpose function key. - F32, - /// General-purpose function key. - F33, - /// General-purpose function key. - F34, - /// General-purpose function key. - F35, -} - -/// Key represents the meaning of a keypress. -/// -/// This is a superset of the UI Events Specification's [`KeyboardEvent.key`] with -/// additions: -/// - All simple variants are wrapped under the `Named` variant -/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`. -/// - The `Dead` variant here, can specify the character which is inserted when pressing the -/// dead-key twice. -/// -/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/ - -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub enum Key { - /// A simple (unparameterised) action - Named(NamedKey), - - /// A key string that corresponds to the character typed by the user, taking into account the - /// user’s current locale setting, and any system-level keyboard mapping overrides that are in - /// effect. - Character(Str), - - /// This variant is used when the key cannot be translated to any other variant. - /// - /// The native key is provided (if available) in order to allow the user to specify keybindings - /// for keys which are not defined by this API, mainly through some sort of UI. - Unidentified(NativeKey), - - /// Contains the text representation of the dead-key when available. - /// - /// ## Platform-specific - /// - **Web:** Always contains `None` - Dead(Option), -} - -impl From for Key { - #[inline] - fn from(action: NamedKey) -> Self { - Key::Named(action) - } -} - -impl From for Key { - #[inline] - fn from(code: NativeKey) -> Self { - Key::Unidentified(code) - } -} - -impl PartialEq for Key { - #[inline] - fn eq(&self, rhs: &NamedKey) -> bool { - match self { - Key::Named(ref a) => a == rhs, - _ => false, - } - } -} - -impl> PartialEq for Key { - #[inline] - fn eq(&self, rhs: &str) -> bool { - match self { - Key::Character(ref s) => s == rhs, - _ => false, - } - } -} - -impl> PartialEq<&str> for Key { - #[inline] - fn eq(&self, rhs: &&str) -> bool { - self == *rhs - } -} - -impl PartialEq for Key { - #[inline] - fn eq(&self, rhs: &NativeKey) -> bool { - match self { - Key::Unidentified(ref code) => code == rhs, - _ => false, - } - } -} - -impl PartialEq> for NativeKey { - #[inline] - fn eq(&self, rhs: &Key) -> bool { - rhs == self - } -} - -impl Key { - /// Convert `Key::Character(SmolStr)` to `Key::Character(&str)` so you can more easily match on - /// `Key`. All other variants remain unchanged. - pub fn as_ref(&self) -> Key<&str> { - match self { - Key::Named(a) => Key::Named(*a), - Key::Character(ch) => Key::Character(ch.as_str()), - Key::Dead(d) => Key::Dead(*d), - Key::Unidentified(u) => Key::Unidentified(u.clone()), - } - } -} - -impl NamedKey { - pub fn to_text(&self) -> Option<&str> { - match self { - NamedKey::Enter => Some("\r"), - NamedKey::Backspace => Some("\x08"), - NamedKey::Tab => Some("\t"), - NamedKey::Space => Some(" "), - NamedKey::Escape => Some("\x1b"), - _ => None, - } - } -} - -impl Key { - pub fn to_text(&self) -> Option<&str> { - match self { - Key::Named(action) => action.to_text(), - Key::Character(ch) => Some(ch.as_str()), - _ => None, - } - } -} - -/// The location of the key on the keyboard. -/// -/// Certain physical keys on the keyboard can have the same value, but are in different locations. -/// For instance, the Shift key can be on the left or right side of the keyboard, or the number -/// keys can be above the letters or on the numpad. This enum allows the user to differentiate -/// them. -/// -/// See the documentation for the [`location`] field on the [`KeyEvent`] struct for more -/// information. -/// -/// [`location`]: ../event/struct.KeyEvent.html#structfield.location -/// [`KeyEvent`]: crate::event::KeyEvent -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum KeyLocation { - /// The key is in its "normal" location on the keyboard. - /// - /// For instance, the "1" key above the "Q" key on a QWERTY keyboard will use this location. - /// This invariant is also returned when the location of the key cannot be identified. - /// - /// ![Standard 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_standard_1_key.svg) - /// - /// - /// For image attribution, see the - /// - /// ATTRIBUTION.md - /// - /// file. - /// - Standard, - - /// The key is on the left side of the keyboard. - /// - /// For instance, the left Shift key below the Caps Lock key on a QWERTY keyboard will use this - /// location. - /// - /// ![Left Shift key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_left_shift_key.svg) - /// - /// - /// For image attribution, see the - /// - /// ATTRIBUTION.md - /// - /// file. - /// - Left, - - /// The key is on the right side of the keyboard. - /// - /// For instance, the right Shift key below the Enter key on a QWERTY keyboard will use this - /// location. - /// - /// ![Right Shift key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_right_shift_key.svg) - /// - /// - /// For image attribution, see the - /// - /// ATTRIBUTION.md - /// - /// file. - /// - Right, - - /// The key is on the numpad. - /// - /// For instance, the "1" key on the numpad will use this location. - /// - /// ![Numpad 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_numpad_1_key.svg) - /// - /// - /// For image attribution, see the - /// - /// ATTRIBUTION.md - /// - /// file. - /// - Numpad, -} - -bitflags! { - /// Represents the current state of the keyboard modifiers - /// - /// Each flag represents a modifier and is set if this modifier is active. - #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct ModifiersState: u32 { - /// The "shift" key. - const SHIFT = 0b100; - /// The "control" key. - const CONTROL = 0b100 << 3; - /// The "alt" key. - const ALT = 0b100 << 6; - /// This is the "windows" key on PC and "command" key on Mac. - const SUPER = 0b100 << 9; - } -} - -impl ModifiersState { - /// Returns `true` if the shift key is pressed. - pub fn shift_key(&self) -> bool { - self.intersects(Self::SHIFT) - } - - /// Returns `true` if the control key is pressed. - pub fn control_key(&self) -> bool { - self.intersects(Self::CONTROL) - } - - /// Returns `true` if the alt key is pressed. - pub fn alt_key(&self) -> bool { - self.intersects(Self::ALT) - } - - /// Returns `true` if the super key is pressed. - pub fn super_key(&self) -> bool { - self.intersects(Self::SUPER) - } -} - -/// The state of the particular modifiers key. -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] -pub enum ModifiersKeyState { - /// The particular key is pressed. - Pressed, - /// The state of the key is unknown. - #[default] - Unknown, -} - -// NOTE: the exact modifier key is not used to represent modifiers state in the -// first place due to a fact that modifiers state could be changed without any -// key being pressed and on some platforms like Wayland/X11 which key resulted -// in modifiers change is hidden, also, not that it really matters. -// -// The reason this API is even exposed is mostly to provide a way for users -// to treat modifiers differently based on their position, which is required -// on macOS due to their AltGr/Option situation. -bitflags! { - #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub(crate) struct ModifiersKeys: u8 { - const LSHIFT = 0b0000_0001; - const RSHIFT = 0b0000_0010; - const LCONTROL = 0b0000_0100; - const RCONTROL = 0b0000_1000; - const LALT = 0b0001_0000; - const RALT = 0b0010_0000; - const LSUPER = 0b0100_0000; - const RSUPER = 0b1000_0000; - } -} diff --git a/sessionlockev/src/keymap.rs b/sessionlockev/src/keymap.rs deleted file mode 100644 index 432f6f8..0000000 --- a/sessionlockev/src/keymap.rs +++ /dev/null @@ -1,893 +0,0 @@ -//! XKB keymap. - -use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey}; - -/// Map the raw X11-style keycode to the `KeyCode` enum. -/// -/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses. -pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey { - scancode_to_physicalkey(keycode.saturating_sub(8)) -} - -/// Map the linux scancode to Keycode. -/// -/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode. -pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey { - // The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as - // libxkbcommon's documentation seems to suggest that the keycode values we're interested in - // are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes, - // I can only hope they agree on what the keycodes mean. - // - // Some of the keycodes are likely superfluous for our purposes, and some are ones which are - // difficult to test the correctness of, or discover the purpose of. Because of this, they've - // either been commented out here, or not included at all. - PhysicalKey::Code(match scancode { - 0 => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(0)), - 1 => KeyCode::Escape, - 2 => KeyCode::Digit1, - 3 => KeyCode::Digit2, - 4 => KeyCode::Digit3, - 5 => KeyCode::Digit4, - 6 => KeyCode::Digit5, - 7 => KeyCode::Digit6, - 8 => KeyCode::Digit7, - 9 => KeyCode::Digit8, - 10 => KeyCode::Digit9, - 11 => KeyCode::Digit0, - 12 => KeyCode::Minus, - 13 => KeyCode::Equal, - 14 => KeyCode::Backspace, - 15 => KeyCode::Tab, - 16 => KeyCode::KeyQ, - 17 => KeyCode::KeyW, - 18 => KeyCode::KeyE, - 19 => KeyCode::KeyR, - 20 => KeyCode::KeyT, - 21 => KeyCode::KeyY, - 22 => KeyCode::KeyU, - 23 => KeyCode::KeyI, - 24 => KeyCode::KeyO, - 25 => KeyCode::KeyP, - 26 => KeyCode::BracketLeft, - 27 => KeyCode::BracketRight, - 28 => KeyCode::Enter, - 29 => KeyCode::ControlLeft, - 30 => KeyCode::KeyA, - 31 => KeyCode::KeyS, - 32 => KeyCode::KeyD, - 33 => KeyCode::KeyF, - 34 => KeyCode::KeyG, - 35 => KeyCode::KeyH, - 36 => KeyCode::KeyJ, - 37 => KeyCode::KeyK, - 38 => KeyCode::KeyL, - 39 => KeyCode::Semicolon, - 40 => KeyCode::Quote, - 41 => KeyCode::Backquote, - 42 => KeyCode::ShiftLeft, - 43 => KeyCode::Backslash, - 44 => KeyCode::KeyZ, - 45 => KeyCode::KeyX, - 46 => KeyCode::KeyC, - 47 => KeyCode::KeyV, - 48 => KeyCode::KeyB, - 49 => KeyCode::KeyN, - 50 => KeyCode::KeyM, - 51 => KeyCode::Comma, - 52 => KeyCode::Period, - 53 => KeyCode::Slash, - 54 => KeyCode::ShiftRight, - 55 => KeyCode::NumpadMultiply, - 56 => KeyCode::AltLeft, - 57 => KeyCode::Space, - 58 => KeyCode::CapsLock, - 59 => KeyCode::F1, - 60 => KeyCode::F2, - 61 => KeyCode::F3, - 62 => KeyCode::F4, - 63 => KeyCode::F5, - 64 => KeyCode::F6, - 65 => KeyCode::F7, - 66 => KeyCode::F8, - 67 => KeyCode::F9, - 68 => KeyCode::F10, - 69 => KeyCode::NumLock, - 70 => KeyCode::ScrollLock, - 71 => KeyCode::Numpad7, - 72 => KeyCode::Numpad8, - 73 => KeyCode::Numpad9, - 74 => KeyCode::NumpadSubtract, - 75 => KeyCode::Numpad4, - 76 => KeyCode::Numpad5, - 77 => KeyCode::Numpad6, - 78 => KeyCode::NumpadAdd, - 79 => KeyCode::Numpad1, - 80 => KeyCode::Numpad2, - 81 => KeyCode::Numpad3, - 82 => KeyCode::Numpad0, - 83 => KeyCode::NumpadDecimal, - 85 => KeyCode::Lang5, - 86 => KeyCode::IntlBackslash, - 87 => KeyCode::F11, - 88 => KeyCode::F12, - 89 => KeyCode::IntlRo, - 90 => KeyCode::Lang3, - 91 => KeyCode::Lang4, - 92 => KeyCode::Convert, - 93 => KeyCode::KanaMode, - 94 => KeyCode::NonConvert, - // 95 => KeyCode::KPJPCOMMA, - 96 => KeyCode::NumpadEnter, - 97 => KeyCode::ControlRight, - 98 => KeyCode::NumpadDivide, - 99 => KeyCode::PrintScreen, - 100 => KeyCode::AltRight, - // 101 => KeyCode::LINEFEED, - 102 => KeyCode::Home, - 103 => KeyCode::ArrowUp, - 104 => KeyCode::PageUp, - 105 => KeyCode::ArrowLeft, - 106 => KeyCode::ArrowRight, - 107 => KeyCode::End, - 108 => KeyCode::ArrowDown, - 109 => KeyCode::PageDown, - 110 => KeyCode::Insert, - 111 => KeyCode::Delete, - // 112 => KeyCode::MACRO, - 113 => KeyCode::AudioVolumeMute, - 114 => KeyCode::AudioVolumeDown, - 115 => KeyCode::AudioVolumeUp, - // 116 => KeyCode::POWER, - 117 => KeyCode::NumpadEqual, - // 118 => KeyCode::KPPLUSMINUS, - 119 => KeyCode::Pause, - // 120 => KeyCode::SCALE, - 121 => KeyCode::NumpadComma, - 122 => KeyCode::Lang1, - 123 => KeyCode::Lang2, - 124 => KeyCode::IntlYen, - 125 => KeyCode::SuperLeft, - 126 => KeyCode::SuperRight, - 127 => KeyCode::ContextMenu, - // 128 => KeyCode::STOP, - // 129 => KeyCode::AGAIN, - // 130 => KeyCode::PROPS, - // 131 => KeyCode::UNDO, - // 132 => KeyCode::FRONT, - // 133 => KeyCode::COPY, - // 134 => KeyCode::OPEN, - // 135 => KeyCode::PASTE, - // 136 => KeyCode::FIND, - // 137 => KeyCode::CUT, - // 138 => KeyCode::HELP, - // 139 => KeyCode::MENU, - // 140 => KeyCode::CALC, - // 141 => KeyCode::SETUP, - // 142 => KeyCode::SLEEP, - // 143 => KeyCode::WAKEUP, - // 144 => KeyCode::FILE, - // 145 => KeyCode::SENDFILE, - // 146 => KeyCode::DELETEFILE, - // 147 => KeyCode::XFER, - // 148 => KeyCode::PROG1, - // 149 => KeyCode::PROG2, - // 150 => KeyCode::WWW, - // 151 => KeyCode::MSDOS, - // 152 => KeyCode::COFFEE, - // 153 => KeyCode::ROTATE_DISPLAY, - // 154 => KeyCode::CYCLEWINDOWS, - // 155 => KeyCode::MAIL, - // 156 => KeyCode::BOOKMARKS, - // 157 => KeyCode::COMPUTER, - // 158 => KeyCode::BACK, - // 159 => KeyCode::FORWARD, - // 160 => KeyCode::CLOSECD, - // 161 => KeyCode::EJECTCD, - // 162 => KeyCode::EJECTCLOSECD, - 163 => KeyCode::MediaTrackNext, - 164 => KeyCode::MediaPlayPause, - 165 => KeyCode::MediaTrackPrevious, - 166 => KeyCode::MediaStop, - // 167 => KeyCode::RECORD, - // 168 => KeyCode::REWIND, - // 169 => KeyCode::PHONE, - // 170 => KeyCode::ISO, - // 171 => KeyCode::CONFIG, - // 172 => KeyCode::HOMEPAGE, - // 173 => KeyCode::REFRESH, - // 174 => KeyCode::EXIT, - // 175 => KeyCode::MOVE, - // 176 => KeyCode::EDIT, - // 177 => KeyCode::SCROLLUP, - // 178 => KeyCode::SCROLLDOWN, - // 179 => KeyCode::KPLEFTPAREN, - // 180 => KeyCode::KPRIGHTPAREN, - // 181 => KeyCode::NEW, - // 182 => KeyCode::REDO, - 183 => KeyCode::F13, - 184 => KeyCode::F14, - 185 => KeyCode::F15, - 186 => KeyCode::F16, - 187 => KeyCode::F17, - 188 => KeyCode::F18, - 189 => KeyCode::F19, - 190 => KeyCode::F20, - 191 => KeyCode::F21, - 192 => KeyCode::F22, - 193 => KeyCode::F23, - 194 => KeyCode::F24, - // 200 => KeyCode::PLAYCD, - // 201 => KeyCode::PAUSECD, - // 202 => KeyCode::PROG3, - // 203 => KeyCode::PROG4, - // 204 => KeyCode::DASHBOARD, - // 205 => KeyCode::SUSPEND, - // 206 => KeyCode::CLOSE, - // 207 => KeyCode::PLAY, - // 208 => KeyCode::FASTFORWARD, - // 209 => KeyCode::BASSBOOST, - // 210 => KeyCode::PRINT, - // 211 => KeyCode::HP, - // 212 => KeyCode::CAMERA, - // 213 => KeyCode::SOUND, - // 214 => KeyCode::QUESTION, - // 215 => KeyCode::EMAIL, - // 216 => KeyCode::CHAT, - // 217 => KeyCode::SEARCH, - // 218 => KeyCode::CONNECT, - // 219 => KeyCode::FINANCE, - // 220 => KeyCode::SPORT, - // 221 => KeyCode::SHOP, - // 222 => KeyCode::ALTERASE, - // 223 => KeyCode::CANCEL, - // 224 => KeyCode::BRIGHTNESSDOW, - // 225 => KeyCode::BRIGHTNESSU, - // 226 => KeyCode::MEDIA, - // 227 => KeyCode::SWITCHVIDEOMODE, - // 228 => KeyCode::KBDILLUMTOGGLE, - // 229 => KeyCode::KBDILLUMDOWN, - // 230 => KeyCode::KBDILLUMUP, - // 231 => KeyCode::SEND, - // 232 => KeyCode::REPLY, - // 233 => KeyCode::FORWARDMAIL, - // 234 => KeyCode::SAVE, - // 235 => KeyCode::DOCUMENTS, - // 236 => KeyCode::BATTERY, - // 237 => KeyCode::BLUETOOTH, - // 238 => KeyCode::WLAN, - // 239 => KeyCode::UWB, - 240 => return PhysicalKey::Unidentified(NativeKeyCode::Unidentified), - // 241 => KeyCode::VIDEO_NEXT, - // 242 => KeyCode::VIDEO_PREV, - // 243 => KeyCode::BRIGHTNESS_CYCLE, - // 244 => KeyCode::BRIGHTNESS_AUTO, - // 245 => KeyCode::DISPLAY_OFF, - // 246 => KeyCode::WWAN, - // 247 => KeyCode::RFKILL, - // 248 => KeyCode::KEY_MICMUTE, - _ => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(scancode)), - }) -} - -// NOTE: maybe one day need it -#[allow(unused)] -pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option { - let code = match key { - PhysicalKey::Code(code) => code, - PhysicalKey::Unidentified(code) => { - return match code { - NativeKeyCode::Unidentified => Some(240), - NativeKeyCode::Xkb(raw) => Some(raw), - _ => None, - }; - } - }; - - match code { - KeyCode::Escape => Some(1), - KeyCode::Digit1 => Some(2), - KeyCode::Digit2 => Some(3), - KeyCode::Digit3 => Some(4), - KeyCode::Digit4 => Some(5), - KeyCode::Digit5 => Some(6), - KeyCode::Digit6 => Some(7), - KeyCode::Digit7 => Some(8), - KeyCode::Digit8 => Some(9), - KeyCode::Digit9 => Some(10), - KeyCode::Digit0 => Some(11), - KeyCode::Minus => Some(12), - KeyCode::Equal => Some(13), - KeyCode::Backspace => Some(14), - KeyCode::Tab => Some(15), - KeyCode::KeyQ => Some(16), - KeyCode::KeyW => Some(17), - KeyCode::KeyE => Some(18), - KeyCode::KeyR => Some(19), - KeyCode::KeyT => Some(20), - KeyCode::KeyY => Some(21), - KeyCode::KeyU => Some(22), - KeyCode::KeyI => Some(23), - KeyCode::KeyO => Some(24), - KeyCode::KeyP => Some(25), - KeyCode::BracketLeft => Some(26), - KeyCode::BracketRight => Some(27), - KeyCode::Enter => Some(28), - KeyCode::ControlLeft => Some(29), - KeyCode::KeyA => Some(30), - KeyCode::KeyS => Some(31), - KeyCode::KeyD => Some(32), - KeyCode::KeyF => Some(33), - KeyCode::KeyG => Some(34), - KeyCode::KeyH => Some(35), - KeyCode::KeyJ => Some(36), - KeyCode::KeyK => Some(37), - KeyCode::KeyL => Some(38), - KeyCode::Semicolon => Some(39), - KeyCode::Quote => Some(40), - KeyCode::Backquote => Some(41), - KeyCode::ShiftLeft => Some(42), - KeyCode::Backslash => Some(43), - KeyCode::KeyZ => Some(44), - KeyCode::KeyX => Some(45), - KeyCode::KeyC => Some(46), - KeyCode::KeyV => Some(47), - KeyCode::KeyB => Some(48), - KeyCode::KeyN => Some(49), - KeyCode::KeyM => Some(50), - KeyCode::Comma => Some(51), - KeyCode::Period => Some(52), - KeyCode::Slash => Some(53), - KeyCode::ShiftRight => Some(54), - KeyCode::NumpadMultiply => Some(55), - KeyCode::AltLeft => Some(56), - KeyCode::Space => Some(57), - KeyCode::CapsLock => Some(58), - KeyCode::F1 => Some(59), - KeyCode::F2 => Some(60), - KeyCode::F3 => Some(61), - KeyCode::F4 => Some(62), - KeyCode::F5 => Some(63), - KeyCode::F6 => Some(64), - KeyCode::F7 => Some(65), - KeyCode::F8 => Some(66), - KeyCode::F9 => Some(67), - KeyCode::F10 => Some(68), - KeyCode::NumLock => Some(69), - KeyCode::ScrollLock => Some(70), - KeyCode::Numpad7 => Some(71), - KeyCode::Numpad8 => Some(72), - KeyCode::Numpad9 => Some(73), - KeyCode::NumpadSubtract => Some(74), - KeyCode::Numpad4 => Some(75), - KeyCode::Numpad5 => Some(76), - KeyCode::Numpad6 => Some(77), - KeyCode::NumpadAdd => Some(78), - KeyCode::Numpad1 => Some(79), - KeyCode::Numpad2 => Some(80), - KeyCode::Numpad3 => Some(81), - KeyCode::Numpad0 => Some(82), - KeyCode::NumpadDecimal => Some(83), - KeyCode::Lang5 => Some(85), - KeyCode::IntlBackslash => Some(86), - KeyCode::F11 => Some(87), - KeyCode::F12 => Some(88), - KeyCode::IntlRo => Some(89), - KeyCode::Lang3 => Some(90), - KeyCode::Lang4 => Some(91), - KeyCode::Convert => Some(92), - KeyCode::KanaMode => Some(93), - KeyCode::NonConvert => Some(94), - KeyCode::NumpadEnter => Some(96), - KeyCode::ControlRight => Some(97), - KeyCode::NumpadDivide => Some(98), - KeyCode::PrintScreen => Some(99), - KeyCode::AltRight => Some(100), - KeyCode::Home => Some(102), - KeyCode::ArrowUp => Some(103), - KeyCode::PageUp => Some(104), - KeyCode::ArrowLeft => Some(105), - KeyCode::ArrowRight => Some(106), - KeyCode::End => Some(107), - KeyCode::ArrowDown => Some(108), - KeyCode::PageDown => Some(109), - KeyCode::Insert => Some(110), - KeyCode::Delete => Some(111), - KeyCode::AudioVolumeMute => Some(113), - KeyCode::AudioVolumeDown => Some(114), - KeyCode::AudioVolumeUp => Some(115), - KeyCode::NumpadEqual => Some(117), - KeyCode::Pause => Some(119), - KeyCode::NumpadComma => Some(121), - KeyCode::Lang1 => Some(122), - KeyCode::Lang2 => Some(123), - KeyCode::IntlYen => Some(124), - KeyCode::SuperLeft => Some(125), - KeyCode::SuperRight => Some(126), - KeyCode::ContextMenu => Some(127), - KeyCode::MediaTrackNext => Some(163), - KeyCode::MediaPlayPause => Some(164), - KeyCode::MediaTrackPrevious => Some(165), - KeyCode::MediaStop => Some(166), - KeyCode::F13 => Some(183), - KeyCode::F14 => Some(184), - KeyCode::F15 => Some(185), - KeyCode::F16 => Some(186), - KeyCode::F17 => Some(187), - KeyCode::F18 => Some(188), - KeyCode::F19 => Some(189), - KeyCode::F20 => Some(190), - KeyCode::F21 => Some(191), - KeyCode::F22 => Some(192), - KeyCode::F23 => Some(193), - KeyCode::F24 => Some(194), - _ => None, - } -} - -pub fn keysym_to_key(keysym: u32) -> Key { - use xkbcommon_dl::keysyms; - Key::Named(match keysym { - // TTY function keys - keysyms::BackSpace => NamedKey::Backspace, - keysyms::Tab => NamedKey::Tab, - // keysyms::Linefeed => NamedKey::Linefeed, - keysyms::Clear => NamedKey::Clear, - keysyms::Return => NamedKey::Enter, - keysyms::Pause => NamedKey::Pause, - keysyms::Scroll_Lock => NamedKey::ScrollLock, - keysyms::Sys_Req => NamedKey::PrintScreen, - keysyms::Escape => NamedKey::Escape, - keysyms::Delete => NamedKey::Delete, - - // IME keys - keysyms::Multi_key => NamedKey::Compose, - keysyms::Codeinput => NamedKey::CodeInput, - keysyms::SingleCandidate => NamedKey::SingleCandidate, - keysyms::MultipleCandidate => NamedKey::AllCandidates, - keysyms::PreviousCandidate => NamedKey::PreviousCandidate, - - // Japanese keys - keysyms::Kanji => NamedKey::KanjiMode, - keysyms::Muhenkan => NamedKey::NonConvert, - keysyms::Henkan_Mode => NamedKey::Convert, - keysyms::Romaji => NamedKey::Romaji, - keysyms::Hiragana => NamedKey::Hiragana, - keysyms::Hiragana_Katakana => NamedKey::HiraganaKatakana, - keysyms::Zenkaku => NamedKey::Zenkaku, - keysyms::Hankaku => NamedKey::Hankaku, - keysyms::Zenkaku_Hankaku => NamedKey::ZenkakuHankaku, - // keysyms::Touroku => NamedKey::Touroku, - // keysyms::Massyo => NamedKey::Massyo, - keysyms::Kana_Lock => NamedKey::KanaMode, - keysyms::Kana_Shift => NamedKey::KanaMode, - keysyms::Eisu_Shift => NamedKey::Alphanumeric, - keysyms::Eisu_toggle => NamedKey::Alphanumeric, - // NOTE: The next three items are aliases for values we've already mapped. - // keysyms::Kanji_Bangou => NamedKey::CodeInput, - // keysyms::Zen_Koho => NamedKey::AllCandidates, - // keysyms::Mae_Koho => NamedKey::PreviousCandidate, - - // Cursor control & motion - keysyms::Home => NamedKey::Home, - keysyms::Left => NamedKey::ArrowLeft, - keysyms::Up => NamedKey::ArrowUp, - keysyms::Right => NamedKey::ArrowRight, - keysyms::Down => NamedKey::ArrowDown, - // keysyms::Prior => NamedKey::PageUp, - keysyms::Page_Up => NamedKey::PageUp, - // keysyms::Next => NamedKey::PageDown, - keysyms::Page_Down => NamedKey::PageDown, - keysyms::End => NamedKey::End, - // keysyms::Begin => NamedKey::Begin, - - // Misc. functions - keysyms::Select => NamedKey::Select, - keysyms::Print => NamedKey::PrintScreen, - keysyms::Execute => NamedKey::Execute, - keysyms::Insert => NamedKey::Insert, - keysyms::Undo => NamedKey::Undo, - keysyms::Redo => NamedKey::Redo, - keysyms::Menu => NamedKey::ContextMenu, - keysyms::Find => NamedKey::Find, - keysyms::Cancel => NamedKey::Cancel, - keysyms::Help => NamedKey::Help, - keysyms::Break => NamedKey::Pause, - keysyms::Mode_switch => NamedKey::ModeChange, - // keysyms::script_switch => NamedKey::ModeChange, - keysyms::Num_Lock => NamedKey::NumLock, - - // Keypad keys - // keysyms::KP_Space => return Key::Character(" "), - keysyms::KP_Tab => NamedKey::Tab, - keysyms::KP_Enter => NamedKey::Enter, - keysyms::KP_F1 => NamedKey::F1, - keysyms::KP_F2 => NamedKey::F2, - keysyms::KP_F3 => NamedKey::F3, - keysyms::KP_F4 => NamedKey::F4, - keysyms::KP_Home => NamedKey::Home, - keysyms::KP_Left => NamedKey::ArrowLeft, - keysyms::KP_Up => NamedKey::ArrowUp, - keysyms::KP_Right => NamedKey::ArrowRight, - keysyms::KP_Down => NamedKey::ArrowDown, - // keysyms::KP_Prior => NamedKey::PageUp, - keysyms::KP_Page_Up => NamedKey::PageUp, - // keysyms::KP_Next => NamedKey::PageDown, - keysyms::KP_Page_Down => NamedKey::PageDown, - keysyms::KP_End => NamedKey::End, - // This is the key labeled "5" on the numpad when NumLock is off. - // keysyms::KP_Begin => NamedKey::Begin, - keysyms::KP_Insert => NamedKey::Insert, - keysyms::KP_Delete => NamedKey::Delete, - // keysyms::KP_Equal => NamedKey::Equal, - // keysyms::KP_Multiply => NamedKey::Multiply, - // keysyms::KP_Add => NamedKey::Add, - // keysyms::KP_Separator => NamedKey::Separator, - // keysyms::KP_Subtract => NamedKey::Subtract, - // keysyms::KP_Decimal => NamedKey::Decimal, - // keysyms::KP_Divide => NamedKey::Divide, - - // keysyms::KP_0 => return Key::Character("0"), - // keysyms::KP_1 => return Key::Character("1"), - // keysyms::KP_2 => return Key::Character("2"), - // keysyms::KP_3 => return Key::Character("3"), - // keysyms::KP_4 => return Key::Character("4"), - // keysyms::KP_5 => return Key::Character("5"), - // keysyms::KP_6 => return Key::Character("6"), - // keysyms::KP_7 => return Key::Character("7"), - // keysyms::KP_8 => return Key::Character("8"), - // keysyms::KP_9 => return Key::Character("9"), - - // Function keys - keysyms::F1 => NamedKey::F1, - keysyms::F2 => NamedKey::F2, - keysyms::F3 => NamedKey::F3, - keysyms::F4 => NamedKey::F4, - keysyms::F5 => NamedKey::F5, - keysyms::F6 => NamedKey::F6, - keysyms::F7 => NamedKey::F7, - keysyms::F8 => NamedKey::F8, - keysyms::F9 => NamedKey::F9, - keysyms::F10 => NamedKey::F10, - keysyms::F11 => NamedKey::F11, - keysyms::F12 => NamedKey::F12, - keysyms::F13 => NamedKey::F13, - keysyms::F14 => NamedKey::F14, - keysyms::F15 => NamedKey::F15, - keysyms::F16 => NamedKey::F16, - keysyms::F17 => NamedKey::F17, - keysyms::F18 => NamedKey::F18, - keysyms::F19 => NamedKey::F19, - keysyms::F20 => NamedKey::F20, - keysyms::F21 => NamedKey::F21, - keysyms::F22 => NamedKey::F22, - keysyms::F23 => NamedKey::F23, - keysyms::F24 => NamedKey::F24, - keysyms::F25 => NamedKey::F25, - keysyms::F26 => NamedKey::F26, - keysyms::F27 => NamedKey::F27, - keysyms::F28 => NamedKey::F28, - keysyms::F29 => NamedKey::F29, - keysyms::F30 => NamedKey::F30, - keysyms::F31 => NamedKey::F31, - keysyms::F32 => NamedKey::F32, - keysyms::F33 => NamedKey::F33, - keysyms::F34 => NamedKey::F34, - keysyms::F35 => NamedKey::F35, - - // Modifiers - keysyms::Shift_L => NamedKey::Shift, - keysyms::Shift_R => NamedKey::Shift, - keysyms::Control_L => NamedKey::Control, - keysyms::Control_R => NamedKey::Control, - keysyms::Caps_Lock => NamedKey::CapsLock, - // keysyms::Shift_Lock => NamedKey::ShiftLock, - - // keysyms::Meta_L => NamedKey::Meta, - // keysyms::Meta_R => NamedKey::Meta, - keysyms::Alt_L => NamedKey::Alt, - keysyms::Alt_R => NamedKey::Alt, - keysyms::Super_L => NamedKey::Super, - keysyms::Super_R => NamedKey::Super, - keysyms::Hyper_L => NamedKey::Hyper, - keysyms::Hyper_R => NamedKey::Hyper, - - // XKB function and modifier keys - // keysyms::ISO_Lock => NamedKey::IsoLock, - // keysyms::ISO_Level2_Latch => NamedKey::IsoLevel2Latch, - keysyms::ISO_Level3_Shift => NamedKey::AltGraph, - keysyms::ISO_Level3_Latch => NamedKey::AltGraph, - keysyms::ISO_Level3_Lock => NamedKey::AltGraph, - // keysyms::ISO_Level5_Shift => NamedKey::IsoLevel5Shift, - // keysyms::ISO_Level5_Latch => NamedKey::IsoLevel5Latch, - // keysyms::ISO_Level5_Lock => NamedKey::IsoLevel5Lock, - // keysyms::ISO_Group_Shift => NamedKey::IsoGroupShift, - // keysyms::ISO_Group_Latch => NamedKey::IsoGroupLatch, - // keysyms::ISO_Group_Lock => NamedKey::IsoGroupLock, - keysyms::ISO_Next_Group => NamedKey::GroupNext, - // keysyms::ISO_Next_Group_Lock => NamedKey::GroupNextLock, - keysyms::ISO_Prev_Group => NamedKey::GroupPrevious, - // keysyms::ISO_Prev_Group_Lock => NamedKey::GroupPreviousLock, - keysyms::ISO_First_Group => NamedKey::GroupFirst, - // keysyms::ISO_First_Group_Lock => NamedKey::GroupFirstLock, - keysyms::ISO_Last_Group => NamedKey::GroupLast, - // keysyms::ISO_Last_Group_Lock => NamedKey::GroupLastLock, - keysyms::ISO_Left_Tab => NamedKey::Tab, - // keysyms::ISO_Move_Line_Up => NamedKey::IsoMoveLineUp, - // keysyms::ISO_Move_Line_Down => NamedKey::IsoMoveLineDown, - // keysyms::ISO_Partial_Line_Up => NamedKey::IsoPartialLineUp, - // keysyms::ISO_Partial_Line_Down => NamedKey::IsoPartialLineDown, - // keysyms::ISO_Partial_Space_Left => NamedKey::IsoPartialSpaceLeft, - // keysyms::ISO_Partial_Space_Right => NamedKey::IsoPartialSpaceRight, - // keysyms::ISO_Set_Margin_Left => NamedKey::IsoSetMarginLeft, - // keysyms::ISO_Set_Margin_Right => NamedKey::IsoSetMarginRight, - // keysyms::ISO_Release_Margin_Left => NamedKey::IsoReleaseMarginLeft, - // keysyms::ISO_Release_Margin_Right => NamedKey::IsoReleaseMarginRight, - // keysyms::ISO_Release_Both_Margins => NamedKey::IsoReleaseBothMargins, - // keysyms::ISO_Fast_Cursor_Left => NamedKey::IsoFastCursorLeft, - // keysyms::ISO_Fast_Cursor_Right => NamedKey::IsoFastCursorRight, - // keysyms::ISO_Fast_Cursor_Up => NamedKey::IsoFastCursorUp, - // keysyms::ISO_Fast_Cursor_Down => NamedKey::IsoFastCursorDown, - // keysyms::ISO_Continuous_Underline => NamedKey::IsoContinuousUnderline, - // keysyms::ISO_Discontinuous_Underline => NamedKey::IsoDiscontinuousUnderline, - // keysyms::ISO_Emphasize => NamedKey::IsoEmphasize, - // keysyms::ISO_Center_Object => NamedKey::IsoCenterObject, - keysyms::ISO_Enter => NamedKey::Enter, - - // dead_grave..dead_currency - - // dead_lowline..dead_longsolidusoverlay - - // dead_a..dead_capital_schwa - - // dead_greek - - // First_Virtual_Screen..Terminate_Server - - // AccessX_Enable..AudibleBell_Enable - - // Pointer_Left..Pointer_Drag5 - - // Pointer_EnableKeys..Pointer_DfltBtnPrev - - // ch..C_H - - // 3270 terminal keys - // keysyms::3270_Duplicate => NamedKey::Duplicate, - // keysyms::3270_FieldMark => NamedKey::FieldMark, - // keysyms::3270_Right2 => NamedKey::Right2, - // keysyms::3270_Left2 => NamedKey::Left2, - // keysyms::3270_BackTab => NamedKey::BackTab, - keysyms::_3270_EraseEOF => NamedKey::EraseEof, - // keysyms::3270_EraseInput => NamedKey::EraseInput, - // keysyms::3270_Reset => NamedKey::Reset, - // keysyms::3270_Quit => NamedKey::Quit, - // keysyms::3270_PA1 => NamedKey::Pa1, - // keysyms::3270_PA2 => NamedKey::Pa2, - // keysyms::3270_PA3 => NamedKey::Pa3, - // keysyms::3270_Test => NamedKey::Test, - keysyms::_3270_Attn => NamedKey::Attn, - // keysyms::3270_CursorBlink => NamedKey::CursorBlink, - // keysyms::3270_AltCursor => NamedKey::AltCursor, - // keysyms::3270_KeyClick => NamedKey::KeyClick, - // keysyms::3270_Jump => NamedKey::Jump, - // keysyms::3270_Ident => NamedKey::Ident, - // keysyms::3270_Rule => NamedKey::Rule, - // keysyms::3270_Copy => NamedKey::Copy, - keysyms::_3270_Play => NamedKey::Play, - // keysyms::3270_Setup => NamedKey::Setup, - // keysyms::3270_Record => NamedKey::Record, - // keysyms::3270_ChangeScreen => NamedKey::ChangeScreen, - // keysyms::3270_DeleteWord => NamedKey::DeleteWord, - keysyms::_3270_ExSelect => NamedKey::ExSel, - keysyms::_3270_CursorSelect => NamedKey::CrSel, - keysyms::_3270_PrintScreen => NamedKey::PrintScreen, - keysyms::_3270_Enter => NamedKey::Enter, - - keysyms::space => NamedKey::Space, - // exclam..Sinh_kunddaliya - - // XFree86 - // keysyms::XF86_ModeLock => NamedKey::ModeLock, - - // XFree86 - Backlight controls - keysyms::XF86_MonBrightnessUp => NamedKey::BrightnessUp, - keysyms::XF86_MonBrightnessDown => NamedKey::BrightnessDown, - // keysyms::XF86_KbdLightOnOff => NamedKey::LightOnOff, - // keysyms::XF86_KbdBrightnessUp => NamedKey::KeyboardBrightnessUp, - // keysyms::XF86_KbdBrightnessDown => NamedKey::KeyboardBrightnessDown, - - // XFree86 - "Internet" - keysyms::XF86_Standby => NamedKey::Standby, - keysyms::XF86_AudioLowerVolume => NamedKey::AudioVolumeDown, - keysyms::XF86_AudioRaiseVolume => NamedKey::AudioVolumeUp, - keysyms::XF86_AudioPlay => NamedKey::MediaPlay, - keysyms::XF86_AudioStop => NamedKey::MediaStop, - keysyms::XF86_AudioPrev => NamedKey::MediaTrackPrevious, - keysyms::XF86_AudioNext => NamedKey::MediaTrackNext, - keysyms::XF86_HomePage => NamedKey::BrowserHome, - keysyms::XF86_Mail => NamedKey::LaunchMail, - // keysyms::XF86_Start => NamedKey::Start, - keysyms::XF86_Search => NamedKey::BrowserSearch, - keysyms::XF86_AudioRecord => NamedKey::MediaRecord, - - // XFree86 - PDA - keysyms::XF86_Calculator => NamedKey::LaunchApplication2, - // keysyms::XF86_Memo => NamedKey::Memo, - // keysyms::XF86_ToDoList => NamedKey::ToDoList, - keysyms::XF86_Calendar => NamedKey::LaunchCalendar, - keysyms::XF86_PowerDown => NamedKey::Power, - // keysyms::XF86_ContrastAdjust => NamedKey::AdjustContrast, - // keysyms::XF86_RockerUp => NamedKey::RockerUp, - // keysyms::XF86_RockerDown => NamedKey::RockerDown, - // keysyms::XF86_RockerEnter => NamedKey::RockerEnter, - - // XFree86 - More "Internet" - keysyms::XF86_Back => NamedKey::BrowserBack, - keysyms::XF86_Forward => NamedKey::BrowserForward, - // keysyms::XF86_Stop => NamedKey::Stop, - keysyms::XF86_Refresh => NamedKey::BrowserRefresh, - keysyms::XF86_PowerOff => NamedKey::Power, - keysyms::XF86_WakeUp => NamedKey::WakeUp, - keysyms::XF86_Eject => NamedKey::Eject, - keysyms::XF86_ScreenSaver => NamedKey::LaunchScreenSaver, - keysyms::XF86_WWW => NamedKey::LaunchWebBrowser, - keysyms::XF86_Sleep => NamedKey::Standby, - keysyms::XF86_Favorites => NamedKey::BrowserFavorites, - keysyms::XF86_AudioPause => NamedKey::MediaPause, - // keysyms::XF86_AudioMedia => NamedKey::AudioMedia, - keysyms::XF86_MyComputer => NamedKey::LaunchApplication1, - // keysyms::XF86_VendorHome => NamedKey::VendorHome, - // keysyms::XF86_LightBulb => NamedKey::LightBulb, - // keysyms::XF86_Shop => NamedKey::BrowserShop, - // keysyms::XF86_History => NamedKey::BrowserHistory, - // keysyms::XF86_OpenURL => NamedKey::OpenUrl, - // keysyms::XF86_AddFavorite => NamedKey::AddFavorite, - // keysyms::XF86_HotLinks => NamedKey::HotLinks, - // keysyms::XF86_BrightnessAdjust => NamedKey::BrightnessAdjust, - // keysyms::XF86_Finance => NamedKey::BrowserFinance, - // keysyms::XF86_Community => NamedKey::BrowserCommunity, - keysyms::XF86_AudioRewind => NamedKey::MediaRewind, - // keysyms::XF86_BackForward => Key::???, - // XF86_Launch0..XF86_LaunchF - - // XF86_ApplicationLeft..XF86_CD - keysyms::XF86_Calculater => NamedKey::LaunchApplication2, // Nice typo, libxkbcommon :) - // XF86_Clear - keysyms::XF86_Close => NamedKey::Close, - keysyms::XF86_Copy => NamedKey::Copy, - keysyms::XF86_Cut => NamedKey::Cut, - // XF86_Display..XF86_Documents - keysyms::XF86_Excel => NamedKey::LaunchSpreadsheet, - // XF86_Explorer..XF86iTouch - keysyms::XF86_LogOff => NamedKey::LogOff, - // XF86_Market..XF86_MenuPB - keysyms::XF86_MySites => NamedKey::BrowserFavorites, - keysyms::XF86_New => NamedKey::New, - // XF86_News..XF86_OfficeHome - keysyms::XF86_Open => NamedKey::Open, - // XF86_Option - keysyms::XF86_Paste => NamedKey::Paste, - keysyms::XF86_Phone => NamedKey::LaunchPhone, - // XF86_Q - keysyms::XF86_Reply => NamedKey::MailReply, - keysyms::XF86_Reload => NamedKey::BrowserRefresh, - // XF86_RotateWindows..XF86_RotationKB - keysyms::XF86_Save => NamedKey::Save, - // XF86_ScrollUp..XF86_ScrollClick - keysyms::XF86_Send => NamedKey::MailSend, - keysyms::XF86_Spell => NamedKey::SpellCheck, - keysyms::XF86_SplitScreen => NamedKey::SplitScreenToggle, - // XF86_Support..XF86_User2KB - keysyms::XF86_Video => NamedKey::LaunchMediaPlayer, - // XF86_WheelButton - keysyms::XF86_Word => NamedKey::LaunchWordProcessor, - // XF86_Xfer - keysyms::XF86_ZoomIn => NamedKey::ZoomIn, - keysyms::XF86_ZoomOut => NamedKey::ZoomOut, - - // XF86_Away..XF86_Messenger - keysyms::XF86_WebCam => NamedKey::LaunchWebCam, - keysyms::XF86_MailForward => NamedKey::MailForward, - // XF86_Pictures - keysyms::XF86_Music => NamedKey::LaunchMusicPlayer, - - // XF86_Battery..XF86_UWB - keysyms::XF86_AudioForward => NamedKey::MediaFastForward, - // XF86_AudioRepeat - keysyms::XF86_AudioRandomPlay => NamedKey::RandomToggle, - keysyms::XF86_Subtitle => NamedKey::Subtitle, - keysyms::XF86_AudioCycleTrack => NamedKey::MediaAudioTrack, - // XF86_CycleAngle..XF86_Blue - keysyms::XF86_Suspend => NamedKey::Standby, - keysyms::XF86_Hibernate => NamedKey::Hibernate, - // XF86_TouchpadToggle..XF86_TouchpadOff - keysyms::XF86_AudioMute => NamedKey::AudioVolumeMute, - - // XF86_Switch_VT_1..XF86_Switch_VT_12 - - // XF86_Ungrab..XF86_ClearGrab - keysyms::XF86_Next_VMode => NamedKey::VideoModeNext, - // keysyms::XF86_Prev_VMode => NamedKey::VideoModePrevious, - // XF86_LogWindowTree..XF86_LogGrabInfo - - // SunFA_Grave..SunFA_Cedilla - - // keysyms::SunF36 => NamedKey::F36 | NamedKey::F11, - // keysyms::SunF37 => NamedKey::F37 | NamedKey::F12, - - // keysyms::SunSys_Req => NamedKey::PrintScreen, - // The next couple of xkb (until SunStop) are already handled. - // SunPrint_Screen..SunPageDown - - // SunUndo..SunFront - keysyms::SUN_Copy => NamedKey::Copy, - keysyms::SUN_Open => NamedKey::Open, - keysyms::SUN_Paste => NamedKey::Paste, - keysyms::SUN_Cut => NamedKey::Cut, - - // SunPowerSwitch - keysyms::SUN_AudioLowerVolume => NamedKey::AudioVolumeDown, - keysyms::SUN_AudioMute => NamedKey::AudioVolumeMute, - keysyms::SUN_AudioRaiseVolume => NamedKey::AudioVolumeUp, - // SUN_VideoDegauss - keysyms::SUN_VideoLowerBrightness => NamedKey::BrightnessDown, - keysyms::SUN_VideoRaiseBrightness => NamedKey::BrightnessUp, - // SunPowerSwitchShift - 0 => return Key::Unidentified(NativeKey::Unidentified), - _ => return Key::Unidentified(NativeKey::Xkb(keysym)), - }) -} - -pub fn keysym_location(keysym: u32) -> KeyLocation { - use xkbcommon_dl::keysyms; - match keysym { - keysyms::Shift_L - | keysyms::Control_L - | keysyms::Meta_L - | keysyms::Alt_L - | keysyms::Super_L - | keysyms::Hyper_L => KeyLocation::Left, - keysyms::Shift_R - | keysyms::Control_R - | keysyms::Meta_R - | keysyms::Alt_R - | keysyms::Super_R - | keysyms::Hyper_R => KeyLocation::Right, - keysyms::KP_0 - | keysyms::KP_1 - | keysyms::KP_2 - | keysyms::KP_3 - | keysyms::KP_4 - | keysyms::KP_5 - | keysyms::KP_6 - | keysyms::KP_7 - | keysyms::KP_8 - | keysyms::KP_9 - | keysyms::KP_Space - | keysyms::KP_Tab - | keysyms::KP_Enter - | keysyms::KP_F1 - | keysyms::KP_F2 - | keysyms::KP_F3 - | keysyms::KP_F4 - | keysyms::KP_Home - | keysyms::KP_Left - | keysyms::KP_Up - | keysyms::KP_Right - | keysyms::KP_Down - | keysyms::KP_Page_Up - | keysyms::KP_Page_Down - | keysyms::KP_End - | keysyms::KP_Begin - | keysyms::KP_Insert - | keysyms::KP_Delete - | keysyms::KP_Equal - | keysyms::KP_Multiply - | keysyms::KP_Add - | keysyms::KP_Separator - | keysyms::KP_Subtract - | keysyms::KP_Decimal - | keysyms::KP_Divide => KeyLocation::Numpad, - _ => KeyLocation::Standard, - } -} diff --git a/sessionlockev/src/lib.rs b/sessionlockev/src/lib.rs index c3887de..37a9f42 100644 --- a/sessionlockev/src/lib.rs +++ b/sessionlockev/src/lib.rs @@ -101,10 +101,8 @@ mod events; -pub mod keyboard; -pub mod xkb_keyboard; - -mod keymap; +pub use waycrate_xkbkeycode::keyboard; +pub use waycrate_xkbkeycode::xkb_keyboard; mod strtoshape; diff --git a/sessionlockev/src/xkb_keyboard.rs b/sessionlockev/src/xkb_keyboard.rs deleted file mode 100644 index dfe9b6d..0000000 --- a/sessionlockev/src/xkb_keyboard.rs +++ /dev/null @@ -1,840 +0,0 @@ -use memmap2::MmapOptions; -use smol_str::SmolStr; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::LazyLock; -use std::{ - env, - ffi::{c_char, CString}, - ops::Deref, - os::{fd::OwnedFd, unix::ffi::OsStringExt}, - ptr::{self, NonNull}, - time::Duration, -}; -use wayland_client::{protocol::wl_keyboard::WlKeyboard, Proxy}; - -use crate::keymap; - -use xkbcommon_dl::{ - self as xkb, xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, - xkb_compose_state_flags, xkb_compose_status, xkb_compose_table, xkb_keycode_t, xkb_keysym_t, - xkb_layout_index_t, xkbcommon_compose_handle, xkbcommon_handle, XkbCommon, XkbCommonCompose, -}; - -use crate::keyboard::ModifiersState; -use xkb::{ - xkb_context, xkb_context_flags, xkb_keymap, xkb_keymap_compile_flags, xkb_state, - xkb_state_component, -}; - -use crate::keyboard::{Key, KeyLocation, PhysicalKey}; - -static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false); - -pub static XKBH: LazyLock<&'static XkbCommon> = LazyLock::new(xkbcommon_handle); -pub static XKBCH: LazyLock<&'static XkbCommonCompose> = LazyLock::new(xkbcommon_compose_handle); - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum RepeatInfo { - /// Keys will be repeated at the specified rate and delay. - Repeat { - /// The time between the key repeats. - gap: Duration, - - /// Delay (in milliseconds) between a key press and the start of repetition. - delay: Duration, - }, - - /// Keys should not be repeated. - Disable, -} - -impl Default for RepeatInfo { - /// The default repeat rate is 25 keys per second with the delay of 200ms. - /// - /// The values are picked based on the default in various compositors and Xorg. - fn default() -> Self { - Self::Repeat { - gap: Duration::from_millis(40), - delay: Duration::from_millis(200), - } - } -} - -#[derive(Debug)] -pub struct KeyboardState { - pub keyboard: WlKeyboard, - - pub xkb_context: Context, - pub repeat_info: RepeatInfo, - pub current_repeat: Option, -} - -impl KeyboardState { - pub fn new(keyboard: WlKeyboard) -> Self { - Self { - keyboard, - xkb_context: Context::new().unwrap(), - repeat_info: RepeatInfo::default(), - current_repeat: None, - } - } -} - -impl Drop for KeyboardState { - fn drop(&mut self) { - if self.keyboard.version() >= 3 { - self.keyboard.release(); - } - } -} - -#[derive(Debug)] -pub enum Error { - /// libxkbcommon is not available - XKBNotFound, -} - -#[derive(Debug)] -pub struct Context { - // NOTE: field order matters. - state: Option, - keymap: Option, - compose_state1: Option, - compose_state2: Option, - _compose_table: Option, - context: XkbContext, - scratch_buffer: Vec, -} - -impl Context { - pub fn new() -> Result { - if xkb::xkbcommon_option().is_none() { - return Err(Error::XKBNotFound); - } - - let context = XkbContext::new(); - let mut compose_table = XkbComposeTable::new(&context); - let mut compose_state1 = compose_table.as_ref().and_then(|table| table.new_state()); - let mut compose_state2 = compose_table.as_ref().and_then(|table| table.new_state()); - - // Disable compose if anything compose related failed to initialize. - if compose_table.is_none() || compose_state1.is_none() || compose_state2.is_none() { - compose_state2 = None; - compose_state1 = None; - compose_table = None; - } - - Ok(Self { - state: None, - keymap: None, - compose_state1, - compose_state2, - _compose_table: compose_table, - context, - scratch_buffer: Vec::with_capacity(8), - }) - } - pub fn set_keymap_from_fd(&mut self, fd: OwnedFd, size: usize) { - let keymap = XkbKeymap::from_fd(&self.context, fd, size); - let state = keymap.as_ref().and_then(XkbState::new_wayland); - if keymap.is_none() || state.is_none() { - log::warn!("failed to update xkb keymap"); - } - self.state = state; - self.keymap = keymap; - } - - pub fn state_mut(&mut self) -> Option<&mut XkbState> { - self.state.as_mut() - } - /// Key builder context with the user provided xkb state. - pub fn key_context(&mut self) -> Option> { - let state = self.state.as_mut()?; - let keymap = self.keymap.as_mut()?; - let compose_state1 = self.compose_state1.as_mut(); - let compose_state2 = self.compose_state2.as_mut(); - let scratch_buffer = &mut self.scratch_buffer; - Some(KeyContext { - state, - keymap, - compose_state1, - compose_state2, - scratch_buffer, - }) - } -} - -#[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 }) - } - - pub fn first_keysym_by_level( - &mut self, - layout: xkb_layout_index_t, - keycode: xkb_keycode_t, - ) -> xkb_keysym_t { - unsafe { - let mut keysyms = ptr::null(); - let count = (XKBH.xkb_keymap_key_get_syms_by_level)( - self.keymap.as_ptr(), - keycode, - layout, - // NOTE: The level should be zero to ignore modifiers. - 0, - &mut keysyms, - ); - - if count == 1 { - *keysyms - } else { - 0 - } - } - } -} - -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: ModifiersStateXkb, -} - -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 = ModifiersStateXkb::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 - } - } - pub fn modifiers(&self) -> ModifiersStateXkb { - self.modifiers - } - pub fn update_modifiers( - &mut self, - mods_depressed: u32, - mods_latched: u32, - mods_locked: u32, - depressed_group: u32, - latched_group: u32, - locked_group: u32, - ) { - let mask = unsafe { - (XKBH.xkb_state_update_mask)( - self.state.as_ptr(), - mods_depressed, - mods_latched, - mods_locked, - depressed_group, - latched_group, - locked_group, - ) - }; - - if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) { - // Effective value of mods have changed, we need to update our state. - self.reload_modifiers(); - } - } - - 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); - 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); - } - - pub fn get_one_sym_raw(&mut self, keycode: xkb_keycode_t) -> xkb_keysym_t { - unsafe { (XKBH.xkb_state_key_get_one_sym)(self.state.as_ptr(), keycode) } - } - - pub fn layout(&mut self, key: xkb_keycode_t) -> xkb_layout_index_t { - unsafe { (XKBH.xkb_state_key_get_layout)(self.state.as_ptr(), key) } - } - - pub fn get_utf8_raw( - &mut self, - keycode: xkb_keycode_t, - scratch_buffer: &mut Vec, - ) -> Option { - make_string_with(scratch_buffer, |ptr, len| unsafe { - (XKBH.xkb_state_key_get_utf8)(self.state.as_ptr(), keycode, ptr, len) - }) - } -} - -#[derive(Debug, Default, Clone, Copy)] -pub struct ModifiersStateXkb { - ctrl: bool, - alt: bool, - shift: bool, - caps_lock: bool, - logo: bool, - num_lock: bool, -} - -impl From for ModifiersState { - fn from(mods: ModifiersStateXkb) -> ModifiersState { - let mut to_mods = ModifiersState::empty(); - to_mods.set(ModifiersState::SHIFT, mods.shift); - to_mods.set(ModifiersState::CONTROL, mods.ctrl); - to_mods.set(ModifiersState::ALT, mods.alt); - to_mods.set(ModifiersState::SUPER, mods.logo); - to_mods - } -} - -#[derive(Debug)] -pub struct XkbComposeTable { - table: NonNull, -} - -impl XkbComposeTable { - pub fn new(context: &XkbContext) -> Option { - 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 table = unsafe { - (XKBCH.xkb_compose_table_new_from_locale)( - context.as_ptr(), - locale.as_ptr(), - xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS, - ) - }; - - let table = NonNull::new(table)?; - Some(Self { table }) - } - - /// Create new state with the given compose table. - pub fn new_state(&self) -> Option { - let state = unsafe { - (XKBCH.xkb_compose_state_new)( - self.table.as_ptr(), - xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, - ) - }; - - let state = NonNull::new(state)?; - Some(XkbComposeState { state }) - } -} - -impl Deref for XkbComposeTable { - type Target = NonNull; - - fn deref(&self) -> &Self::Target { - &self.table - } -} - -impl Drop for XkbComposeTable { - fn drop(&mut self) { - unsafe { - (XKBCH.xkb_compose_table_unref)(self.table.as_ptr()); - } - } -} - -#[derive(Debug)] -pub struct XkbComposeState { - state: NonNull, -} - -// NOTE: This is track_caller so we can have more informative line numbers when logging -#[track_caller] -fn byte_slice_to_smol_str(bytes: &[u8]) -> Option { - std::str::from_utf8(bytes) - .map(SmolStr::new) - .map_err(|e| { - log::warn!( - "UTF-8 received from libxkbcommon ({:?}) was invalid: {e}", - bytes - ) - }) - .ok() -} - -/// Shared logic for constructing a string with `xkb_compose_state_get_utf8` and -/// `xkb_state_key_get_utf8`. -fn make_string_with(scratch_buffer: &mut Vec, mut f: F) -> Option -where - F: FnMut(*mut c_char, usize) -> i32, -{ - let size = f(ptr::null_mut(), 0); - if size == 0 { - return None; - } - let size = usize::try_from(size).unwrap(); - scratch_buffer.clear(); - // The allocated buffer must include space for the null-terminator. - scratch_buffer.reserve(size + 1); - unsafe { - let written = f( - scratch_buffer.as_mut_ptr().cast(), - scratch_buffer.capacity(), - ); - if usize::try_from(written).unwrap() != size { - // This will likely never happen. - return None; - } - scratch_buffer.set_len(size); - }; - - byte_slice_to_smol_str(scratch_buffer) -} - -impl XkbComposeState { - pub fn get_string(&mut self, scratch_buffer: &mut Vec) -> Option { - make_string_with(scratch_buffer, |ptr, len| unsafe { - (XKBCH.xkb_compose_state_get_utf8)(self.state.as_ptr(), ptr, len) - }) - } - - #[inline] - pub fn feed(&mut self, keysym: xkb_keysym_t) -> ComposeStatus { - let feed_result = unsafe { (XKBCH.xkb_compose_state_feed)(self.state.as_ptr(), keysym) }; - match feed_result { - xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED => ComposeStatus::Ignored, - xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED => { - ComposeStatus::Accepted(self.status()) - } - } - } - - #[inline] - pub fn reset(&mut self) { - unsafe { - (XKBCH.xkb_compose_state_reset)(self.state.as_ptr()); - } - } - - #[inline] - pub fn status(&mut self) -> xkb_compose_status { - unsafe { (XKBCH.xkb_compose_state_get_status)(self.state.as_ptr()) } - } -} - -impl Drop for XkbComposeState { - fn drop(&mut self) { - unsafe { - (XKBCH.xkb_compose_state_unref)(self.state.as_ptr()); - }; - } -} - -#[derive(Copy, Clone, Debug)] -pub enum ComposeStatus { - Accepted(xkb_compose_status), - Ignored, - None, -} - -pub struct KeyContext<'a> { - pub state: &'a mut XkbState, - pub keymap: &'a mut XkbKeymap, - compose_state1: Option<&'a mut XkbComposeState>, - compose_state2: Option<&'a mut XkbComposeState>, - scratch_buffer: &'a mut Vec, -} - -#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] -pub enum ElementState { - Pressed, - Released, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct KeyEventExtra { - pub text_with_all_modifiers: Option, - pub key_without_modifiers: Key, -} - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct KeyEvent { - /// Represents the position of a key independent of the currently active layout. - /// - /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode). - /// The most prevalent use case for this is games. For example the default keys for the player - /// to move around might be the W, A, S, and D keys on a US layout. The position of these keys - /// is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY" - /// layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.) - /// - /// ## Caveats - /// - /// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that - /// implements DVORAK in hardware (or firmware) - /// - Your application will likely have to handle keyboards which are missing keys that your - /// own keyboard has. - /// - Certain `KeyCode`s will move between a couple of different positions depending on what - /// layout the keyboard was manufactured to support. - /// - /// **Because of these caveats, it is important that you provide users with a way to configure - /// most (if not all) keybinds in your application.** - /// - /// ## `Fn` and `FnLock` - /// - /// `Fn` and `FnLock` key events are *exceedingly unlikely* to be emitted by Winit. These keys - /// are usually handled at the hardware or OS level, and aren't surfaced to applications. If - /// you somehow see this in the wild, we'd like to know :) - pub physical_key: PhysicalKey, - - /// This value is affected by all modifiers except Ctrl. - /// - /// This has two use cases: - /// - Allows querying whether the current input is a Dead key. - /// - Allows handling key-bindings on platforms which don't - /// support [`key_without_modifiers`]. - /// - /// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard - /// shortcuts, **it is important that you provide users with a way to configure your - /// application's shortcuts so you don't render your application unusable for users with an - /// incompatible keyboard layout.** - /// - /// ## Platform-specific - /// - **Web:** Dead keys might be reported as the real key instead - /// of `Dead` depending on the browser/OS. - /// - /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers - pub logical_key: Key, - - /// Contains the text produced by this keypress. - /// - /// In most cases this is identical to the content - /// of the `Character` variant of `logical_key`. - /// However, on Windows when a dead key was pressed earlier - /// but cannot be combined with the character from this - /// keypress, the produced text will consist of two characters: - /// the dead-key-character followed by the character resulting - /// from this keypress. - /// - /// An additional difference from `logical_key` is that - /// this field stores the text representation of any key - /// that has such a representation. For example when - /// `logical_key` is `Key::Named(NamedKey::Enter)`, this field is `Some("\r")`. - /// - /// This is `None` if the current keypress cannot - /// be interpreted as text. - /// - /// See also: `text_with_all_modifiers()` - pub text: Option, - - /// Contains the location of this key on the keyboard. - /// - /// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" - /// key appears on the left side of the QWERTY keyboard as well as the right side. However, - /// both keys have the same symbolic value. Another example of this phenomenon is the "1" - /// key, which appears both above the "Q" key and as the "Keypad 1" key. - /// - /// This field allows the user to differentiate between keys like this that have the same - /// symbolic value but different locations on the keyboard. - /// - /// See the [`KeyLocation`] type for more details. - /// - /// [`KeyLocation`]: crate::keyboard::KeyLocation - pub location: KeyLocation, - - /// Whether the key is being pressed or released. - /// - /// See the [`ElementState`] type for more details. - pub state: ElementState, - - /// Whether or not this key is a key repeat event. - /// - /// On some systems, holding down a key for some period of time causes that key to be repeated - /// as though it were being pressed and released repeatedly. This field is `true` if and only - /// if this event is the result of one of those repeats. - /// - pub repeat: bool, - - /// Platform-specific key event information. - /// - /// On Windows, Linux and macOS, this type contains the key without modifiers and the text with - /// all modifiers applied. - /// - /// On Android, iOS, Redox and Web, this type is a no-op. - pub(crate) platform_specific: KeyEventExtra, -} - -impl KeyEvent { - #[inline] - pub fn text_with_all_modifiers(&self) -> Option<&str> { - self.platform_specific - .text_with_all_modifiers - .as_ref() - .map(|s| s.as_str()) - } - - #[inline] - pub fn key_without_modifiers(&self) -> Key { - self.platform_specific.key_without_modifiers.clone() - } -} - -impl<'a> KeyContext<'a> { - pub fn process_key_event( - &mut self, - keycode: u32, - state: ElementState, - repeat: bool, - ) -> KeyEvent { - let mut event = - KeyEventResults::new(self, keycode, !repeat && state == ElementState::Pressed); - let physical_key = keymap::raw_keycode_to_physicalkey(keycode); - let (logical_key, location) = event.key(); - let text = event.text(); - let (key_without_modifiers, _) = event.key_without_modifiers(); - let text_with_all_modifiers = event.text_with_all_modifiers(); - - let platform_specific = KeyEventExtra { - text_with_all_modifiers, - key_without_modifiers, - }; - - KeyEvent { - physical_key, - logical_key, - text, - location, - state, - repeat, - platform_specific, - } - } - - fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option { - self.scratch_buffer.clear(); - self.scratch_buffer.reserve(8); - loop { - let bytes_written = unsafe { - (XKBH.xkb_keysym_to_utf8)( - keysym, - self.scratch_buffer.as_mut_ptr().cast(), - self.scratch_buffer.capacity(), - ) - }; - if bytes_written == 0 { - return None; - } else if bytes_written == -1 { - self.scratch_buffer.reserve(8); - } else { - unsafe { - self.scratch_buffer - .set_len(bytes_written.try_into().unwrap()) - }; - break; - } - } - - // Remove the null-terminator - self.scratch_buffer.pop(); - byte_slice_to_smol_str(self.scratch_buffer) - } -} - -struct KeyEventResults<'a, 'b> { - context: &'a mut KeyContext<'b>, - keycode: u32, - keysym: u32, - compose: ComposeStatus, -} - -impl<'a, 'b> KeyEventResults<'a, 'b> { - fn new(context: &'a mut KeyContext<'b>, keycode: u32, compose: bool) -> Self { - let keysym = context.state.get_one_sym_raw(keycode); - - let compose = if let Some(state) = context.compose_state1.as_mut().filter(|_| compose) { - if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) { - state.reset(); - context.compose_state2.as_mut().unwrap().reset(); - } - state.feed(keysym) - } else { - ComposeStatus::None - }; - - KeyEventResults { - context, - keycode, - keysym, - compose, - } - } - - pub fn key(&mut self) -> (Key, KeyLocation) { - let (key, location) = match self.keysym_to_key(self.keysym) { - Ok(known) => return known, - Err(undefined) => undefined, - }; - - if let ComposeStatus::Accepted(xkb_compose_status::XKB_COMPOSE_COMPOSING) = self.compose { - let compose_state = self.context.compose_state2.as_mut().unwrap(); - // When pressing a dead key twice, the non-combining variant of that character will - // be produced. Since this function only concerns itself with a single keypress, we - // simulate this double press here by feeding the keysym to the compose state - // twice. - - compose_state.feed(self.keysym); - if matches!(compose_state.feed(self.keysym), ComposeStatus::Accepted(_)) { - // Extracting only a single `char` here *should* be fine, assuming that no - // dead key's non-combining variant ever occupies more than one `char`. - let text = compose_state.get_string(self.context.scratch_buffer); - let key = Key::Dead(text.and_then(|s| s.chars().next())); - (key, location) - } else { - (key, location) - } - } else { - let key = self - .composed_text() - .unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym)) - .map(Key::Character) - .unwrap_or(key); - (key, location) - } - } - - pub fn key_without_modifiers(&mut self) -> (Key, KeyLocation) { - // This will become a pointer to an array which libxkbcommon owns, so we don't need to - // deallocate it. - let layout = self.context.state.layout(self.keycode); - let keysym = self - .context - .keymap - .first_keysym_by_level(layout, self.keycode); - - match self.keysym_to_key(keysym) { - Ok((key, location)) => (key, location), - Err((key, location)) => { - let key = self - .context - .keysym_to_utf8_raw(keysym) - .map(Key::Character) - .unwrap_or(key); - (key, location) - } - } - } - - fn keysym_to_key(&self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> { - let location = keymap::keysym_location(keysym); - let key = keymap::keysym_to_key(keysym); - if matches!(key, Key::Unidentified(_)) { - Err((key, location)) - } else { - Ok((key, location)) - } - } - - pub fn text(&mut self) -> Option { - self.composed_text() - .unwrap_or_else(|_| self.context.keysym_to_utf8_raw(self.keysym)) - } - - // The current behaviour makes it so composing a character overrides attempts to input a - // control character with the `Ctrl` key. We can potentially add a configuration option - // if someone specifically wants the oppsite behaviour. - pub fn text_with_all_modifiers(&mut self) -> Option { - match self.composed_text() { - Ok(text) => text, - Err(_) => self - .context - .state - .get_utf8_raw(self.keycode, self.context.scratch_buffer), - } - } - - fn composed_text(&mut self) -> Result, ()> { - match self.compose { - ComposeStatus::Accepted(status) => match status { - xkb_compose_status::XKB_COMPOSE_COMPOSED => { - let state = self.context.compose_state1.as_mut().unwrap(); - Ok(state.get_string(self.context.scratch_buffer)) - } - xkb_compose_status::XKB_COMPOSE_COMPOSING - | xkb_compose_status::XKB_COMPOSE_CANCELLED => Ok(None), - xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()), - }, - _ => Err(()), - } - } -} diff --git a/waycrate_xkbkeycode/Cargo.toml b/waycrate_xkbkeycode/Cargo.toml new file mode 100644 index 0000000..66279ef --- /dev/null +++ b/waycrate_xkbkeycode/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "waycrate_xkbkeycode" +authors.workspace = true +edition.workspace = true +version.workspace = true +license.workspace = true +repository.workspace = true +description.workspace = true +keywords.workspace = true +readme.workspace = true + +[dependencies] +xkbcommon-dl.workspace = true +smol_str.workspace = true +memmap2.workspace = true +bitflags.workspace = true +wayland-client.workspace = true +wayland-backend.workspace = true +log.workspace = true diff --git a/layershellev/src/keyboard.rs b/waycrate_xkbkeycode/src/keyboard.rs similarity index 100% rename from layershellev/src/keyboard.rs rename to waycrate_xkbkeycode/src/keyboard.rs diff --git a/layershellev/src/keymap.rs b/waycrate_xkbkeycode/src/keymap.rs similarity index 100% rename from layershellev/src/keymap.rs rename to waycrate_xkbkeycode/src/keymap.rs diff --git a/waycrate_xkbkeycode/src/lib.rs b/waycrate_xkbkeycode/src/lib.rs new file mode 100644 index 0000000..b511212 --- /dev/null +++ b/waycrate_xkbkeycode/src/lib.rs @@ -0,0 +1,3 @@ +pub mod keymap; +pub mod keyboard; +pub mod xkb_keyboard; diff --git a/layershellev/src/xkb_keyboard.rs b/waycrate_xkbkeycode/src/xkb_keyboard.rs similarity index 100% rename from layershellev/src/xkb_keyboard.rs rename to waycrate_xkbkeycode/src/xkb_keyboard.rs From 30aca21f28229ca922caeb8d3689022f936c1b4f Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 30 Jul 2024 22:44:18 +0859 Subject: [PATCH 14/16] fix: format --- waycrate_xkbkeycode/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/waycrate_xkbkeycode/src/lib.rs b/waycrate_xkbkeycode/src/lib.rs index b511212..4ffd391 100644 --- a/waycrate_xkbkeycode/src/lib.rs +++ b/waycrate_xkbkeycode/src/lib.rs @@ -1,3 +1,3 @@ -pub mod keymap; pub mod keyboard; +pub mod keymap; pub mod xkb_keyboard; From 33fd1c6948b80a99913295618309b80a1b1defee Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Wed, 31 Jul 2024 01:00:38 +0859 Subject: [PATCH 15/16] chore: little format --- Cargo.toml | 1 - _typos.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ca6ba97..9f5031a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ members = [ "sessionlockev", "waycrate_xkbkeycode", "iced_examples/*", - ] [workspace.package] diff --git a/_typos.toml b/_typos.toml index d70e89c..c712db0 100644 --- a/_typos.toml +++ b/_typos.toml @@ -2,5 +2,4 @@ extend-exclude = ["**/Cargo.lock"] [default.extend-words] -# Don't correct the surname "Teh" Calculater = "Calculater" From e2e4881b267cc1a1142d0353a1c5ef311f382325 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Wed, 31 Jul 2024 10:18:19 +0859 Subject: [PATCH 16/16] chore: update modifiers --- iced_layershell/src/application.rs | 7 ++----- iced_layershell/src/application/state.rs | 3 +++ iced_layershell/src/conversion.rs | 2 +- iced_layershell/src/multi_window/state.rs | 3 +++ iced_sessionlock/src/multi_window/state.rs | 3 +++ 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/iced_layershell/src/application.rs b/iced_layershell/src/application.rs index 0196e45..622d507 100644 --- a/iced_layershell/src/application.rs +++ b/iced_layershell/src/application.rs @@ -194,11 +194,8 @@ where // TODO: maybe use it later LayerEvent::BindProvide(_, _) => {} LayerEvent::RequestMessages(message) => { - match message { - DispatchMessage::MouseEnter { serial, .. } => { - pointer_serial = *serial; - } - _ => {} + if let DispatchMessage::MouseEnter { serial, .. } = message { + pointer_serial = *serial; } event_sender diff --git a/iced_layershell/src/application/state.rs b/iced_layershell/src/application/state.rs index bf98efe..b69fdc0 100644 --- a/iced_layershell/src/application/state.rs +++ b/iced_layershell/src/application/state.rs @@ -97,6 +97,9 @@ where WindowEvent::CursorMoved { x, y } => { self.mouse_position = Some(Point::new(*x as f32, *y as f32)); } + WindowEvent::ModifiersChanged(modifiers) => { + self.modifiers = *modifiers; + } _ => {} } } diff --git a/iced_layershell/src/conversion.rs b/iced_layershell/src/conversion.rs index d3ccdcc..4cfd1ca 100644 --- a/iced_layershell/src/conversion.rs +++ b/iced_layershell/src/conversion.rs @@ -73,7 +73,7 @@ pub fn window_event( } })), LayerShellEvent::ModifiersChanged(new_modifiers) => Some(IcedEvent::Keyboard( - keyboard::Event::ModifiersChanged(keymap::modifiers(new_modifiers.clone())), + keyboard::Event::ModifiersChanged(keymap::modifiers(*new_modifiers)), )), _ => None, } diff --git a/iced_layershell/src/multi_window/state.rs b/iced_layershell/src/multi_window/state.rs index 910f68f..697ce28 100644 --- a/iced_layershell/src/multi_window/state.rs +++ b/iced_layershell/src/multi_window/state.rs @@ -95,6 +95,9 @@ where WindowEvent::CursorMoved { x, y } => { self.mouse_position = Some(Point::new(*x as f32, *y as f32)); } + WindowEvent::ModifiersChanged(modifiers) => { + self.modifiers = *modifiers; + } _ => {} } } diff --git a/iced_sessionlock/src/multi_window/state.rs b/iced_sessionlock/src/multi_window/state.rs index 8041624..66d1d6a 100644 --- a/iced_sessionlock/src/multi_window/state.rs +++ b/iced_sessionlock/src/multi_window/state.rs @@ -95,6 +95,9 @@ where WindowEvent::CursorMoved { x, y } => { self.mouse_position = Some(Point::new(*x as f32, *y as f32)); } + WindowEvent::ModifiersChanged(modifiers) => { + self.modifiers = modifiers.clone(); + } _ => {} } }