use winit::keyboard::{Key, KeyLocation, NamedKey, NativeKey, PhysicalKey};
use super::KeyboardState;
pub(super) const VOID_SYMBOL: u32 = 0x00ff_ffff;
const SHIFT_MASK: u32 = 1 << 0;
const LOCK_MASK: u32 = 1 << 1;
const CONTROL_MASK: u32 = 1 << 2;
const MOD1_MASK: u32 = 1 << 3;
const MOD2_MASK: u32 = 1 << 4;
const MOD4_MASK: u32 = 1 << 6;
pub(super) fn keysym(key: &Key, location: KeyLocation, shift: bool) -> u32 {
match key {
Key::Named(named) => named_keysym(*named, location, shift),
Key::Character(text) => text
.chars()
.next()
.map_or(VOID_SYMBOL, |ch| character_keysym(ch, location)),
Key::Dead(ch) => ch.and_then(dead_keysym).unwrap_or(VOID_SYMBOL),
Key::Unidentified(NativeKey::Xkb(raw)) => *raw,
Key::Unidentified(_) => VOID_SYMBOL,
}
}
fn named_keysym(key: NamedKey, location: KeyLocation, shift: bool) -> u32 {
let keypad = location == KeyLocation::Numpad;
let right = location == KeyLocation::Right;
let pick = |main: u32, kp: u32| if keypad { kp } else { main };
let side = |left: u32, right_side: u32| if right { right_side } else { left };
if let Some(n) = function_key_number(key) {
return if keypad && n <= 4 {
0xff91 + n - 1
} else {
0xffbe + n - 1
};
}
match key {
NamedKey::Backspace => 0xff08,
NamedKey::Tab if keypad => 0xff89,
NamedKey::Tab if shift => 0xfe20,
NamedKey::Tab => 0xff09,
NamedKey::Clear => 0xff0b,
NamedKey::Enter => pick(0xff0d, 0xff8d),
NamedKey::Pause => 0xff13,
NamedKey::ScrollLock => 0xff14,
NamedKey::Escape => 0xff1b,
NamedKey::Delete => pick(0xffff, 0xff9f),
NamedKey::Compose => 0xff20,
NamedKey::KanjiMode => 0xff21,
NamedKey::NonConvert => 0xff22,
NamedKey::Convert => 0xff23,
NamedKey::Romaji => 0xff24,
NamedKey::Hiragana => 0xff25,
NamedKey::HiraganaKatakana => 0xff27,
NamedKey::Zenkaku => 0xff28,
NamedKey::Hankaku => 0xff29,
NamedKey::ZenkakuHankaku => 0xff2a,
NamedKey::KanaMode => 0xff2d,
NamedKey::Alphanumeric => 0xff30,
NamedKey::CodeInput => 0xff37,
NamedKey::SingleCandidate => 0xff3c,
NamedKey::AllCandidates => 0xff3d,
NamedKey::PreviousCandidate => 0xff3e,
NamedKey::Home => pick(0xff50, 0xff95),
NamedKey::ArrowLeft => pick(0xff51, 0xff96),
NamedKey::ArrowUp => pick(0xff52, 0xff97),
NamedKey::ArrowRight => pick(0xff53, 0xff98),
NamedKey::ArrowDown => pick(0xff54, 0xff99),
NamedKey::PageUp => pick(0xff55, 0xff9a),
NamedKey::PageDown => pick(0xff56, 0xff9b),
NamedKey::End => pick(0xff57, 0xff9c),
NamedKey::Select => 0xff60,
NamedKey::PrintScreen => 0xff61,
NamedKey::Execute => 0xff62,
NamedKey::Insert => pick(0xff63, 0xff9e),
NamedKey::Undo => 0xff65,
NamedKey::Redo => 0xff66,
NamedKey::ContextMenu => 0xff67,
NamedKey::Find => 0xff68,
NamedKey::Cancel => 0xff69,
NamedKey::Help => 0xff6a,
NamedKey::ModeChange => 0xff7e,
NamedKey::NumLock => 0xff7f,
NamedKey::Shift => side(0xffe1, 0xffe2),
NamedKey::Control => side(0xffe3, 0xffe4),
NamedKey::CapsLock => 0xffe5,
NamedKey::Meta => side(0xffe7, 0xffe8),
NamedKey::Alt => side(0xffe9, 0xffea),
NamedKey::Super => side(0xffeb, 0xffec),
NamedKey::Hyper => side(0xffed, 0xffee),
NamedKey::AltGraph => 0xfe03,
NamedKey::GroupNext => 0xfe08,
NamedKey::GroupPrevious => 0xfe0a,
NamedKey::GroupFirst => 0xfe0c,
NamedKey::GroupLast => 0xfe0e,
NamedKey::Space => 0x20,
_ => VOID_SYMBOL,
}
}
fn function_key_number(key: NamedKey) -> Option<u32> {
const KEYS: [NamedKey; 35] = [
NamedKey::F1,
NamedKey::F2,
NamedKey::F3,
NamedKey::F4,
NamedKey::F5,
NamedKey::F6,
NamedKey::F7,
NamedKey::F8,
NamedKey::F9,
NamedKey::F10,
NamedKey::F11,
NamedKey::F12,
NamedKey::F13,
NamedKey::F14,
NamedKey::F15,
NamedKey::F16,
NamedKey::F17,
NamedKey::F18,
NamedKey::F19,
NamedKey::F20,
NamedKey::F21,
NamedKey::F22,
NamedKey::F23,
NamedKey::F24,
NamedKey::F25,
NamedKey::F26,
NamedKey::F27,
NamedKey::F28,
NamedKey::F29,
NamedKey::F30,
NamedKey::F31,
NamedKey::F32,
NamedKey::F33,
NamedKey::F34,
NamedKey::F35,
];
KEYS.iter()
.position(|&k| k == key)
.map(|index| index as u32 + 1)
}
fn character_keysym(ch: char, location: KeyLocation) -> u32 {
if location == KeyLocation::Numpad
&& let Some(keypad) = keypad_keysym(ch)
{
return keypad;
}
let code = ch as u32;
match code {
0x20..=0x7e | 0xa0..=0xff => code,
0x08..=0x0b | 0x0d | 0x1b => 0xff00 | code,
0x7f => 0xffff,
0x00..=0x1f | 0x80..=0x9f => VOID_SYMBOL,
_ => 0x0100_0000 + code,
}
}
fn keypad_keysym(ch: char) -> Option<u32> {
Some(match ch {
'0'..='9' => 0xffb0 + (ch as u32 - '0' as u32),
' ' => 0xff80,
'*' => 0xffaa,
'+' => 0xffab,
',' => 0xffac,
'-' => 0xffad,
'.' => 0xffae,
'/' => 0xffaf,
'=' => 0xffbd,
_ => return None,
})
}
fn dead_keysym(ch: char) -> Option<u32> {
Some(match ch {
'`' => 0xfe50,
'´' => 0xfe51,
'^' => 0xfe52,
'~' => 0xfe53,
'¯' => 0xfe54,
'˘' => 0xfe55,
'˙' => 0xfe56,
'¨' => 0xfe57,
'°' => 0xfe58,
'˝' => 0xfe59,
'ˇ' => 0xfe5a,
'¸' => 0xfe5b,
'˛' => 0xfe5c,
_ => return None,
})
}
pub(super) fn hardware_keycode(key: PhysicalKey) -> u32 {
#[cfg(all(unix, not(target_os = "macos")))]
{
use winit::platform::scancode::PhysicalKeyExtScancode;
key.to_scancode().map_or(0, |scancode| scancode + 8)
}
#[cfg(not(all(unix, not(target_os = "macos"))))]
{
let _ = key;
0
}
}
pub(super) fn modifier_mask(keyboard: KeyboardState, num_lock: bool) -> u32 {
let modifiers = keyboard.modifiers;
let mut mask = 0;
if modifiers.shift_key() {
mask |= SHIFT_MASK;
}
if keyboard.caps_lock {
mask |= LOCK_MASK;
}
if modifiers.control_key() {
mask |= CONTROL_MASK;
}
if modifiers.alt_key() {
mask |= MOD1_MASK;
}
if num_lock {
mask |= MOD2_MASK;
}
if modifiers.super_key() {
mask |= MOD4_MASK;
}
mask
}
pub(super) fn num_lock_shown(key: &Key, location: KeyLocation) -> Option<bool> {
if location != KeyLocation::Numpad {
return None;
}
match key {
Key::Character(text) => text
.chars()
.next()
.filter(|ch| ch.is_ascii_digit() || matches!(ch, '.' | ','))
.map(|_| true),
Key::Named(
NamedKey::Home
| NamedKey::End
| NamedKey::ArrowLeft
| NamedKey::ArrowRight
| NamedKey::ArrowUp
| NamedKey::ArrowDown
| NamedKey::PageUp
| NamedKey::PageDown
| NamedKey::Insert
| NamedKey::Delete
| NamedKey::Clear,
) => Some(false),
Key::Unidentified(NativeKey::Xkb(0xff9d)) => Some(false),
_ => None,
}
}
pub(super) fn types_text(key: &Key) -> bool {
matches!(
key,
Key::Character(_) | Key::Dead(_) | Key::Named(NamedKey::Space)
)
}
pub(super) fn event_string(key: &Key) -> String {
match key {
Key::Character(text) if !text.is_empty() && !text.chars().any(char::is_control) => {
text.to_string()
}
Key::Named(NamedKey::Space) => " ".to_owned(),
_ => String::new(),
}
}