#![allow(non_upper_case_globals)]
#![deprecated = "the `xkeysym` crate implements this crate without any cruft"]
#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
#![no_std]
use breadx::{
display::Cookie,
prelude::*,
protocol::xproto::{GetKeyboardMappingReply, Keycode, Keysym, Setup},
Error, Result,
};
use keysyms::*;
const NO_SYMBOL: Keysym = 0;
#[path = "automatically_generated.rs"]
pub mod keysyms;
pub struct KeyboardState {
innards: Innards,
}
enum Innards {
Unresolved(Cookie<GetKeyboardMappingReply>),
Resolved(GetKeyboardMappingReply),
}
impl KeyboardState {
pub fn new(dpy: &mut impl Display) -> Result<Self> {
let min_keycode = dpy.setup().min_keycode;
let max_keycode = dpy.setup().max_keycode;
let cookie = dpy.get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?;
Ok(Self {
innards: Innards::Unresolved(cookie),
})
}
#[cfg(feature = "async")]
pub async fn new_async(dpy: &mut impl AsyncDisplay) -> Result<Self> {
let min_keycode = dpy.setup().min_keycode;
let max_keycode = dpy.setup().max_keycode;
let cookie = dpy
.get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)
.await?;
Ok(Self {
innards: Innards::Unresolved(cookie),
})
}
fn resolve(&mut self, dpy: &mut impl Display) -> Result<&mut GetKeyboardMappingReply> {
match self.innards {
Innards::Unresolved(ref cookie) => {
let reply = dpy.wait_for_reply(*cookie)?;
self.innards = Innards::Resolved(reply);
match &mut self.innards {
Innards::Resolved(reply) => Ok(reply),
_ => unreachable!(),
}
}
Innards::Resolved(ref mut reply) => Ok(reply),
}
}
#[cfg(feature = "async")]
async fn resolve_async(
&mut self,
dpy: &mut impl AsyncDisplay,
) -> Result<&mut GetKeyboardMappingReply> {
match self.innards {
Innards::Unresolved(ref cookie) => {
let reply = dpy.wait_for_reply(*cookie).await?;
self.innards = Innards::Resolved(reply);
match &mut self.innards {
Innards::Resolved(reply) => Ok(reply),
_ => unreachable!(),
}
}
Innards::Resolved(ref mut reply) => Ok(reply),
}
}
pub fn refresh(&mut self, dpy: &mut impl Display) -> Result<()> {
let min_keycode = dpy.setup().min_keycode;
let max_keycode = dpy.setup().max_keycode;
let cookie = dpy.get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)?;
self.innards = Innards::Unresolved(cookie);
Ok(())
}
#[cfg(feature = "async")]
pub async fn refresh_async(&mut self, dpy: &mut impl AsyncDisplay) -> Result<()> {
let min_keycode = dpy.setup().min_keycode;
let max_keycode = dpy.setup().max_keycode;
let cookie = dpy
.get_keyboard_mapping(min_keycode, max_keycode - min_keycode + 1)
.await?;
self.innards = Innards::Unresolved(cookie);
Ok(())
}
pub fn symbol(
&mut self,
dpy: &mut impl Display,
keycode: Keycode,
column: u8,
) -> Result<Keysym> {
let reply = self.resolve(dpy)?;
get_symbol(dpy.setup(), reply, keycode, column)
}
#[cfg(feature = "async")]
pub async fn symbol_async(
&mut self,
dpy: &mut impl AsyncDisplay,
keycode: Keycode,
column: u8,
) -> Result<Keysym> {
let reply = self.resolve_async(dpy).await?;
get_symbol(dpy.setup(), reply, keycode, column)
}
}
fn get_symbol(
setup: &Setup,
mapping: &GetKeyboardMappingReply,
keycode: Keycode,
mut column: u8,
) -> Result<Keysym> {
let mut per = mapping.keysyms_per_keycode;
if column >= per && column > 3 {
return Err(Error::make_msg("Invalid column"));
}
let start = (keycode - setup.min_keycode) as usize * per as usize;
let end = start + per as usize;
let keysyms = &mapping.keysyms[start..end];
if column < 4 {
if column > 1 {
while per > 2 && keysyms[per as usize - 1] == NO_SYMBOL {
per -= 1;
}
if per < 3 {
column -= 2;
}
}
if per <= column | 1 || keysyms[column as usize | 1] == NO_SYMBOL {
let (upper, lower) = convert_case(keysyms[column as usize & !1]);
if column & 1 == 0 {
return Ok(lower);
} else {
return Ok(upper);
}
}
}
Ok(keysyms[column as usize])
}
pub fn is_keypad_key(keysym: Keysym) -> bool {
matches!(keysym, KEY_KP_Space..=KEY_KP_Equal)
}
pub fn is_private_keypad_key(keysym: Keysym) -> bool {
matches!(keysym, 0x11000000..=0x1100FFFF)
}
pub fn is_cursor_key(keysym: Keysym) -> bool {
matches!(keysym, KEY_Home..=KEY_Select)
}
pub fn is_pf_key(keysym: Keysym) -> bool {
matches!(keysym, KEY_KP_F1..=KEY_KP_F4)
}
pub fn is_function_key(keysym: Keysym) -> bool {
matches!(keysym, KEY_F1..=KEY_F35)
}
pub fn is_misc_function_key(keysym: Keysym) -> bool {
matches!(keysym, KEY_Select..=KEY_Break)
}
pub fn is_modifier_key(keysym: Keysym) -> bool {
matches!(
keysym,
KEY_Shift_L..=KEY_Hyper_R
| KEY_ISO_Lock..=KEY_ISO_Level5_Lock
| KEY_Mode_switch
| KEY_Num_Lock
)
}
fn convert_case(keysym: Keysym) -> (Keysym, Keysym) {
let (mut upper, mut lower) = (keysym, keysym);
#[allow(non_upper_case_globals)]
match keysym {
KEY_A..=KEY_Z => lower += KEY_a - KEY_A,
KEY_a..=KEY_z => upper -= KEY_a - KEY_A,
KEY_Agrave..=KEY_Odiaeresis => lower += KEY_agrave - KEY_Agrave,
KEY_agrave..=KEY_odiaeresis => upper -= KEY_agrave - KEY_Agrave,
KEY_Ooblique..=KEY_Thorn => lower += KEY_oslash - KEY_Ooblique,
KEY_oslash..=KEY_thorn => upper -= KEY_oslash - KEY_Ooblique,
KEY_Aogonek => lower = KEY_aogonek,
KEY_aogonek => upper = KEY_Aogonek,
KEY_Lstroke..=KEY_Sacute => lower += KEY_lstroke - KEY_Lstroke,
KEY_lstroke..=KEY_sacute => upper -= KEY_lstroke - KEY_Lstroke,
KEY_Scaron..=KEY_Zacute => lower += KEY_scaron - KEY_Scaron,
KEY_scaron..=KEY_zacute => upper -= KEY_scaron - KEY_Scaron,
KEY_Zcaron..=KEY_Zabovedot => lower += KEY_zcaron - KEY_Zcaron,
KEY_zcaron..=KEY_zabovedot => upper -= KEY_zcaron - KEY_Zcaron,
KEY_Racute..=KEY_Tcedilla => lower += KEY_racute - KEY_Racute,
KEY_racute..=KEY_tcedilla => upper -= KEY_racute - KEY_Racute,
KEY_Hstroke..=KEY_Hcircumflex => lower += KEY_hstroke - KEY_Hstroke,
KEY_hstroke..=KEY_hcircumflex => upper -= KEY_hstroke - KEY_Hstroke,
KEY_Gbreve..=KEY_Jcircumflex => lower += KEY_gbreve - KEY_Gbreve,
KEY_gbreve..=KEY_jcircumflex => upper -= KEY_gbreve - KEY_Gbreve,
KEY_Cabovedot..=KEY_Scircumflex => lower += KEY_cabovedot - KEY_Cabovedot,
KEY_cabovedot..=KEY_scircumflex => upper -= KEY_cabovedot - KEY_Cabovedot,
KEY_Rcedilla..=KEY_Tslash => lower += KEY_rcedilla - KEY_Rcedilla,
KEY_rcedilla..=KEY_tslash => upper -= KEY_rcedilla - KEY_Rcedilla,
KEY_ENG => lower = KEY_eng,
KEY_eng => upper = KEY_ENG,
KEY_Amacron..=KEY_Umacron => lower += KEY_amacron - KEY_Amacron,
KEY_amacron..=KEY_umacron => upper -= KEY_amacron - KEY_Amacron,
KEY_Serbian_DJE..=KEY_Serbian_DZE => lower -= KEY_Serbian_DJE - KEY_Serbian_dje,
KEY_Serbian_dje..=KEY_Serbian_dze => upper += KEY_Serbian_DJE - KEY_Serbian_dje,
KEY_Cyrillic_YU..=KEY_Cyrillic_HARDSIGN => lower -= KEY_Cyrillic_YU - KEY_Cyrillic_yu,
KEY_Cyrillic_yu..=KEY_Cyrillic_hardsign => upper += KEY_Cyrillic_YU - KEY_Cyrillic_yu,
KEY_Greek_ALPHAaccent..=KEY_Greek_OMEGAaccent => {
lower += KEY_Greek_alphaaccent - KEY_Greek_ALPHAaccent
}
KEY_Greek_alphaaccent..=KEY_Greek_omegaaccent
if !matches!(
keysym,
KEY_Greek_iotaaccentdieresis | KEY_Greek_upsilonaccentdieresis
) =>
{
upper -= KEY_Greek_alphaaccent - KEY_Greek_ALPHAaccent
}
KEY_Greek_ALPHA..=KEY_Greek_OMEGA => lower += KEY_Greek_alpha - KEY_Greek_ALPHA,
KEY_Greek_alpha..=KEY_Greek_omega if !matches!(keysym, KEY_Greek_finalsmallsigma) => {
upper -= KEY_Greek_alpha - KEY_Greek_ALPHA
}
KEY_Armenian_AYB..=KEY_Armenian_fe => {
lower |= 1;
upper &= !1;
}
_ => {}
}
(upper, lower)
}