Overhaul the Keyboard API
Overhaul the keyboard API in winit to mimic the W3C specification
to achieve better crossplatform parity. The `KeyboardInput` event
is now uses `KeyEvent` which consists of:
- `physical_key` - a cross platform way to refer to scancodes;
- `logical_key` - keysym value, which shows your key respecting the
layout;
- `text` - the text produced by this keypress;
- `location` - the location of the key on the keyboard;
- `repeat` - whether the key was produced by the repeat.
And also a `platform_specific` field which encapsulates extra
information on desktop platforms, like key without modifiers
and text with all modifiers.
The `Modifiers` were also slightly reworked as in, the information
whether the left or right modifier is pressed is now also exposed
on platforms where it could be queried reliably. The support was
also added for the web and orbital platforms finishing the API
change.
This change made the `OptionAsAlt` API on macOS redundant thus it
was removed all together.
Co-authored-by: Artúr Kovács <kovacs.artur.barnabas@gmail.com>
Co-authored-by: Kirill Chibisov <contact@kchibisov.com>
Co-authored-by: daxpedda <daxpedda@gmail.com>
Fixes: #2631.
Fixes: #2055.
Fixes: #2032.
Fixes: #1904.
Fixes: #1810.
Fixes: #1700.
Fixes: #1443.
Fixes: #1343.
Fixes: #1208.
Fixes: #1151.
Fixes: #812.
Fixes: #600.
Fixes: #361.
Fixes: #343.
This commit is contained in:
parent
f3f46cb3f6
commit
918430979f
81 changed files with 9577 additions and 3419 deletions
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
convert::TryInto,
|
||||
hash::Hash,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
|
|
@ -10,7 +11,7 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
|
||||
use android_activity::input::{InputEvent, KeyAction, MotionAction};
|
||||
use android_activity::{
|
||||
AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
|
||||
};
|
||||
|
|
@ -19,12 +20,16 @@ use raw_window_handle::{
|
|||
AndroidDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle,
|
||||
};
|
||||
|
||||
#[cfg(feature = "android-native-activity")]
|
||||
use ndk_sys::AKeyEvent_getKeyCode;
|
||||
|
||||
use crate::platform_impl::Fullscreen;
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error,
|
||||
event::{self, StartCause, VirtualKeyCode},
|
||||
event::{self, StartCause},
|
||||
event_loop::{self, ControlFlow, EventLoopWindowTarget as RootELW},
|
||||
keyboard::{Key, KeyCode, KeyLocation, NativeKey, NativeKeyCode},
|
||||
window::{
|
||||
self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowButtons, WindowLevel,
|
||||
},
|
||||
|
|
@ -32,170 +37,6 @@ use crate::{
|
|||
|
||||
static HAS_FOCUS: Lazy<RwLock<bool>> = Lazy::new(|| RwLock::new(true));
|
||||
|
||||
fn ndk_keycode_to_virtualkeycode(keycode: Keycode) -> Option<event::VirtualKeyCode> {
|
||||
match keycode {
|
||||
Keycode::A => Some(VirtualKeyCode::A),
|
||||
Keycode::B => Some(VirtualKeyCode::B),
|
||||
Keycode::C => Some(VirtualKeyCode::C),
|
||||
Keycode::D => Some(VirtualKeyCode::D),
|
||||
Keycode::E => Some(VirtualKeyCode::E),
|
||||
Keycode::F => Some(VirtualKeyCode::F),
|
||||
Keycode::G => Some(VirtualKeyCode::G),
|
||||
Keycode::H => Some(VirtualKeyCode::H),
|
||||
Keycode::I => Some(VirtualKeyCode::I),
|
||||
Keycode::J => Some(VirtualKeyCode::J),
|
||||
Keycode::K => Some(VirtualKeyCode::K),
|
||||
Keycode::L => Some(VirtualKeyCode::L),
|
||||
Keycode::M => Some(VirtualKeyCode::M),
|
||||
Keycode::N => Some(VirtualKeyCode::N),
|
||||
Keycode::O => Some(VirtualKeyCode::O),
|
||||
Keycode::P => Some(VirtualKeyCode::P),
|
||||
Keycode::Q => Some(VirtualKeyCode::Q),
|
||||
Keycode::R => Some(VirtualKeyCode::R),
|
||||
Keycode::S => Some(VirtualKeyCode::S),
|
||||
Keycode::T => Some(VirtualKeyCode::T),
|
||||
Keycode::U => Some(VirtualKeyCode::U),
|
||||
Keycode::V => Some(VirtualKeyCode::V),
|
||||
Keycode::W => Some(VirtualKeyCode::W),
|
||||
Keycode::X => Some(VirtualKeyCode::X),
|
||||
Keycode::Y => Some(VirtualKeyCode::Y),
|
||||
Keycode::Z => Some(VirtualKeyCode::Z),
|
||||
|
||||
Keycode::Keycode0 => Some(VirtualKeyCode::Key0),
|
||||
Keycode::Keycode1 => Some(VirtualKeyCode::Key1),
|
||||
Keycode::Keycode2 => Some(VirtualKeyCode::Key2),
|
||||
Keycode::Keycode3 => Some(VirtualKeyCode::Key3),
|
||||
Keycode::Keycode4 => Some(VirtualKeyCode::Key4),
|
||||
Keycode::Keycode5 => Some(VirtualKeyCode::Key5),
|
||||
Keycode::Keycode6 => Some(VirtualKeyCode::Key6),
|
||||
Keycode::Keycode7 => Some(VirtualKeyCode::Key7),
|
||||
Keycode::Keycode8 => Some(VirtualKeyCode::Key8),
|
||||
Keycode::Keycode9 => Some(VirtualKeyCode::Key9),
|
||||
|
||||
Keycode::Numpad0 => Some(VirtualKeyCode::Numpad0),
|
||||
Keycode::Numpad1 => Some(VirtualKeyCode::Numpad1),
|
||||
Keycode::Numpad2 => Some(VirtualKeyCode::Numpad2),
|
||||
Keycode::Numpad3 => Some(VirtualKeyCode::Numpad3),
|
||||
Keycode::Numpad4 => Some(VirtualKeyCode::Numpad4),
|
||||
Keycode::Numpad5 => Some(VirtualKeyCode::Numpad5),
|
||||
Keycode::Numpad6 => Some(VirtualKeyCode::Numpad6),
|
||||
Keycode::Numpad7 => Some(VirtualKeyCode::Numpad7),
|
||||
Keycode::Numpad8 => Some(VirtualKeyCode::Numpad8),
|
||||
Keycode::Numpad9 => Some(VirtualKeyCode::Numpad9),
|
||||
|
||||
Keycode::NumpadAdd => Some(VirtualKeyCode::NumpadAdd),
|
||||
Keycode::NumpadSubtract => Some(VirtualKeyCode::NumpadSubtract),
|
||||
Keycode::NumpadMultiply => Some(VirtualKeyCode::NumpadMultiply),
|
||||
Keycode::NumpadDivide => Some(VirtualKeyCode::NumpadDivide),
|
||||
Keycode::NumpadEnter => Some(VirtualKeyCode::NumpadEnter),
|
||||
Keycode::NumpadEquals => Some(VirtualKeyCode::NumpadEquals),
|
||||
Keycode::NumpadComma => Some(VirtualKeyCode::NumpadComma),
|
||||
Keycode::NumpadDot => Some(VirtualKeyCode::NumpadDecimal),
|
||||
Keycode::NumLock => Some(VirtualKeyCode::Numlock),
|
||||
|
||||
Keycode::DpadLeft => Some(VirtualKeyCode::Left),
|
||||
Keycode::DpadRight => Some(VirtualKeyCode::Right),
|
||||
Keycode::DpadUp => Some(VirtualKeyCode::Up),
|
||||
Keycode::DpadDown => Some(VirtualKeyCode::Down),
|
||||
|
||||
Keycode::F1 => Some(VirtualKeyCode::F1),
|
||||
Keycode::F2 => Some(VirtualKeyCode::F2),
|
||||
Keycode::F3 => Some(VirtualKeyCode::F3),
|
||||
Keycode::F4 => Some(VirtualKeyCode::F4),
|
||||
Keycode::F5 => Some(VirtualKeyCode::F5),
|
||||
Keycode::F6 => Some(VirtualKeyCode::F6),
|
||||
Keycode::F7 => Some(VirtualKeyCode::F7),
|
||||
Keycode::F8 => Some(VirtualKeyCode::F8),
|
||||
Keycode::F9 => Some(VirtualKeyCode::F9),
|
||||
Keycode::F10 => Some(VirtualKeyCode::F10),
|
||||
Keycode::F11 => Some(VirtualKeyCode::F11),
|
||||
Keycode::F12 => Some(VirtualKeyCode::F12),
|
||||
|
||||
Keycode::Space => Some(VirtualKeyCode::Space),
|
||||
Keycode::Escape => Some(VirtualKeyCode::Escape),
|
||||
Keycode::Enter => Some(VirtualKeyCode::Return), // not on the Numpad
|
||||
Keycode::Tab => Some(VirtualKeyCode::Tab),
|
||||
|
||||
Keycode::PageUp => Some(VirtualKeyCode::PageUp),
|
||||
Keycode::PageDown => Some(VirtualKeyCode::PageDown),
|
||||
Keycode::MoveHome => Some(VirtualKeyCode::Home),
|
||||
Keycode::MoveEnd => Some(VirtualKeyCode::End),
|
||||
Keycode::Insert => Some(VirtualKeyCode::Insert),
|
||||
|
||||
Keycode::Del => Some(VirtualKeyCode::Back), // Backspace (above Enter)
|
||||
Keycode::ForwardDel => Some(VirtualKeyCode::Delete), // Delete (below Insert)
|
||||
|
||||
Keycode::Copy => Some(VirtualKeyCode::Copy),
|
||||
Keycode::Paste => Some(VirtualKeyCode::Paste),
|
||||
Keycode::Cut => Some(VirtualKeyCode::Cut),
|
||||
|
||||
Keycode::VolumeUp => Some(VirtualKeyCode::VolumeUp),
|
||||
Keycode::VolumeDown => Some(VirtualKeyCode::VolumeDown),
|
||||
Keycode::VolumeMute => Some(VirtualKeyCode::Mute), // ???
|
||||
Keycode::Mute => Some(VirtualKeyCode::Mute), // ???
|
||||
Keycode::MediaPlayPause => Some(VirtualKeyCode::PlayPause),
|
||||
Keycode::MediaStop => Some(VirtualKeyCode::MediaStop), // ??? simple "Stop"?
|
||||
Keycode::MediaNext => Some(VirtualKeyCode::NextTrack),
|
||||
Keycode::MediaPrevious => Some(VirtualKeyCode::PrevTrack),
|
||||
|
||||
Keycode::Plus => Some(VirtualKeyCode::Plus),
|
||||
Keycode::Minus => Some(VirtualKeyCode::Minus),
|
||||
Keycode::Equals => Some(VirtualKeyCode::Equals),
|
||||
Keycode::Semicolon => Some(VirtualKeyCode::Semicolon),
|
||||
Keycode::Slash => Some(VirtualKeyCode::Slash),
|
||||
Keycode::Backslash => Some(VirtualKeyCode::Backslash),
|
||||
Keycode::Comma => Some(VirtualKeyCode::Comma),
|
||||
Keycode::Period => Some(VirtualKeyCode::Period),
|
||||
Keycode::Apostrophe => Some(VirtualKeyCode::Apostrophe),
|
||||
Keycode::Grave => Some(VirtualKeyCode::Grave),
|
||||
Keycode::At => Some(VirtualKeyCode::At),
|
||||
|
||||
// TODO: Maybe mapping this to Snapshot makes more sense? See: "PrtScr/SysRq"
|
||||
Keycode::Sysrq => Some(VirtualKeyCode::Sysrq),
|
||||
// These are usually the same (Pause/Break)
|
||||
Keycode::Break => Some(VirtualKeyCode::Pause),
|
||||
// These are exactly the same
|
||||
Keycode::ScrollLock => Some(VirtualKeyCode::Scroll),
|
||||
|
||||
Keycode::Yen => Some(VirtualKeyCode::Yen),
|
||||
Keycode::Kana => Some(VirtualKeyCode::Kana),
|
||||
|
||||
Keycode::CtrlLeft => Some(VirtualKeyCode::LControl),
|
||||
Keycode::CtrlRight => Some(VirtualKeyCode::RControl),
|
||||
|
||||
Keycode::ShiftLeft => Some(VirtualKeyCode::LShift),
|
||||
Keycode::ShiftRight => Some(VirtualKeyCode::RShift),
|
||||
|
||||
Keycode::AltLeft => Some(VirtualKeyCode::LAlt),
|
||||
Keycode::AltRight => Some(VirtualKeyCode::RAlt),
|
||||
|
||||
// Different names for the same keys
|
||||
Keycode::MetaLeft => Some(VirtualKeyCode::LWin),
|
||||
Keycode::MetaRight => Some(VirtualKeyCode::RWin),
|
||||
|
||||
Keycode::LeftBracket => Some(VirtualKeyCode::LBracket),
|
||||
Keycode::RightBracket => Some(VirtualKeyCode::RBracket),
|
||||
|
||||
Keycode::Power => Some(VirtualKeyCode::Power),
|
||||
Keycode::Sleep => Some(VirtualKeyCode::Sleep), // what about SoftSleep?
|
||||
Keycode::Wakeup => Some(VirtualKeyCode::Wake),
|
||||
|
||||
Keycode::NavigateNext => Some(VirtualKeyCode::NavigateForward),
|
||||
Keycode::NavigatePrevious => Some(VirtualKeyCode::NavigateBackward),
|
||||
|
||||
Keycode::Calculator => Some(VirtualKeyCode::Calculator),
|
||||
Keycode::Explorer => Some(VirtualKeyCode::MyComputer), // "close enough"
|
||||
Keycode::Envelope => Some(VirtualKeyCode::Mail), // "close enough"
|
||||
|
||||
Keycode::Star => Some(VirtualKeyCode::Asterisk), // ???
|
||||
Keycode::AllApps => Some(VirtualKeyCode::Apps), // ???
|
||||
Keycode::AppSwitch => Some(VirtualKeyCode::Apps), // ???
|
||||
Keycode::Refresh => Some(VirtualKeyCode::WebRefresh), // ???
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
struct PeekableReceiver<T> {
|
||||
recv: mpsc::Receiver<T>,
|
||||
first: Option<T>,
|
||||
|
|
@ -287,6 +128,9 @@ impl RedrawRequester {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEventExtra {}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
android_app: AndroidApp,
|
||||
window_target: event_loop::EventLoopWindowTarget<T>,
|
||||
|
|
@ -551,25 +395,48 @@ impl<T: 'static> EventLoop<T> {
|
|||
}
|
||||
}
|
||||
InputEvent::KeyEvent(key) => {
|
||||
let device_id = event::DeviceId(DeviceId);
|
||||
|
||||
let state = match key.action() {
|
||||
KeyAction::Down => event::ElementState::Pressed,
|
||||
KeyAction::Up => event::ElementState::Released,
|
||||
_ => event::ElementState::Released,
|
||||
};
|
||||
#[allow(deprecated)]
|
||||
|
||||
#[cfg(feature = "android-native-activity")]
|
||||
let (keycode_u32, scancode_u32) = unsafe {
|
||||
// We abuse the fact that `android_activity`'s `KeyEvent` is `repr(transparent)`
|
||||
let event = (key as *const android_activity::input::KeyEvent<'_>).cast::<ndk::event::KeyEvent>();
|
||||
// We use the unsafe function directly because we want to forward the
|
||||
// keycode value even if it doesn't have a variant defined in the ndk
|
||||
// crate.
|
||||
(
|
||||
AKeyEvent_getKeyCode((*event).ptr().as_ptr()) as u32,
|
||||
(*event).scan_code() as u32
|
||||
)
|
||||
};
|
||||
#[cfg(feature = "android-game-activity")]
|
||||
let (keycode_u32, scancode_u32) = (key.keyCode as u32, key.scanCode as u32);
|
||||
let keycode = keycode_u32
|
||||
.try_into()
|
||||
.unwrap_or(ndk::event::Keycode::Unknown);
|
||||
let physical_key = KeyCode::Unidentified(
|
||||
NativeKeyCode::Android(scancode_u32),
|
||||
);
|
||||
let native = NativeKey::Android(keycode_u32);
|
||||
let logical_key = keycode_to_logical(keycode, native);
|
||||
// TODO: maybe use getUnicodeChar to get the logical key
|
||||
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
input: event::KeyboardInput {
|
||||
scancode: key.scan_code() as u32,
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
event: event::KeyEvent {
|
||||
state,
|
||||
virtual_keycode: ndk_keycode_to_virtualkeycode(
|
||||
key.key_code(),
|
||||
),
|
||||
modifiers: event::ModifiersState::default(),
|
||||
physical_key,
|
||||
logical_key,
|
||||
location: keycode_to_location(keycode),
|
||||
repeat: key.repeat_count() > 0,
|
||||
text: None,
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
|
|
@ -578,7 +445,7 @@ impl<T: 'static> EventLoop<T> {
|
|||
event,
|
||||
self.window_target(),
|
||||
control_flow,
|
||||
callback
|
||||
callback,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
|
|
@ -1084,6 +951,8 @@ impl Window {
|
|||
pub fn title(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
|
||||
pub fn reset_dead_keys(&self) {}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
|
|
@ -1185,3 +1054,377 @@ impl VideoMode {
|
|||
self.monitor.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn keycode_to_logical(keycode: ndk::event::Keycode, native: NativeKey) -> Key {
|
||||
use ndk::event::Keycode::*;
|
||||
|
||||
// The android `Keycode` is sort-of layout dependent. More specifically
|
||||
// if I press the Z key using a US layout, then I get KEYCODE_Z,
|
||||
// but if I press the same key after switching to a HUN layout, I get
|
||||
// KEYCODE_Y.
|
||||
//
|
||||
// To prevents us from using this value to determine the `physical_key`
|
||||
// (also know as winit's `KeyCode`)
|
||||
//
|
||||
// Unfortunately the documentation says that the scancode values
|
||||
// "are not reliable and vary from device to device". Which seems to mean
|
||||
// that there's no way to reliably get the physical_key on android.
|
||||
|
||||
match keycode {
|
||||
Unknown => Key::Unidentified(native),
|
||||
|
||||
// Can be added on demand
|
||||
SoftLeft => Key::Unidentified(native),
|
||||
SoftRight => Key::Unidentified(native),
|
||||
|
||||
// Using `BrowserHome` instead of `GoHome` according to
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||
Home => Key::BrowserHome,
|
||||
Back => Key::BrowserBack,
|
||||
Call => Key::Call,
|
||||
Endcall => Key::EndCall,
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Reporting unidentified, because the specific character is layout dependent.
|
||||
// (I'm not sure though)
|
||||
Keycode0 => Key::Unidentified(native),
|
||||
Keycode1 => Key::Unidentified(native),
|
||||
Keycode2 => Key::Unidentified(native),
|
||||
Keycode3 => Key::Unidentified(native),
|
||||
Keycode4 => Key::Unidentified(native),
|
||||
Keycode5 => Key::Unidentified(native),
|
||||
Keycode6 => Key::Unidentified(native),
|
||||
Keycode7 => Key::Unidentified(native),
|
||||
Keycode8 => Key::Unidentified(native),
|
||||
Keycode9 => Key::Unidentified(native),
|
||||
Star => Key::Unidentified(native),
|
||||
Pound => Key::Unidentified(native),
|
||||
A => Key::Unidentified(native),
|
||||
B => Key::Unidentified(native),
|
||||
C => Key::Unidentified(native),
|
||||
D => Key::Unidentified(native),
|
||||
E => Key::Unidentified(native),
|
||||
F => Key::Unidentified(native),
|
||||
G => Key::Unidentified(native),
|
||||
H => Key::Unidentified(native),
|
||||
I => Key::Unidentified(native),
|
||||
J => Key::Unidentified(native),
|
||||
K => Key::Unidentified(native),
|
||||
L => Key::Unidentified(native),
|
||||
M => Key::Unidentified(native),
|
||||
N => Key::Unidentified(native),
|
||||
O => Key::Unidentified(native),
|
||||
P => Key::Unidentified(native),
|
||||
Q => Key::Unidentified(native),
|
||||
R => Key::Unidentified(native),
|
||||
S => Key::Unidentified(native),
|
||||
T => Key::Unidentified(native),
|
||||
U => Key::Unidentified(native),
|
||||
V => Key::Unidentified(native),
|
||||
W => Key::Unidentified(native),
|
||||
X => Key::Unidentified(native),
|
||||
Y => Key::Unidentified(native),
|
||||
Z => Key::Unidentified(native),
|
||||
Comma => Key::Unidentified(native),
|
||||
Period => Key::Unidentified(native),
|
||||
Grave => Key::Unidentified(native),
|
||||
Minus => Key::Unidentified(native),
|
||||
Equals => Key::Unidentified(native),
|
||||
LeftBracket => Key::Unidentified(native),
|
||||
RightBracket => Key::Unidentified(native),
|
||||
Backslash => Key::Unidentified(native),
|
||||
Semicolon => Key::Unidentified(native),
|
||||
Apostrophe => Key::Unidentified(native),
|
||||
Slash => Key::Unidentified(native),
|
||||
At => Key::Unidentified(native),
|
||||
Plus => Key::Unidentified(native),
|
||||
//-------------------------------------------------------------------------------
|
||||
DpadUp => Key::ArrowUp,
|
||||
DpadDown => Key::ArrowDown,
|
||||
DpadLeft => Key::ArrowLeft,
|
||||
DpadRight => Key::ArrowRight,
|
||||
DpadCenter => Key::Enter,
|
||||
|
||||
VolumeUp => Key::AudioVolumeUp,
|
||||
VolumeDown => Key::AudioVolumeDown,
|
||||
Power => Key::Power,
|
||||
Camera => Key::Camera,
|
||||
Clear => Key::Clear,
|
||||
|
||||
AltLeft => Key::Alt,
|
||||
AltRight => Key::Alt,
|
||||
ShiftLeft => Key::Shift,
|
||||
ShiftRight => Key::Shift,
|
||||
Tab => Key::Tab,
|
||||
Space => Key::Space,
|
||||
Sym => Key::Symbol,
|
||||
Explorer => Key::LaunchWebBrowser,
|
||||
Envelope => Key::LaunchMail,
|
||||
Enter => Key::Enter,
|
||||
Del => Key::Backspace,
|
||||
|
||||
// According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM
|
||||
Num => Key::Alt,
|
||||
|
||||
Headsethook => Key::HeadsetHook,
|
||||
Focus => Key::CameraFocus,
|
||||
|
||||
Menu => Key::Unidentified(native),
|
||||
|
||||
Notification => Key::Notification,
|
||||
Search => Key::BrowserSearch,
|
||||
MediaPlayPause => Key::MediaPlayPause,
|
||||
MediaStop => Key::MediaStop,
|
||||
MediaNext => Key::MediaTrackNext,
|
||||
MediaPrevious => Key::MediaTrackPrevious,
|
||||
MediaRewind => Key::MediaRewind,
|
||||
MediaFastForward => Key::MediaFastForward,
|
||||
Mute => Key::MicrophoneVolumeMute,
|
||||
PageUp => Key::PageUp,
|
||||
PageDown => Key::PageDown,
|
||||
Pictsymbols => Key::Unidentified(native),
|
||||
SwitchCharset => Key::Unidentified(native),
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Gamepad events should be exposed through a separate API, not
|
||||
// keyboard events
|
||||
ButtonA => Key::Unidentified(native),
|
||||
ButtonB => Key::Unidentified(native),
|
||||
ButtonC => Key::Unidentified(native),
|
||||
ButtonX => Key::Unidentified(native),
|
||||
ButtonY => Key::Unidentified(native),
|
||||
ButtonZ => Key::Unidentified(native),
|
||||
ButtonL1 => Key::Unidentified(native),
|
||||
ButtonR1 => Key::Unidentified(native),
|
||||
ButtonL2 => Key::Unidentified(native),
|
||||
ButtonR2 => Key::Unidentified(native),
|
||||
ButtonThumbl => Key::Unidentified(native),
|
||||
ButtonThumbr => Key::Unidentified(native),
|
||||
ButtonStart => Key::Unidentified(native),
|
||||
ButtonSelect => Key::Unidentified(native),
|
||||
ButtonMode => Key::Unidentified(native),
|
||||
// -----------------------------------------------------------------
|
||||
Escape => Key::Escape,
|
||||
ForwardDel => Key::Delete,
|
||||
CtrlLeft => Key::Control,
|
||||
CtrlRight => Key::Control,
|
||||
CapsLock => Key::CapsLock,
|
||||
ScrollLock => Key::ScrollLock,
|
||||
MetaLeft => Key::Super,
|
||||
MetaRight => Key::Super,
|
||||
Function => Key::Fn,
|
||||
Sysrq => Key::PrintScreen,
|
||||
Break => Key::Pause,
|
||||
MoveHome => Key::Home,
|
||||
MoveEnd => Key::End,
|
||||
Insert => Key::Insert,
|
||||
Forward => Key::BrowserForward,
|
||||
MediaPlay => Key::MediaPlay,
|
||||
MediaPause => Key::MediaPause,
|
||||
MediaClose => Key::MediaClose,
|
||||
MediaEject => Key::Eject,
|
||||
MediaRecord => Key::MediaRecord,
|
||||
F1 => Key::F1,
|
||||
F2 => Key::F2,
|
||||
F3 => Key::F3,
|
||||
F4 => Key::F4,
|
||||
F5 => Key::F5,
|
||||
F6 => Key::F6,
|
||||
F7 => Key::F7,
|
||||
F8 => Key::F8,
|
||||
F9 => Key::F9,
|
||||
F10 => Key::F10,
|
||||
F11 => Key::F11,
|
||||
F12 => Key::F12,
|
||||
NumLock => Key::NumLock,
|
||||
Numpad0 => Key::Unidentified(native),
|
||||
Numpad1 => Key::Unidentified(native),
|
||||
Numpad2 => Key::Unidentified(native),
|
||||
Numpad3 => Key::Unidentified(native),
|
||||
Numpad4 => Key::Unidentified(native),
|
||||
Numpad5 => Key::Unidentified(native),
|
||||
Numpad6 => Key::Unidentified(native),
|
||||
Numpad7 => Key::Unidentified(native),
|
||||
Numpad8 => Key::Unidentified(native),
|
||||
Numpad9 => Key::Unidentified(native),
|
||||
NumpadDivide => Key::Unidentified(native),
|
||||
NumpadMultiply => Key::Unidentified(native),
|
||||
NumpadSubtract => Key::Unidentified(native),
|
||||
NumpadAdd => Key::Unidentified(native),
|
||||
NumpadDot => Key::Unidentified(native),
|
||||
NumpadComma => Key::Unidentified(native),
|
||||
NumpadEnter => Key::Unidentified(native),
|
||||
NumpadEquals => Key::Unidentified(native),
|
||||
NumpadLeftParen => Key::Unidentified(native),
|
||||
NumpadRightParen => Key::Unidentified(native),
|
||||
|
||||
VolumeMute => Key::AudioVolumeMute,
|
||||
Info => Key::Info,
|
||||
ChannelUp => Key::ChannelUp,
|
||||
ChannelDown => Key::ChannelDown,
|
||||
ZoomIn => Key::ZoomIn,
|
||||
ZoomOut => Key::ZoomOut,
|
||||
Tv => Key::TV,
|
||||
Window => Key::Unidentified(native),
|
||||
Guide => Key::Guide,
|
||||
Dvr => Key::DVR,
|
||||
Bookmark => Key::BrowserFavorites,
|
||||
Captions => Key::ClosedCaptionToggle,
|
||||
Settings => Key::Settings,
|
||||
TvPower => Key::TVPower,
|
||||
TvInput => Key::TVInput,
|
||||
StbPower => Key::STBPower,
|
||||
StbInput => Key::STBInput,
|
||||
AvrPower => Key::AVRPower,
|
||||
AvrInput => Key::AVRInput,
|
||||
ProgRed => Key::ColorF0Red,
|
||||
ProgGreen => Key::ColorF1Green,
|
||||
ProgYellow => Key::ColorF2Yellow,
|
||||
ProgBlue => Key::ColorF3Blue,
|
||||
AppSwitch => Key::AppSwitch,
|
||||
Button1 => Key::Unidentified(native),
|
||||
Button2 => Key::Unidentified(native),
|
||||
Button3 => Key::Unidentified(native),
|
||||
Button4 => Key::Unidentified(native),
|
||||
Button5 => Key::Unidentified(native),
|
||||
Button6 => Key::Unidentified(native),
|
||||
Button7 => Key::Unidentified(native),
|
||||
Button8 => Key::Unidentified(native),
|
||||
Button9 => Key::Unidentified(native),
|
||||
Button10 => Key::Unidentified(native),
|
||||
Button11 => Key::Unidentified(native),
|
||||
Button12 => Key::Unidentified(native),
|
||||
Button13 => Key::Unidentified(native),
|
||||
Button14 => Key::Unidentified(native),
|
||||
Button15 => Key::Unidentified(native),
|
||||
Button16 => Key::Unidentified(native),
|
||||
LanguageSwitch => Key::GroupNext,
|
||||
MannerMode => Key::MannerMode,
|
||||
Keycode3dMode => Key::TV3DMode,
|
||||
Contacts => Key::LaunchContacts,
|
||||
Calendar => Key::LaunchCalendar,
|
||||
Music => Key::LaunchMusicPlayer,
|
||||
Calculator => Key::LaunchApplication2,
|
||||
ZenkakuHankaku => Key::ZenkakuHankaku,
|
||||
Eisu => Key::Eisu,
|
||||
Muhenkan => Key::NonConvert,
|
||||
Henkan => Key::Convert,
|
||||
KatakanaHiragana => Key::HiraganaKatakana,
|
||||
Yen => Key::Unidentified(native),
|
||||
Ro => Key::Unidentified(native),
|
||||
Kana => Key::KanjiMode,
|
||||
Assist => Key::Unidentified(native),
|
||||
BrightnessDown => Key::BrightnessDown,
|
||||
BrightnessUp => Key::BrightnessUp,
|
||||
MediaAudioTrack => Key::MediaAudioTrack,
|
||||
Sleep => Key::Standby,
|
||||
Wakeup => Key::WakeUp,
|
||||
Pairing => Key::Pairing,
|
||||
MediaTopMenu => Key::MediaTopMenu,
|
||||
Keycode11 => Key::Unidentified(native),
|
||||
Keycode12 => Key::Unidentified(native),
|
||||
LastChannel => Key::MediaLast,
|
||||
TvDataService => Key::TVDataService,
|
||||
VoiceAssist => Key::VoiceDial,
|
||||
TvRadioService => Key::TVRadioService,
|
||||
TvTeletext => Key::Teletext,
|
||||
TvNumberEntry => Key::TVNumberEntry,
|
||||
TvTerrestrialAnalog => Key::TVTerrestrialAnalog,
|
||||
TvTerrestrialDigital => Key::TVTerrestrialDigital,
|
||||
TvSatellite => Key::TVSatellite,
|
||||
TvSatelliteBs => Key::TVSatelliteBS,
|
||||
TvSatelliteCs => Key::TVSatelliteCS,
|
||||
TvSatelliteService => Key::TVSatelliteToggle,
|
||||
TvNetwork => Key::TVNetwork,
|
||||
TvAntennaCable => Key::TVAntennaCable,
|
||||
TvInputHdmi1 => Key::TVInputHDMI1,
|
||||
TvInputHdmi2 => Key::TVInputHDMI2,
|
||||
TvInputHdmi3 => Key::TVInputHDMI3,
|
||||
TvInputHdmi4 => Key::TVInputHDMI4,
|
||||
TvInputComposite1 => Key::TVInputComposite1,
|
||||
TvInputComposite2 => Key::TVInputComposite2,
|
||||
TvInputComponent1 => Key::TVInputComponent1,
|
||||
TvInputComponent2 => Key::TVInputComponent2,
|
||||
TvInputVga1 => Key::TVInputVGA1,
|
||||
TvAudioDescription => Key::TVAudioDescription,
|
||||
TvAudioDescriptionMixUp => Key::TVAudioDescriptionMixUp,
|
||||
TvAudioDescriptionMixDown => Key::TVAudioDescriptionMixDown,
|
||||
TvZoomMode => Key::ZoomToggle,
|
||||
TvContentsMenu => Key::TVContentsMenu,
|
||||
TvMediaContextMenu => Key::TVMediaContext,
|
||||
TvTimerProgramming => Key::TVTimer,
|
||||
Help => Key::Help,
|
||||
NavigatePrevious => Key::NavigatePrevious,
|
||||
NavigateNext => Key::NavigateNext,
|
||||
NavigateIn => Key::NavigateIn,
|
||||
NavigateOut => Key::NavigateOut,
|
||||
StemPrimary => Key::Unidentified(native),
|
||||
Stem1 => Key::Unidentified(native),
|
||||
Stem2 => Key::Unidentified(native),
|
||||
Stem3 => Key::Unidentified(native),
|
||||
DpadUpLeft => Key::Unidentified(native),
|
||||
DpadDownLeft => Key::Unidentified(native),
|
||||
DpadUpRight => Key::Unidentified(native),
|
||||
DpadDownRight => Key::Unidentified(native),
|
||||
MediaSkipForward => Key::MediaSkipForward,
|
||||
MediaSkipBackward => Key::MediaSkipBackward,
|
||||
MediaStepForward => Key::MediaStepForward,
|
||||
MediaStepBackward => Key::MediaStepBackward,
|
||||
SoftSleep => Key::Unidentified(native),
|
||||
Cut => Key::Cut,
|
||||
Copy => Key::Copy,
|
||||
Paste => Key::Paste,
|
||||
SystemNavigationUp => Key::Unidentified(native),
|
||||
SystemNavigationDown => Key::Unidentified(native),
|
||||
SystemNavigationLeft => Key::Unidentified(native),
|
||||
SystemNavigationRight => Key::Unidentified(native),
|
||||
AllApps => Key::Unidentified(native),
|
||||
Refresh => Key::BrowserRefresh,
|
||||
ThumbsUp => Key::Unidentified(native),
|
||||
ThumbsDown => Key::Unidentified(native),
|
||||
ProfileSwitch => Key::Unidentified(native),
|
||||
}
|
||||
}
|
||||
|
||||
fn keycode_to_location(keycode: ndk::event::Keycode) -> KeyLocation {
|
||||
use ndk::event::Keycode::*;
|
||||
|
||||
match keycode {
|
||||
AltLeft => KeyLocation::Left,
|
||||
AltRight => KeyLocation::Right,
|
||||
ShiftLeft => KeyLocation::Left,
|
||||
ShiftRight => KeyLocation::Right,
|
||||
|
||||
// According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM
|
||||
Num => KeyLocation::Left,
|
||||
|
||||
CtrlLeft => KeyLocation::Left,
|
||||
CtrlRight => KeyLocation::Right,
|
||||
MetaLeft => KeyLocation::Left,
|
||||
MetaRight => KeyLocation::Right,
|
||||
|
||||
NumLock => KeyLocation::Numpad,
|
||||
Numpad0 => KeyLocation::Numpad,
|
||||
Numpad1 => KeyLocation::Numpad,
|
||||
Numpad2 => KeyLocation::Numpad,
|
||||
Numpad3 => KeyLocation::Numpad,
|
||||
Numpad4 => KeyLocation::Numpad,
|
||||
Numpad5 => KeyLocation::Numpad,
|
||||
Numpad6 => KeyLocation::Numpad,
|
||||
Numpad7 => KeyLocation::Numpad,
|
||||
Numpad8 => KeyLocation::Numpad,
|
||||
Numpad9 => KeyLocation::Numpad,
|
||||
NumpadDivide => KeyLocation::Numpad,
|
||||
NumpadMultiply => KeyLocation::Numpad,
|
||||
NumpadSubtract => KeyLocation::Numpad,
|
||||
NumpadAdd => KeyLocation::Numpad,
|
||||
NumpadDot => KeyLocation::Numpad,
|
||||
NumpadComma => KeyLocation::Numpad,
|
||||
NumpadEnter => KeyLocation::Numpad,
|
||||
NumpadEquals => KeyLocation::Numpad,
|
||||
NumpadLeftParen => KeyLocation::Numpad,
|
||||
NumpadRightParen => KeyLocation::Numpad,
|
||||
|
||||
_ => KeyLocation::Standard,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ impl DeviceId {
|
|||
unsafe impl Send for DeviceId {}
|
||||
unsafe impl Sync for DeviceId {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OsError {}
|
||||
|
||||
|
|
|
|||
|
|
@ -368,6 +368,10 @@ impl Inner {
|
|||
warn!("`Window::title` is ignored on iOS");
|
||||
String::new()
|
||||
}
|
||||
|
||||
pub fn reset_dead_keys(&self) {
|
||||
// Noop
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
|
|
|
|||
882
src/platform_impl/linux/common/keymap.rs
Normal file
882
src/platform_impl/linux/common/keymap.rs
Normal file
|
|
@ -0,0 +1,882 @@
|
|||
//! Convert XKB keys to Winit keys.
|
||||
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKey, NativeKeyCode};
|
||||
|
||||
/// 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_keycode(keycode: u32) -> KeyCode {
|
||||
let rawkey = keycode - 8;
|
||||
// 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.
|
||||
match rawkey {
|
||||
0 => KeyCode::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 => KeyCode::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,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::Xkb(rawkey)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keycode_to_raw(keycode: KeyCode) -> Option<u32> {
|
||||
match keycode {
|
||||
KeyCode::Unidentified(NativeKeyCode::Unidentified) => Some(240),
|
||||
KeyCode::Unidentified(NativeKeyCode::Xkb(raw)) => Some(raw),
|
||||
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,
|
||||
}
|
||||
.map(|raw| raw + 8)
|
||||
}
|
||||
|
||||
pub fn keysym_to_key(keysym: u32) -> Key {
|
||||
use xkbcommon_dl::keysyms;
|
||||
match keysym {
|
||||
// TTY function keys
|
||||
keysyms::XKB_KEY_BackSpace => Key::Backspace,
|
||||
keysyms::XKB_KEY_Tab => Key::Tab,
|
||||
// keysyms::XKB_KEY_Linefeed => Key::Linefeed,
|
||||
keysyms::XKB_KEY_Clear => Key::Clear,
|
||||
keysyms::XKB_KEY_Return => Key::Enter,
|
||||
keysyms::XKB_KEY_Pause => Key::Pause,
|
||||
keysyms::XKB_KEY_Scroll_Lock => Key::ScrollLock,
|
||||
keysyms::XKB_KEY_Sys_Req => Key::PrintScreen,
|
||||
keysyms::XKB_KEY_Escape => Key::Escape,
|
||||
keysyms::XKB_KEY_Delete => Key::Delete,
|
||||
|
||||
// IME keys
|
||||
keysyms::XKB_KEY_Multi_key => Key::Compose,
|
||||
keysyms::XKB_KEY_Codeinput => Key::CodeInput,
|
||||
keysyms::XKB_KEY_SingleCandidate => Key::SingleCandidate,
|
||||
keysyms::XKB_KEY_MultipleCandidate => Key::AllCandidates,
|
||||
keysyms::XKB_KEY_PreviousCandidate => Key::PreviousCandidate,
|
||||
|
||||
// Japanese keys
|
||||
keysyms::XKB_KEY_Kanji => Key::KanjiMode,
|
||||
keysyms::XKB_KEY_Muhenkan => Key::NonConvert,
|
||||
keysyms::XKB_KEY_Henkan_Mode => Key::Convert,
|
||||
keysyms::XKB_KEY_Romaji => Key::Romaji,
|
||||
keysyms::XKB_KEY_Hiragana => Key::Hiragana,
|
||||
keysyms::XKB_KEY_Hiragana_Katakana => Key::HiraganaKatakana,
|
||||
keysyms::XKB_KEY_Zenkaku => Key::Zenkaku,
|
||||
keysyms::XKB_KEY_Hankaku => Key::Hankaku,
|
||||
keysyms::XKB_KEY_Zenkaku_Hankaku => Key::ZenkakuHankaku,
|
||||
// keysyms::XKB_KEY_Touroku => Key::Touroku,
|
||||
// keysyms::XKB_KEY_Massyo => Key::Massyo,
|
||||
keysyms::XKB_KEY_Kana_Lock => Key::KanaMode,
|
||||
keysyms::XKB_KEY_Kana_Shift => Key::KanaMode,
|
||||
keysyms::XKB_KEY_Eisu_Shift => Key::Alphanumeric,
|
||||
keysyms::XKB_KEY_Eisu_toggle => Key::Alphanumeric,
|
||||
// NOTE: The next three items are aliases for values we've already mapped.
|
||||
// keysyms::XKB_KEY_Kanji_Bangou => Key::CodeInput,
|
||||
// keysyms::XKB_KEY_Zen_Koho => Key::AllCandidates,
|
||||
// keysyms::XKB_KEY_Mae_Koho => Key::PreviousCandidate,
|
||||
|
||||
// Cursor control & motion
|
||||
keysyms::XKB_KEY_Home => Key::Home,
|
||||
keysyms::XKB_KEY_Left => Key::ArrowLeft,
|
||||
keysyms::XKB_KEY_Up => Key::ArrowUp,
|
||||
keysyms::XKB_KEY_Right => Key::ArrowRight,
|
||||
keysyms::XKB_KEY_Down => Key::ArrowDown,
|
||||
// keysyms::XKB_KEY_Prior => Key::PageUp,
|
||||
keysyms::XKB_KEY_Page_Up => Key::PageUp,
|
||||
// keysyms::XKB_KEY_Next => Key::PageDown,
|
||||
keysyms::XKB_KEY_Page_Down => Key::PageDown,
|
||||
keysyms::XKB_KEY_End => Key::End,
|
||||
// keysyms::XKB_KEY_Begin => Key::Begin,
|
||||
|
||||
// Misc. functions
|
||||
keysyms::XKB_KEY_Select => Key::Select,
|
||||
keysyms::XKB_KEY_Print => Key::PrintScreen,
|
||||
keysyms::XKB_KEY_Execute => Key::Execute,
|
||||
keysyms::XKB_KEY_Insert => Key::Insert,
|
||||
keysyms::XKB_KEY_Undo => Key::Undo,
|
||||
keysyms::XKB_KEY_Redo => Key::Redo,
|
||||
keysyms::XKB_KEY_Menu => Key::ContextMenu,
|
||||
keysyms::XKB_KEY_Find => Key::Find,
|
||||
keysyms::XKB_KEY_Cancel => Key::Cancel,
|
||||
keysyms::XKB_KEY_Help => Key::Help,
|
||||
keysyms::XKB_KEY_Break => Key::Pause,
|
||||
keysyms::XKB_KEY_Mode_switch => Key::ModeChange,
|
||||
// keysyms::XKB_KEY_script_switch => Key::ModeChange,
|
||||
keysyms::XKB_KEY_Num_Lock => Key::NumLock,
|
||||
|
||||
// Keypad keys
|
||||
// keysyms::XKB_KEY_KP_Space => Key::Character(" "),
|
||||
keysyms::XKB_KEY_KP_Tab => Key::Tab,
|
||||
keysyms::XKB_KEY_KP_Enter => Key::Enter,
|
||||
keysyms::XKB_KEY_KP_F1 => Key::F1,
|
||||
keysyms::XKB_KEY_KP_F2 => Key::F2,
|
||||
keysyms::XKB_KEY_KP_F3 => Key::F3,
|
||||
keysyms::XKB_KEY_KP_F4 => Key::F4,
|
||||
keysyms::XKB_KEY_KP_Home => Key::Home,
|
||||
keysyms::XKB_KEY_KP_Left => Key::ArrowLeft,
|
||||
keysyms::XKB_KEY_KP_Up => Key::ArrowLeft,
|
||||
keysyms::XKB_KEY_KP_Right => Key::ArrowRight,
|
||||
keysyms::XKB_KEY_KP_Down => Key::ArrowDown,
|
||||
// keysyms::XKB_KEY_KP_Prior => Key::PageUp,
|
||||
keysyms::XKB_KEY_KP_Page_Up => Key::PageUp,
|
||||
// keysyms::XKB_KEY_KP_Next => Key::PageDown,
|
||||
keysyms::XKB_KEY_KP_Page_Down => Key::PageDown,
|
||||
keysyms::XKB_KEY_KP_End => Key::End,
|
||||
// This is the key labeled "5" on the numpad when NumLock is off.
|
||||
// keysyms::XKB_KEY_KP_Begin => Key::Begin,
|
||||
keysyms::XKB_KEY_KP_Insert => Key::Insert,
|
||||
keysyms::XKB_KEY_KP_Delete => Key::Delete,
|
||||
// keysyms::XKB_KEY_KP_Equal => Key::Equal,
|
||||
// keysyms::XKB_KEY_KP_Multiply => Key::Multiply,
|
||||
// keysyms::XKB_KEY_KP_Add => Key::Add,
|
||||
// keysyms::XKB_KEY_KP_Separator => Key::Separator,
|
||||
// keysyms::XKB_KEY_KP_Subtract => Key::Subtract,
|
||||
// keysyms::XKB_KEY_KP_Decimal => Key::Decimal,
|
||||
// keysyms::XKB_KEY_KP_Divide => Key::Divide,
|
||||
|
||||
// keysyms::XKB_KEY_KP_0 => Key::Character("0"),
|
||||
// keysyms::XKB_KEY_KP_1 => Key::Character("1"),
|
||||
// keysyms::XKB_KEY_KP_2 => Key::Character("2"),
|
||||
// keysyms::XKB_KEY_KP_3 => Key::Character("3"),
|
||||
// keysyms::XKB_KEY_KP_4 => Key::Character("4"),
|
||||
// keysyms::XKB_KEY_KP_5 => Key::Character("5"),
|
||||
// keysyms::XKB_KEY_KP_6 => Key::Character("6"),
|
||||
// keysyms::XKB_KEY_KP_7 => Key::Character("7"),
|
||||
// keysyms::XKB_KEY_KP_8 => Key::Character("8"),
|
||||
// keysyms::XKB_KEY_KP_9 => Key::Character("9"),
|
||||
|
||||
// Function keys
|
||||
keysyms::XKB_KEY_F1 => Key::F1,
|
||||
keysyms::XKB_KEY_F2 => Key::F2,
|
||||
keysyms::XKB_KEY_F3 => Key::F3,
|
||||
keysyms::XKB_KEY_F4 => Key::F4,
|
||||
keysyms::XKB_KEY_F5 => Key::F5,
|
||||
keysyms::XKB_KEY_F6 => Key::F6,
|
||||
keysyms::XKB_KEY_F7 => Key::F7,
|
||||
keysyms::XKB_KEY_F8 => Key::F8,
|
||||
keysyms::XKB_KEY_F9 => Key::F9,
|
||||
keysyms::XKB_KEY_F10 => Key::F10,
|
||||
keysyms::XKB_KEY_F11 => Key::F11,
|
||||
keysyms::XKB_KEY_F12 => Key::F12,
|
||||
keysyms::XKB_KEY_F13 => Key::F13,
|
||||
keysyms::XKB_KEY_F14 => Key::F14,
|
||||
keysyms::XKB_KEY_F15 => Key::F15,
|
||||
keysyms::XKB_KEY_F16 => Key::F16,
|
||||
keysyms::XKB_KEY_F17 => Key::F17,
|
||||
keysyms::XKB_KEY_F18 => Key::F18,
|
||||
keysyms::XKB_KEY_F19 => Key::F19,
|
||||
keysyms::XKB_KEY_F20 => Key::F20,
|
||||
keysyms::XKB_KEY_F21 => Key::F21,
|
||||
keysyms::XKB_KEY_F22 => Key::F22,
|
||||
keysyms::XKB_KEY_F23 => Key::F23,
|
||||
keysyms::XKB_KEY_F24 => Key::F24,
|
||||
keysyms::XKB_KEY_F25 => Key::F25,
|
||||
keysyms::XKB_KEY_F26 => Key::F26,
|
||||
keysyms::XKB_KEY_F27 => Key::F27,
|
||||
keysyms::XKB_KEY_F28 => Key::F28,
|
||||
keysyms::XKB_KEY_F29 => Key::F29,
|
||||
keysyms::XKB_KEY_F30 => Key::F30,
|
||||
keysyms::XKB_KEY_F31 => Key::F31,
|
||||
keysyms::XKB_KEY_F32 => Key::F32,
|
||||
keysyms::XKB_KEY_F33 => Key::F33,
|
||||
keysyms::XKB_KEY_F34 => Key::F34,
|
||||
keysyms::XKB_KEY_F35 => Key::F35,
|
||||
|
||||
// Modifiers
|
||||
keysyms::XKB_KEY_Shift_L => Key::Shift,
|
||||
keysyms::XKB_KEY_Shift_R => Key::Shift,
|
||||
keysyms::XKB_KEY_Control_L => Key::Control,
|
||||
keysyms::XKB_KEY_Control_R => Key::Control,
|
||||
keysyms::XKB_KEY_Caps_Lock => Key::CapsLock,
|
||||
// keysyms::XKB_KEY_Shift_Lock => Key::ShiftLock,
|
||||
|
||||
// keysyms::XKB_KEY_Meta_L => Key::Meta,
|
||||
// keysyms::XKB_KEY_Meta_R => Key::Meta,
|
||||
keysyms::XKB_KEY_Alt_L => Key::Alt,
|
||||
keysyms::XKB_KEY_Alt_R => Key::Alt,
|
||||
keysyms::XKB_KEY_Super_L => Key::Super,
|
||||
keysyms::XKB_KEY_Super_R => Key::Super,
|
||||
keysyms::XKB_KEY_Hyper_L => Key::Hyper,
|
||||
keysyms::XKB_KEY_Hyper_R => Key::Hyper,
|
||||
|
||||
// XKB function and modifier keys
|
||||
// keysyms::XKB_KEY_ISO_Lock => Key::IsoLock,
|
||||
// keysyms::XKB_KEY_ISO_Level2_Latch => Key::IsoLevel2Latch,
|
||||
keysyms::XKB_KEY_ISO_Level3_Shift => Key::AltGraph,
|
||||
keysyms::XKB_KEY_ISO_Level3_Latch => Key::AltGraph,
|
||||
keysyms::XKB_KEY_ISO_Level3_Lock => Key::AltGraph,
|
||||
// keysyms::XKB_KEY_ISO_Level5_Shift => Key::IsoLevel5Shift,
|
||||
// keysyms::XKB_KEY_ISO_Level5_Latch => Key::IsoLevel5Latch,
|
||||
// keysyms::XKB_KEY_ISO_Level5_Lock => Key::IsoLevel5Lock,
|
||||
// keysyms::XKB_KEY_ISO_Group_Shift => Key::IsoGroupShift,
|
||||
// keysyms::XKB_KEY_ISO_Group_Latch => Key::IsoGroupLatch,
|
||||
// keysyms::XKB_KEY_ISO_Group_Lock => Key::IsoGroupLock,
|
||||
keysyms::XKB_KEY_ISO_Next_Group => Key::GroupNext,
|
||||
// keysyms::XKB_KEY_ISO_Next_Group_Lock => Key::GroupNextLock,
|
||||
keysyms::XKB_KEY_ISO_Prev_Group => Key::GroupPrevious,
|
||||
// keysyms::XKB_KEY_ISO_Prev_Group_Lock => Key::GroupPreviousLock,
|
||||
keysyms::XKB_KEY_ISO_First_Group => Key::GroupFirst,
|
||||
// keysyms::XKB_KEY_ISO_First_Group_Lock => Key::GroupFirstLock,
|
||||
keysyms::XKB_KEY_ISO_Last_Group => Key::GroupLast,
|
||||
// keysyms::XKB_KEY_ISO_Last_Group_Lock => Key::GroupLastLock,
|
||||
//
|
||||
keysyms::XKB_KEY_ISO_Left_Tab => Key::Tab,
|
||||
// keysyms::XKB_KEY_ISO_Move_Line_Up => Key::IsoMoveLineUp,
|
||||
// keysyms::XKB_KEY_ISO_Move_Line_Down => Key::IsoMoveLineDown,
|
||||
// keysyms::XKB_KEY_ISO_Partial_Line_Up => Key::IsoPartialLineUp,
|
||||
// keysyms::XKB_KEY_ISO_Partial_Line_Down => Key::IsoPartialLineDown,
|
||||
// keysyms::XKB_KEY_ISO_Partial_Space_Left => Key::IsoPartialSpaceLeft,
|
||||
// keysyms::XKB_KEY_ISO_Partial_Space_Right => Key::IsoPartialSpaceRight,
|
||||
// keysyms::XKB_KEY_ISO_Set_Margin_Left => Key::IsoSetMarginLeft,
|
||||
// keysyms::XKB_KEY_ISO_Set_Margin_Right => Key::IsoSetMarginRight,
|
||||
// keysyms::XKB_KEY_ISO_Release_Margin_Left => Key::IsoReleaseMarginLeft,
|
||||
// keysyms::XKB_KEY_ISO_Release_Margin_Right => Key::IsoReleaseMarginRight,
|
||||
// keysyms::XKB_KEY_ISO_Release_Both_Margins => Key::IsoReleaseBothMargins,
|
||||
// keysyms::XKB_KEY_ISO_Fast_Cursor_Left => Key::IsoFastCursorLeft,
|
||||
// keysyms::XKB_KEY_ISO_Fast_Cursor_Right => Key::IsoFastCursorRight,
|
||||
// keysyms::XKB_KEY_ISO_Fast_Cursor_Up => Key::IsoFastCursorUp,
|
||||
// keysyms::XKB_KEY_ISO_Fast_Cursor_Down => Key::IsoFastCursorDown,
|
||||
// keysyms::XKB_KEY_ISO_Continuous_Underline => Key::IsoContinuousUnderline,
|
||||
// keysyms::XKB_KEY_ISO_Discontinuous_Underline => Key::IsoDiscontinuousUnderline,
|
||||
// keysyms::XKB_KEY_ISO_Emphasize => Key::IsoEmphasize,
|
||||
// keysyms::XKB_KEY_ISO_Center_Object => Key::IsoCenterObject,
|
||||
keysyms::XKB_KEY_ISO_Enter => Key::Enter,
|
||||
|
||||
// XKB_KEY_dead_grave..XKB_KEY_dead_currency
|
||||
|
||||
// XKB_KEY_dead_lowline..XKB_KEY_dead_longsolidusoverlay
|
||||
|
||||
// XKB_KEY_dead_a..XKB_KEY_dead_capital_schwa
|
||||
|
||||
// XKB_KEY_dead_greek
|
||||
|
||||
// XKB_KEY_First_Virtual_Screen..XKB_KEY_Terminate_Server
|
||||
|
||||
// XKB_KEY_AccessX_Enable..XKB_KEY_AudibleBell_Enable
|
||||
|
||||
// XKB_KEY_Pointer_Left..XKB_KEY_Pointer_Drag5
|
||||
|
||||
// XKB_KEY_Pointer_EnableKeys..XKB_KEY_Pointer_DfltBtnPrev
|
||||
|
||||
// XKB_KEY_ch..XKB_KEY_C_H
|
||||
|
||||
// 3270 terminal keys
|
||||
// keysyms::XKB_KEY_3270_Duplicate => Key::Duplicate,
|
||||
// keysyms::XKB_KEY_3270_FieldMark => Key::FieldMark,
|
||||
// keysyms::XKB_KEY_3270_Right2 => Key::Right2,
|
||||
// keysyms::XKB_KEY_3270_Left2 => Key::Left2,
|
||||
// keysyms::XKB_KEY_3270_BackTab => Key::BackTab,
|
||||
keysyms::XKB_KEY_3270_EraseEOF => Key::EraseEof,
|
||||
// keysyms::XKB_KEY_3270_EraseInput => Key::EraseInput,
|
||||
// keysyms::XKB_KEY_3270_Reset => Key::Reset,
|
||||
// keysyms::XKB_KEY_3270_Quit => Key::Quit,
|
||||
// keysyms::XKB_KEY_3270_PA1 => Key::Pa1,
|
||||
// keysyms::XKB_KEY_3270_PA2 => Key::Pa2,
|
||||
// keysyms::XKB_KEY_3270_PA3 => Key::Pa3,
|
||||
// keysyms::XKB_KEY_3270_Test => Key::Test,
|
||||
keysyms::XKB_KEY_3270_Attn => Key::Attn,
|
||||
// keysyms::XKB_KEY_3270_CursorBlink => Key::CursorBlink,
|
||||
// keysyms::XKB_KEY_3270_AltCursor => Key::AltCursor,
|
||||
// keysyms::XKB_KEY_3270_KeyClick => Key::KeyClick,
|
||||
// keysyms::XKB_KEY_3270_Jump => Key::Jump,
|
||||
// keysyms::XKB_KEY_3270_Ident => Key::Ident,
|
||||
// keysyms::XKB_KEY_3270_Rule => Key::Rule,
|
||||
// keysyms::XKB_KEY_3270_Copy => Key::Copy,
|
||||
keysyms::XKB_KEY_3270_Play => Key::Play,
|
||||
// keysyms::XKB_KEY_3270_Setup => Key::Setup,
|
||||
// keysyms::XKB_KEY_3270_Record => Key::Record,
|
||||
// keysyms::XKB_KEY_3270_ChangeScreen => Key::ChangeScreen,
|
||||
// keysyms::XKB_KEY_3270_DeleteWord => Key::DeleteWord,
|
||||
keysyms::XKB_KEY_3270_ExSelect => Key::ExSel,
|
||||
keysyms::XKB_KEY_3270_CursorSelect => Key::CrSel,
|
||||
keysyms::XKB_KEY_3270_PrintScreen => Key::PrintScreen,
|
||||
keysyms::XKB_KEY_3270_Enter => Key::Enter,
|
||||
|
||||
keysyms::XKB_KEY_space => Key::Space,
|
||||
// XKB_KEY_exclam..XKB_KEY_Sinh_kunddaliya
|
||||
|
||||
// XFree86
|
||||
// keysyms::XKB_KEY_XF86ModeLock => Key::ModeLock,
|
||||
|
||||
// XFree86 - Backlight controls
|
||||
keysyms::XKB_KEY_XF86MonBrightnessUp => Key::BrightnessUp,
|
||||
keysyms::XKB_KEY_XF86MonBrightnessDown => Key::BrightnessDown,
|
||||
// keysyms::XKB_KEY_XF86KbdLightOnOff => Key::LightOnOff,
|
||||
// keysyms::XKB_KEY_XF86KbdBrightnessUp => Key::KeyboardBrightnessUp,
|
||||
// keysyms::XKB_KEY_XF86KbdBrightnessDown => Key::KeyboardBrightnessDown,
|
||||
|
||||
// XFree86 - "Internet"
|
||||
keysyms::XKB_KEY_XF86Standby => Key::Standby,
|
||||
keysyms::XKB_KEY_XF86AudioLowerVolume => Key::AudioVolumeDown,
|
||||
keysyms::XKB_KEY_XF86AudioRaiseVolume => Key::AudioVolumeUp,
|
||||
keysyms::XKB_KEY_XF86AudioPlay => Key::MediaPlay,
|
||||
keysyms::XKB_KEY_XF86AudioStop => Key::MediaStop,
|
||||
keysyms::XKB_KEY_XF86AudioPrev => Key::MediaTrackPrevious,
|
||||
keysyms::XKB_KEY_XF86AudioNext => Key::MediaTrackNext,
|
||||
keysyms::XKB_KEY_XF86HomePage => Key::BrowserHome,
|
||||
keysyms::XKB_KEY_XF86Mail => Key::LaunchMail,
|
||||
// keysyms::XKB_KEY_XF86Start => Key::Start,
|
||||
keysyms::XKB_KEY_XF86Search => Key::BrowserSearch,
|
||||
keysyms::XKB_KEY_XF86AudioRecord => Key::MediaRecord,
|
||||
|
||||
// XFree86 - PDA
|
||||
keysyms::XKB_KEY_XF86Calculator => Key::LaunchApplication2,
|
||||
// keysyms::XKB_KEY_XF86Memo => Key::Memo,
|
||||
// keysyms::XKB_KEY_XF86ToDoList => Key::ToDoList,
|
||||
keysyms::XKB_KEY_XF86Calendar => Key::LaunchCalendar,
|
||||
keysyms::XKB_KEY_XF86PowerDown => Key::Power,
|
||||
// keysyms::XKB_KEY_XF86ContrastAdjust => Key::AdjustContrast,
|
||||
// keysyms::XKB_KEY_XF86RockerUp => Key::RockerUp,
|
||||
// keysyms::XKB_KEY_XF86RockerDown => Key::RockerDown,
|
||||
// keysyms::XKB_KEY_XF86RockerEnter => Key::RockerEnter,
|
||||
|
||||
// XFree86 - More "Internet"
|
||||
keysyms::XKB_KEY_XF86Back => Key::BrowserBack,
|
||||
keysyms::XKB_KEY_XF86Forward => Key::BrowserForward,
|
||||
// keysyms::XKB_KEY_XF86Stop => Key::Stop,
|
||||
keysyms::XKB_KEY_XF86Refresh => Key::BrowserRefresh,
|
||||
keysyms::XKB_KEY_XF86PowerOff => Key::Power,
|
||||
keysyms::XKB_KEY_XF86WakeUp => Key::WakeUp,
|
||||
keysyms::XKB_KEY_XF86Eject => Key::Eject,
|
||||
keysyms::XKB_KEY_XF86ScreenSaver => Key::LaunchScreenSaver,
|
||||
keysyms::XKB_KEY_XF86WWW => Key::LaunchWebBrowser,
|
||||
keysyms::XKB_KEY_XF86Sleep => Key::Standby,
|
||||
keysyms::XKB_KEY_XF86Favorites => Key::BrowserFavorites,
|
||||
keysyms::XKB_KEY_XF86AudioPause => Key::MediaPause,
|
||||
// keysyms::XKB_KEY_XF86AudioMedia => Key::AudioMedia,
|
||||
keysyms::XKB_KEY_XF86MyComputer => Key::LaunchApplication1,
|
||||
// keysyms::XKB_KEY_XF86VendorHome => Key::VendorHome,
|
||||
// keysyms::XKB_KEY_XF86LightBulb => Key::LightBulb,
|
||||
// keysyms::XKB_KEY_XF86Shop => Key::BrowserShop,
|
||||
// keysyms::XKB_KEY_XF86History => Key::BrowserHistory,
|
||||
// keysyms::XKB_KEY_XF86OpenURL => Key::OpenUrl,
|
||||
// keysyms::XKB_KEY_XF86AddFavorite => Key::AddFavorite,
|
||||
// keysyms::XKB_KEY_XF86HotLinks => Key::HotLinks,
|
||||
// keysyms::XKB_KEY_XF86BrightnessAdjust => Key::BrightnessAdjust,
|
||||
// keysyms::XKB_KEY_XF86Finance => Key::BrowserFinance,
|
||||
// keysyms::XKB_KEY_XF86Community => Key::BrowserCommunity,
|
||||
keysyms::XKB_KEY_XF86AudioRewind => Key::MediaRewind,
|
||||
// keysyms::XKB_KEY_XF86BackForward => Key::???,
|
||||
// XKB_KEY_XF86Launch0..XKB_KEY_XF86LaunchF
|
||||
|
||||
// XKB_KEY_XF86ApplicationLeft..XKB_KEY_XF86CD
|
||||
keysyms::XKB_KEY_XF86Calculater => Key::LaunchApplication2, // Nice typo, libxkbcommon :)
|
||||
// XKB_KEY_XF86Clear
|
||||
keysyms::XKB_KEY_XF86Close => Key::Close,
|
||||
keysyms::XKB_KEY_XF86Copy => Key::Copy,
|
||||
keysyms::XKB_KEY_XF86Cut => Key::Cut,
|
||||
// XKB_KEY_XF86Display..XKB_KEY_XF86Documents
|
||||
keysyms::XKB_KEY_XF86Excel => Key::LaunchSpreadsheet,
|
||||
// XKB_KEY_XF86Explorer..XKB_KEY_XF86iTouch
|
||||
keysyms::XKB_KEY_XF86LogOff => Key::LogOff,
|
||||
// XKB_KEY_XF86Market..XKB_KEY_XF86MenuPB
|
||||
keysyms::XKB_KEY_XF86MySites => Key::BrowserFavorites,
|
||||
keysyms::XKB_KEY_XF86New => Key::New,
|
||||
// XKB_KEY_XF86News..XKB_KEY_XF86OfficeHome
|
||||
keysyms::XKB_KEY_XF86Open => Key::Open,
|
||||
// XKB_KEY_XF86Option
|
||||
keysyms::XKB_KEY_XF86Paste => Key::Paste,
|
||||
keysyms::XKB_KEY_XF86Phone => Key::LaunchPhone,
|
||||
// XKB_KEY_XF86Q
|
||||
keysyms::XKB_KEY_XF86Reply => Key::MailReply,
|
||||
keysyms::XKB_KEY_XF86Reload => Key::BrowserRefresh,
|
||||
// XKB_KEY_XF86RotateWindows..XKB_KEY_XF86RotationKB
|
||||
keysyms::XKB_KEY_XF86Save => Key::Save,
|
||||
// XKB_KEY_XF86ScrollUp..XKB_KEY_XF86ScrollClick
|
||||
keysyms::XKB_KEY_XF86Send => Key::MailSend,
|
||||
keysyms::XKB_KEY_XF86Spell => Key::SpellCheck,
|
||||
keysyms::XKB_KEY_XF86SplitScreen => Key::SplitScreenToggle,
|
||||
// XKB_KEY_XF86Support..XKB_KEY_XF86User2KB
|
||||
keysyms::XKB_KEY_XF86Video => Key::LaunchMediaPlayer,
|
||||
// XKB_KEY_XF86WheelButton
|
||||
keysyms::XKB_KEY_XF86Word => Key::LaunchWordProcessor,
|
||||
// XKB_KEY_XF86Xfer
|
||||
keysyms::XKB_KEY_XF86ZoomIn => Key::ZoomIn,
|
||||
keysyms::XKB_KEY_XF86ZoomOut => Key::ZoomOut,
|
||||
|
||||
// XKB_KEY_XF86Away..XKB_KEY_XF86Messenger
|
||||
keysyms::XKB_KEY_XF86WebCam => Key::LaunchWebCam,
|
||||
keysyms::XKB_KEY_XF86MailForward => Key::MailForward,
|
||||
// XKB_KEY_XF86Pictures
|
||||
keysyms::XKB_KEY_XF86Music => Key::LaunchMusicPlayer,
|
||||
|
||||
// XKB_KEY_XF86Battery..XKB_KEY_XF86UWB
|
||||
//
|
||||
keysyms::XKB_KEY_XF86AudioForward => Key::MediaFastForward,
|
||||
// XKB_KEY_XF86AudioRepeat
|
||||
keysyms::XKB_KEY_XF86AudioRandomPlay => Key::RandomToggle,
|
||||
keysyms::XKB_KEY_XF86Subtitle => Key::Subtitle,
|
||||
keysyms::XKB_KEY_XF86AudioCycleTrack => Key::MediaAudioTrack,
|
||||
// XKB_KEY_XF86CycleAngle..XKB_KEY_XF86Blue
|
||||
//
|
||||
keysyms::XKB_KEY_XF86Suspend => Key::Standby,
|
||||
keysyms::XKB_KEY_XF86Hibernate => Key::Hibernate,
|
||||
// XKB_KEY_XF86TouchpadToggle..XKB_KEY_XF86TouchpadOff
|
||||
//
|
||||
keysyms::XKB_KEY_XF86AudioMute => Key::AudioVolumeMute,
|
||||
|
||||
// XKB_KEY_XF86Switch_VT_1..XKB_KEY_XF86Switch_VT_12
|
||||
|
||||
// XKB_KEY_XF86Ungrab..XKB_KEY_XF86ClearGrab
|
||||
keysyms::XKB_KEY_XF86Next_VMode => Key::VideoModeNext,
|
||||
// keysyms::XKB_KEY_XF86Prev_VMode => Key::VideoModePrevious,
|
||||
// XKB_KEY_XF86LogWindowTree..XKB_KEY_XF86LogGrabInfo
|
||||
|
||||
// XKB_KEY_SunFA_Grave..XKB_KEY_SunFA_Cedilla
|
||||
|
||||
// keysyms::XKB_KEY_SunF36 => Key::F36 | Key::F11,
|
||||
// keysyms::XKB_KEY_SunF37 => Key::F37 | Key::F12,
|
||||
|
||||
// keysyms::XKB_KEY_SunSys_Req => Key::PrintScreen,
|
||||
// The next couple of xkb (until XKB_KEY_SunStop) are already handled.
|
||||
// XKB_KEY_SunPrint_Screen..XKB_KEY_SunPageDown
|
||||
|
||||
// XKB_KEY_SunUndo..XKB_KEY_SunFront
|
||||
keysyms::XKB_KEY_SunCopy => Key::Copy,
|
||||
keysyms::XKB_KEY_SunOpen => Key::Open,
|
||||
keysyms::XKB_KEY_SunPaste => Key::Paste,
|
||||
keysyms::XKB_KEY_SunCut => Key::Cut,
|
||||
|
||||
// XKB_KEY_SunPowerSwitch
|
||||
keysyms::XKB_KEY_SunAudioLowerVolume => Key::AudioVolumeDown,
|
||||
keysyms::XKB_KEY_SunAudioMute => Key::AudioVolumeMute,
|
||||
keysyms::XKB_KEY_SunAudioRaiseVolume => Key::AudioVolumeUp,
|
||||
// XKB_KEY_SunVideoDegauss
|
||||
keysyms::XKB_KEY_SunVideoLowerBrightness => Key::BrightnessDown,
|
||||
keysyms::XKB_KEY_SunVideoRaiseBrightness => Key::BrightnessUp,
|
||||
// XKB_KEY_SunPowerSwitchShift
|
||||
//
|
||||
0 => Key::Unidentified(NativeKey::Unidentified),
|
||||
_ => Key::Unidentified(NativeKey::Xkb(keysym)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keysym_location(keysym: u32) -> KeyLocation {
|
||||
use xkbcommon_dl::keysyms;
|
||||
match keysym {
|
||||
keysyms::XKB_KEY_Shift_L
|
||||
| keysyms::XKB_KEY_Control_L
|
||||
| keysyms::XKB_KEY_Meta_L
|
||||
| keysyms::XKB_KEY_Alt_L
|
||||
| keysyms::XKB_KEY_Super_L
|
||||
| keysyms::XKB_KEY_Hyper_L => KeyLocation::Left,
|
||||
keysyms::XKB_KEY_Shift_R
|
||||
| keysyms::XKB_KEY_Control_R
|
||||
| keysyms::XKB_KEY_Meta_R
|
||||
| keysyms::XKB_KEY_Alt_R
|
||||
| keysyms::XKB_KEY_Super_R
|
||||
| keysyms::XKB_KEY_Hyper_R => KeyLocation::Right,
|
||||
keysyms::XKB_KEY_KP_0
|
||||
| keysyms::XKB_KEY_KP_1
|
||||
| keysyms::XKB_KEY_KP_2
|
||||
| keysyms::XKB_KEY_KP_3
|
||||
| keysyms::XKB_KEY_KP_4
|
||||
| keysyms::XKB_KEY_KP_5
|
||||
| keysyms::XKB_KEY_KP_6
|
||||
| keysyms::XKB_KEY_KP_7
|
||||
| keysyms::XKB_KEY_KP_8
|
||||
| keysyms::XKB_KEY_KP_9
|
||||
| keysyms::XKB_KEY_KP_Space
|
||||
| keysyms::XKB_KEY_KP_Tab
|
||||
| keysyms::XKB_KEY_KP_Enter
|
||||
| keysyms::XKB_KEY_KP_F1
|
||||
| keysyms::XKB_KEY_KP_F2
|
||||
| keysyms::XKB_KEY_KP_F3
|
||||
| keysyms::XKB_KEY_KP_F4
|
||||
| keysyms::XKB_KEY_KP_Home
|
||||
| keysyms::XKB_KEY_KP_Left
|
||||
| keysyms::XKB_KEY_KP_Up
|
||||
| keysyms::XKB_KEY_KP_Right
|
||||
| keysyms::XKB_KEY_KP_Down
|
||||
| keysyms::XKB_KEY_KP_Page_Up
|
||||
| keysyms::XKB_KEY_KP_Page_Down
|
||||
| keysyms::XKB_KEY_KP_End
|
||||
| keysyms::XKB_KEY_KP_Begin
|
||||
| keysyms::XKB_KEY_KP_Insert
|
||||
| keysyms::XKB_KEY_KP_Delete
|
||||
| keysyms::XKB_KEY_KP_Equal
|
||||
| keysyms::XKB_KEY_KP_Multiply
|
||||
| keysyms::XKB_KEY_KP_Add
|
||||
| keysyms::XKB_KEY_KP_Separator
|
||||
| keysyms::XKB_KEY_KP_Subtract
|
||||
| keysyms::XKB_KEY_KP_Decimal
|
||||
| keysyms::XKB_KEY_KP_Divide => KeyLocation::Numpad,
|
||||
_ => KeyLocation::Standard,
|
||||
}
|
||||
}
|
||||
2
src/platform_impl/linux/common/mod.rs
Normal file
2
src/platform_impl/linux/common/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod keymap;
|
||||
pub mod xkb_state;
|
||||
668
src/platform_impl/linux/common/xkb_state.rs
Normal file
668
src/platform_impl/linux/common/xkb_state.rs
Normal file
|
|
@ -0,0 +1,668 @@
|
|||
use std::convert::TryInto;
|
||||
use std::env;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use smol_str::SmolStr;
|
||||
use xkbcommon_dl::{
|
||||
self as ffi, xkb_state_component, XKBCOMMON_COMPOSE_HANDLE as XKBCH, XKBCOMMON_HANDLE as XKBH,
|
||||
};
|
||||
#[cfg(feature = "wayland")]
|
||||
use {memmap2::MmapOptions, wayland_backend::io_lifetimes::OwnedFd};
|
||||
#[cfg(feature = "x11")]
|
||||
use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::XKBCOMMON_X11_HANDLE as XKBXH};
|
||||
|
||||
use crate::event::KeyEvent;
|
||||
use crate::platform_impl::common::keymap;
|
||||
use crate::platform_impl::KeyEventExtra;
|
||||
use crate::{
|
||||
event::ElementState,
|
||||
keyboard::{Key, KeyCode, KeyLocation},
|
||||
};
|
||||
|
||||
// TODO: Wire this up without using a static `AtomicBool`.
|
||||
static RESET_DEAD_KEYS: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[inline(always)]
|
||||
pub fn reset_dead_keys() {
|
||||
RESET_DEAD_KEYS.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KbdState {
|
||||
#[cfg(feature = "x11")]
|
||||
xcb_connection: *mut xcb_connection_t,
|
||||
xkb_context: *mut ffi::xkb_context,
|
||||
xkb_keymap: *mut ffi::xkb_keymap,
|
||||
xkb_state: *mut ffi::xkb_state,
|
||||
xkb_compose_table: *mut ffi::xkb_compose_table,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
xkb_compose_state_2: *mut ffi::xkb_compose_state,
|
||||
mods_state: ModifiersState,
|
||||
#[cfg(feature = "x11")]
|
||||
pub core_keyboard_id: i32,
|
||||
scratch_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl KbdState {
|
||||
pub fn update_modifiers(
|
||||
&mut self,
|
||||
mods_depressed: u32,
|
||||
mods_latched: u32,
|
||||
mods_locked: u32,
|
||||
depressed_group: u32,
|
||||
latched_group: u32,
|
||||
locked_group: u32,
|
||||
) {
|
||||
if !self.ready() {
|
||||
return;
|
||||
}
|
||||
let mask = unsafe {
|
||||
(XKBH.xkb_state_update_mask)(
|
||||
self.xkb_state,
|
||||
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.mods_state.update_with(self.xkb_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_one_sym_raw(&mut self, keycode: u32) -> u32 {
|
||||
if !self.ready() {
|
||||
return 0;
|
||||
}
|
||||
unsafe { (XKBH.xkb_state_key_get_one_sym)(self.xkb_state, keycode) }
|
||||
}
|
||||
|
||||
pub fn get_utf8_raw(&mut self, keycode: u32) -> Option<SmolStr> {
|
||||
if !self.ready() {
|
||||
return None;
|
||||
}
|
||||
let xkb_state = self.xkb_state;
|
||||
self.make_string_with({
|
||||
|ptr, len| unsafe { (XKBH.xkb_state_key_get_utf8)(xkb_state, keycode, ptr, len) }
|
||||
})
|
||||
}
|
||||
|
||||
fn compose_feed_normal(&mut self, keysym: u32) -> Option<ffi::xkb_compose_feed_result> {
|
||||
self.compose_feed(self.xkb_compose_state, keysym)
|
||||
}
|
||||
|
||||
fn compose_feed_2(&mut self, keysym: u32) -> Option<ffi::xkb_compose_feed_result> {
|
||||
self.compose_feed(self.xkb_compose_state_2, keysym)
|
||||
}
|
||||
|
||||
fn compose_feed(
|
||||
&mut self,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
keysym: u32,
|
||||
) -> Option<ffi::xkb_compose_feed_result> {
|
||||
if !self.ready() || self.xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
if RESET_DEAD_KEYS.swap(false, Ordering::SeqCst) {
|
||||
unsafe { self.init_compose() };
|
||||
}
|
||||
Some(unsafe { (XKBCH.xkb_compose_state_feed)(xkb_compose_state, keysym) })
|
||||
}
|
||||
|
||||
fn compose_status_normal(&mut self) -> Option<ffi::xkb_compose_status> {
|
||||
self.compose_status(self.xkb_compose_state)
|
||||
}
|
||||
|
||||
fn compose_status(
|
||||
&mut self,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
) -> Option<ffi::xkb_compose_status> {
|
||||
if !self.ready() || xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { (XKBCH.xkb_compose_state_get_status)(xkb_compose_state) })
|
||||
}
|
||||
|
||||
fn compose_get_utf8_normal(&mut self) -> Option<SmolStr> {
|
||||
self.compose_get_utf8(self.xkb_compose_state)
|
||||
}
|
||||
|
||||
fn compose_get_utf8_2(&mut self) -> Option<SmolStr> {
|
||||
self.compose_get_utf8(self.xkb_compose_state_2)
|
||||
}
|
||||
|
||||
fn compose_get_utf8(
|
||||
&mut self,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
) -> Option<SmolStr> {
|
||||
if !self.ready() || xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
self.make_string_with(|ptr, len| unsafe {
|
||||
(XKBCH.xkb_compose_state_get_utf8)(xkb_compose_state, ptr, len)
|
||||
})
|
||||
}
|
||||
|
||||
/// Shared logic for constructing a string with `xkb_compose_state_get_utf8` and
|
||||
/// `xkb_state_key_get_utf8`.
|
||||
fn make_string_with<F>(&mut self, mut f: F) -> Option<SmolStr>
|
||||
where
|
||||
F: FnMut(*mut i8, usize) -> i32,
|
||||
{
|
||||
let size = f(ptr::null_mut(), 0);
|
||||
if size == 0 {
|
||||
return None;
|
||||
}
|
||||
let size = usize::try_from(size).unwrap();
|
||||
self.scratch_buffer.clear();
|
||||
// The allocated buffer must include space for the null-terminator
|
||||
self.scratch_buffer.reserve(size + 1);
|
||||
unsafe {
|
||||
let written = f(
|
||||
self.scratch_buffer.as_mut_ptr().cast(),
|
||||
self.scratch_buffer.capacity(),
|
||||
);
|
||||
if usize::try_from(written).unwrap() != size {
|
||||
// This will likely never happen
|
||||
return None;
|
||||
}
|
||||
self.scratch_buffer.set_len(size);
|
||||
};
|
||||
byte_slice_to_smol_str(&self.scratch_buffer)
|
||||
}
|
||||
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
if ffi::XKBCOMMON_OPTION.as_ref().is_none() {
|
||||
return Err(Error::XKBNotFound);
|
||||
}
|
||||
|
||||
let context =
|
||||
unsafe { (XKBH.xkb_context_new)(ffi::xkb_context_flags::XKB_CONTEXT_NO_FLAGS) };
|
||||
if context.is_null() {
|
||||
return Err(Error::XKBNotFound);
|
||||
}
|
||||
|
||||
let mut me = Self {
|
||||
#[cfg(feature = "x11")]
|
||||
xcb_connection: ptr::null_mut(),
|
||||
xkb_context: context,
|
||||
xkb_keymap: ptr::null_mut(),
|
||||
xkb_state: ptr::null_mut(),
|
||||
xkb_compose_table: ptr::null_mut(),
|
||||
xkb_compose_state: ptr::null_mut(),
|
||||
xkb_compose_state_2: ptr::null_mut(),
|
||||
mods_state: ModifiersState::new(),
|
||||
#[cfg(feature = "x11")]
|
||||
core_keyboard_id: 0,
|
||||
scratch_buffer: Vec::new(),
|
||||
};
|
||||
|
||||
unsafe { me.init_compose() };
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
pub fn from_x11_xkb(connection: *mut xcb_connection_t) -> Result<Self, Error> {
|
||||
let mut me = Self::new()?;
|
||||
me.xcb_connection = connection;
|
||||
|
||||
let result = unsafe {
|
||||
(XKBXH.xkb_x11_setup_xkb_extension)(
|
||||
connection,
|
||||
1,
|
||||
2,
|
||||
xkbcommon_dl::x11::xkb_x11_setup_xkb_extension_flags::XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
assert_eq!(result, 1, "Failed to initialize libxkbcommon");
|
||||
|
||||
unsafe { me.init_with_x11_keymap() };
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
unsafe fn init_compose(&mut self) {
|
||||
let locale = env::var_os("LC_ALL")
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.or_else(|| env::var_os("LC_CTYPE"))
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.or_else(|| env::var_os("LANG"))
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.unwrap_or_else(|| "C".into());
|
||||
let locale = CString::new(locale.into_vec()).unwrap();
|
||||
|
||||
let compose_table = (XKBCH.xkb_compose_table_new_from_locale)(
|
||||
self.xkb_context,
|
||||
locale.as_ptr(),
|
||||
ffi::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
|
||||
);
|
||||
|
||||
if compose_table.is_null() {
|
||||
// init of compose table failed, continue without compose
|
||||
return;
|
||||
}
|
||||
|
||||
let compose_state = (XKBCH.xkb_compose_state_new)(
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
|
||||
);
|
||||
|
||||
if compose_state.is_null() {
|
||||
// init of compose state failed, continue without compose
|
||||
(XKBCH.xkb_compose_table_unref)(compose_table);
|
||||
return;
|
||||
}
|
||||
|
||||
let compose_state_2 = (XKBCH.xkb_compose_state_new)(
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
|
||||
);
|
||||
|
||||
if compose_state_2.is_null() {
|
||||
// init of compose state failed, continue without compose
|
||||
(XKBCH.xkb_compose_table_unref)(compose_table);
|
||||
(XKBCH.xkb_compose_state_unref)(compose_state);
|
||||
return;
|
||||
}
|
||||
|
||||
self.xkb_compose_table = compose_table;
|
||||
self.xkb_compose_state = compose_state;
|
||||
self.xkb_compose_state_2 = compose_state_2;
|
||||
}
|
||||
|
||||
unsafe fn post_init(&mut self, state: *mut ffi::xkb_state, keymap: *mut ffi::xkb_keymap) {
|
||||
self.xkb_keymap = keymap;
|
||||
self.xkb_state = state;
|
||||
self.mods_state.update_with(state);
|
||||
}
|
||||
|
||||
unsafe fn de_init(&mut self) {
|
||||
(XKBH.xkb_state_unref)(self.xkb_state);
|
||||
self.xkb_state = ptr::null_mut();
|
||||
(XKBH.xkb_keymap_unref)(self.xkb_keymap);
|
||||
self.xkb_keymap = ptr::null_mut();
|
||||
}
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
pub unsafe fn init_with_x11_keymap(&mut self) {
|
||||
if !self.xkb_keymap.is_null() {
|
||||
self.de_init();
|
||||
}
|
||||
|
||||
// TODO: Support keyboards other than the "virtual core keyboard device".
|
||||
self.core_keyboard_id = (XKBXH.xkb_x11_get_core_keyboard_device_id)(self.xcb_connection);
|
||||
let keymap = (XKBXH.xkb_x11_keymap_new_from_device)(
|
||||
self.xkb_context,
|
||||
self.xcb_connection,
|
||||
self.core_keyboard_id,
|
||||
xkbcommon_dl::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
);
|
||||
if keymap.is_null() {
|
||||
panic!("Failed to get keymap from X11 server.");
|
||||
}
|
||||
|
||||
let state = (XKBXH.xkb_x11_state_new_from_device)(
|
||||
keymap,
|
||||
self.xcb_connection,
|
||||
self.core_keyboard_id,
|
||||
);
|
||||
self.post_init(state, keymap);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub unsafe fn init_with_fd(&mut self, fd: OwnedFd, size: usize) {
|
||||
if !self.xkb_keymap.is_null() {
|
||||
self.de_init();
|
||||
}
|
||||
|
||||
let map = MmapOptions::new()
|
||||
.len(size)
|
||||
.map_copy_read_only(&fd)
|
||||
.unwrap();
|
||||
|
||||
let keymap = (XKBH.xkb_keymap_new_from_string)(
|
||||
self.xkb_context,
|
||||
map.as_ptr() as *const _,
|
||||
ffi::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||
);
|
||||
|
||||
if keymap.is_null() {
|
||||
panic!("Received invalid keymap from compositor.");
|
||||
}
|
||||
|
||||
let state = (XKBH.xkb_state_new)(keymap);
|
||||
self.post_init(state, keymap);
|
||||
}
|
||||
|
||||
#[cfg(feature = "wayland")]
|
||||
pub fn key_repeats(&mut self, keycode: ffi::xkb_keycode_t) -> bool {
|
||||
unsafe { (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, keycode) == 1 }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ready(&self) -> bool {
|
||||
!self.xkb_state.is_null()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn mods_state(&self) -> ModifiersState {
|
||||
self.mods_state
|
||||
}
|
||||
|
||||
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 = event.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 {
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
};
|
||||
|
||||
KeyEvent {
|
||||
physical_key,
|
||||
logical_key,
|
||||
text,
|
||||
location,
|
||||
state,
|
||||
repeat,
|
||||
platform_specific,
|
||||
}
|
||||
}
|
||||
|
||||
fn keysym_to_utf8_raw(&mut self, keysym: u32) -> Option<SmolStr> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KbdState {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.xkb_compose_state.is_null() {
|
||||
(XKBCH.xkb_compose_state_unref)(self.xkb_compose_state);
|
||||
}
|
||||
if !self.xkb_compose_state_2.is_null() {
|
||||
(XKBCH.xkb_compose_state_unref)(self.xkb_compose_state_2);
|
||||
}
|
||||
if !self.xkb_compose_table.is_null() {
|
||||
(XKBCH.xkb_compose_table_unref)(self.xkb_compose_table);
|
||||
}
|
||||
if !self.xkb_state.is_null() {
|
||||
(XKBH.xkb_state_unref)(self.xkb_state);
|
||||
}
|
||||
if !self.xkb_keymap.is_null() {
|
||||
(XKBH.xkb_keymap_unref)(self.xkb_keymap);
|
||||
}
|
||||
(XKBH.xkb_context_unref)(self.xkb_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyEventResults<'a> {
|
||||
state: &'a mut KbdState,
|
||||
keycode: u32,
|
||||
keysym: u32,
|
||||
compose: Option<XkbCompose>,
|
||||
}
|
||||
|
||||
impl<'a> KeyEventResults<'a> {
|
||||
fn new(state: &'a mut KbdState, keycode: u32, compose: bool) -> Self {
|
||||
let keysym = state.get_one_sym_raw(keycode);
|
||||
|
||||
let compose = if compose {
|
||||
Some(match state.compose_feed_normal(keysym) {
|
||||
Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => {
|
||||
// Unwrapping is safe here, as `compose_feed` returns `None` when composition is uninitialized.
|
||||
XkbCompose::Accepted(state.compose_status_normal().unwrap())
|
||||
}
|
||||
Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED) => XkbCompose::Ignored,
|
||||
None => XkbCompose::Uninitialized,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
KeyEventResults {
|
||||
state,
|
||||
keycode,
|
||||
keysym,
|
||||
compose,
|
||||
}
|
||||
}
|
||||
|
||||
fn keycode(&mut self) -> KeyCode {
|
||||
keymap::raw_keycode_to_keycode(self.keycode)
|
||||
}
|
||||
|
||||
pub fn key(&mut self) -> (Key, KeyLocation) {
|
||||
self.keysym_to_key(self.keysym)
|
||||
.unwrap_or_else(|(key, location)| match self.compose {
|
||||
Some(XkbCompose::Accepted(ffi::xkb_compose_status::XKB_COMPOSE_COMPOSING)) => {
|
||||
// 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.
|
||||
self.state.compose_feed_2(self.keysym);
|
||||
match self.state.compose_feed_2(self.keysym) {
|
||||
Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_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`.
|
||||
Key::Dead(
|
||||
self.state
|
||||
.compose_get_utf8_2()
|
||||
.map(|s| s.chars().next().unwrap()),
|
||||
),
|
||||
location,
|
||||
),
|
||||
_ => (key, location),
|
||||
}
|
||||
}
|
||||
_ => (
|
||||
self.composed_text()
|
||||
.unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym))
|
||||
.map(Key::Character)
|
||||
.unwrap_or(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 mut keysyms = ptr::null();
|
||||
let keysym_count = unsafe {
|
||||
(XKBH.xkb_keymap_key_get_syms_by_level)(
|
||||
self.state.xkb_keymap,
|
||||
self.keycode,
|
||||
0,
|
||||
0,
|
||||
&mut keysyms,
|
||||
)
|
||||
};
|
||||
let keysym = if keysym_count == 1 {
|
||||
unsafe { *keysyms }
|
||||
} else {
|
||||
0
|
||||
};
|
||||
self.keysym_to_key(keysym)
|
||||
.unwrap_or_else(|(key, location)| {
|
||||
(
|
||||
self.state
|
||||
.keysym_to_utf8_raw(keysym)
|
||||
.map(Key::Character)
|
||||
.unwrap_or(key),
|
||||
location,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn keysym_to_key(&mut self, keysym: u32) -> Result<(Key, KeyLocation), (Key, KeyLocation)> {
|
||||
let location = super::keymap::keysym_location(keysym);
|
||||
let key = super::keymap::keysym_to_key(keysym);
|
||||
if matches!(key, Key::Unidentified(_)) {
|
||||
Err((key, location))
|
||||
} else {
|
||||
Ok((key, location))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text(&mut self) -> Option<SmolStr> {
|
||||
self.composed_text()
|
||||
.unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym))
|
||||
}
|
||||
|
||||
pub fn text_with_all_modifiers(&mut self) -> Option<SmolStr> {
|
||||
// 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.
|
||||
self.composed_text()
|
||||
.unwrap_or_else(|_| self.state.get_utf8_raw(self.keycode))
|
||||
}
|
||||
|
||||
fn composed_text(&mut self) -> Result<Option<SmolStr>, ()> {
|
||||
if let Some(compose) = &self.compose {
|
||||
match compose {
|
||||
XkbCompose::Accepted(status) => match status {
|
||||
ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => {
|
||||
Ok(self.state.compose_get_utf8_normal())
|
||||
}
|
||||
ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => Err(()),
|
||||
_ => Ok(None),
|
||||
},
|
||||
XkbCompose::Ignored | XkbCompose::Uninitialized => Err(()),
|
||||
}
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
///
|
||||
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
|
||||
///
|
||||
/// For some modifiers, this means that the key is currently pressed, others are toggled
|
||||
/// (like caps lock).
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ModifiersState {
|
||||
/// The "control" key
|
||||
pub ctrl: bool,
|
||||
/// The "alt" key
|
||||
pub alt: bool,
|
||||
/// The "shift" key
|
||||
pub shift: bool,
|
||||
/// The "Caps lock" key
|
||||
pub caps_lock: bool,
|
||||
/// The "logo" key
|
||||
///
|
||||
/// Also known as the "windows" key on most keyboards
|
||||
pub logo: bool,
|
||||
/// The "Num lock" key
|
||||
pub num_lock: bool,
|
||||
}
|
||||
|
||||
impl ModifiersState {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn update_with(&mut self, state: *mut ffi::xkb_state) {
|
||||
let mod_name_is_active = |mod_name: &[u8]| unsafe {
|
||||
(XKBH.xkb_state_mod_name_is_active)(
|
||||
state,
|
||||
mod_name.as_ptr() as *const c_char,
|
||||
xkb_state_component::XKB_STATE_MODS_EFFECTIVE,
|
||||
) > 0
|
||||
};
|
||||
self.ctrl = mod_name_is_active(ffi::XKB_MOD_NAME_CTRL);
|
||||
self.alt = mod_name_is_active(ffi::XKB_MOD_NAME_ALT);
|
||||
self.shift = mod_name_is_active(ffi::XKB_MOD_NAME_SHIFT);
|
||||
self.caps_lock = mod_name_is_active(ffi::XKB_MOD_NAME_CAPS);
|
||||
self.logo = mod_name_is_active(ffi::XKB_MOD_NAME_LOGO);
|
||||
self.num_lock = mod_name_is_active(ffi::XKB_MOD_NAME_NUM);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ModifiersState> for crate::keyboard::ModifiersState {
|
||||
fn from(mods: ModifiersState) -> crate::keyboard::ModifiersState {
|
||||
let mut to_mods = crate::keyboard::ModifiersState::empty();
|
||||
to_mods.set(crate::keyboard::ModifiersState::SHIFT, mods.shift);
|
||||
to_mods.set(crate::keyboard::ModifiersState::CONTROL, mods.ctrl);
|
||||
to_mods.set(crate::keyboard::ModifiersState::ALT, mods.alt);
|
||||
to_mods.set(crate::keyboard::ModifiersState::SUPER, mods.logo);
|
||||
to_mods
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// libxkbcommon is not available
|
||||
XKBNotFound,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum XkbCompose {
|
||||
Accepted(ffi::xkb_compose_status),
|
||||
Ignored,
|
||||
Uninitialized,
|
||||
}
|
||||
|
||||
// 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<SmolStr> {
|
||||
std::str::from_utf8(bytes)
|
||||
.map(SmolStr::new)
|
||||
.map_err(|e| {
|
||||
warn!(
|
||||
"UTF-8 received from libxkbcommon ({:?}) was invalid: {e}",
|
||||
bytes
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ use std::{
|
|||
#[cfg(x11_platform)]
|
||||
use once_cell::sync::Lazy;
|
||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
pub use self::x11::XNotSupported;
|
||||
|
|
@ -28,11 +29,13 @@ use crate::platform::x11::XlibErrorHook;
|
|||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
event::Event,
|
||||
event::{Event, KeyEvent},
|
||||
event_loop::{
|
||||
ControlFlow, DeviceEventFilter, EventLoopClosed, EventLoopWindowTarget as RootELW,
|
||||
},
|
||||
icon::Icon,
|
||||
keyboard::{Key, KeyCode},
|
||||
platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode},
|
||||
window::{
|
||||
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
WindowAttributes, WindowButtons, WindowLevel,
|
||||
|
|
@ -42,6 +45,7 @@ use crate::{
|
|||
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
|
||||
pub(self) use crate::platform_impl::Fullscreen;
|
||||
|
||||
pub mod common;
|
||||
#[cfg(wayland_platform)]
|
||||
pub mod wayland;
|
||||
#[cfg(x11_platform)]
|
||||
|
|
@ -514,6 +518,11 @@ impl Window {
|
|||
x11_or_wayland!(match self; Window(w) => w.set_ime_position(position))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset_dead_keys(&self) {
|
||||
common::xkb_state::reset_dead_keys()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ime_allowed(&self, allowed: bool) {
|
||||
x11_or_wayland!(match self; Window(w) => w.set_ime_allowed(allowed))
|
||||
|
|
@ -624,6 +633,37 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub key_without_modifiers: Key,
|
||||
pub text_with_all_modifiers: Option<SmolStr>,
|
||||
}
|
||||
|
||||
impl KeyEventExtModifierSupplement for KeyEvent {
|
||||
#[inline]
|
||||
fn text_with_all_modifiers(&self) -> Option<&str> {
|
||||
self.platform_specific
|
||||
.text_with_all_modifiers
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn key_without_modifiers(&self) -> Key {
|
||||
self.platform_specific.key_without_modifiers.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyCodeExtScancode for KeyCode {
|
||||
fn from_scancode(scancode: u32) -> KeyCode {
|
||||
common::keymap::raw_keycode_to_keycode(scancode)
|
||||
}
|
||||
|
||||
fn to_scancode(self) -> Option<u32> {
|
||||
common::keymap::keycode_to_raw(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hooks for X11 errors.
|
||||
#[cfg(x11_platform)]
|
||||
pub(crate) static mut XLIB_ERROR_HOOKS: Lazy<Mutex<Vec<XlibErrorHook>>> =
|
||||
|
|
|
|||
|
|
@ -1,192 +0,0 @@
|
|||
use sctk::seat::keyboard::keysyms;
|
||||
|
||||
use crate::event::VirtualKeyCode;
|
||||
|
||||
/// Convert xkb keysym into winit's VirtualKeyCode.
|
||||
pub fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
match keysym {
|
||||
// Numbers.
|
||||
keysyms::XKB_KEY_1 => Some(VirtualKeyCode::Key1),
|
||||
keysyms::XKB_KEY_2 => Some(VirtualKeyCode::Key2),
|
||||
keysyms::XKB_KEY_3 => Some(VirtualKeyCode::Key3),
|
||||
keysyms::XKB_KEY_4 => Some(VirtualKeyCode::Key4),
|
||||
keysyms::XKB_KEY_5 => Some(VirtualKeyCode::Key5),
|
||||
keysyms::XKB_KEY_6 => Some(VirtualKeyCode::Key6),
|
||||
keysyms::XKB_KEY_7 => Some(VirtualKeyCode::Key7),
|
||||
keysyms::XKB_KEY_8 => Some(VirtualKeyCode::Key8),
|
||||
keysyms::XKB_KEY_9 => Some(VirtualKeyCode::Key9),
|
||||
keysyms::XKB_KEY_0 => Some(VirtualKeyCode::Key0),
|
||||
// Letters.
|
||||
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),
|
||||
keysyms::XKB_KEY_B | keysyms::XKB_KEY_b => Some(VirtualKeyCode::B),
|
||||
keysyms::XKB_KEY_C | keysyms::XKB_KEY_c => Some(VirtualKeyCode::C),
|
||||
keysyms::XKB_KEY_D | keysyms::XKB_KEY_d => Some(VirtualKeyCode::D),
|
||||
keysyms::XKB_KEY_E | keysyms::XKB_KEY_e => Some(VirtualKeyCode::E),
|
||||
keysyms::XKB_KEY_F | keysyms::XKB_KEY_f => Some(VirtualKeyCode::F),
|
||||
keysyms::XKB_KEY_G | keysyms::XKB_KEY_g => Some(VirtualKeyCode::G),
|
||||
keysyms::XKB_KEY_H | keysyms::XKB_KEY_h => Some(VirtualKeyCode::H),
|
||||
keysyms::XKB_KEY_I | keysyms::XKB_KEY_i => Some(VirtualKeyCode::I),
|
||||
keysyms::XKB_KEY_J | keysyms::XKB_KEY_j => Some(VirtualKeyCode::J),
|
||||
keysyms::XKB_KEY_K | keysyms::XKB_KEY_k => Some(VirtualKeyCode::K),
|
||||
keysyms::XKB_KEY_L | keysyms::XKB_KEY_l => Some(VirtualKeyCode::L),
|
||||
keysyms::XKB_KEY_M | keysyms::XKB_KEY_m => Some(VirtualKeyCode::M),
|
||||
keysyms::XKB_KEY_N | keysyms::XKB_KEY_n => Some(VirtualKeyCode::N),
|
||||
keysyms::XKB_KEY_O | keysyms::XKB_KEY_o => Some(VirtualKeyCode::O),
|
||||
keysyms::XKB_KEY_P | keysyms::XKB_KEY_p => Some(VirtualKeyCode::P),
|
||||
keysyms::XKB_KEY_Q | keysyms::XKB_KEY_q => Some(VirtualKeyCode::Q),
|
||||
keysyms::XKB_KEY_R | keysyms::XKB_KEY_r => Some(VirtualKeyCode::R),
|
||||
keysyms::XKB_KEY_S | keysyms::XKB_KEY_s => Some(VirtualKeyCode::S),
|
||||
keysyms::XKB_KEY_T | keysyms::XKB_KEY_t => Some(VirtualKeyCode::T),
|
||||
keysyms::XKB_KEY_U | keysyms::XKB_KEY_u => Some(VirtualKeyCode::U),
|
||||
keysyms::XKB_KEY_V | keysyms::XKB_KEY_v => Some(VirtualKeyCode::V),
|
||||
keysyms::XKB_KEY_W | keysyms::XKB_KEY_w => Some(VirtualKeyCode::W),
|
||||
keysyms::XKB_KEY_X | keysyms::XKB_KEY_x => Some(VirtualKeyCode::X),
|
||||
keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y),
|
||||
keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z),
|
||||
// Escape.
|
||||
keysyms::XKB_KEY_Escape => Some(VirtualKeyCode::Escape),
|
||||
// Function keys.
|
||||
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
|
||||
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
|
||||
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
|
||||
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
|
||||
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
|
||||
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
|
||||
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
|
||||
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
|
||||
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
|
||||
keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10),
|
||||
keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11),
|
||||
keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12),
|
||||
keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13),
|
||||
keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14),
|
||||
keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15),
|
||||
keysyms::XKB_KEY_F16 => Some(VirtualKeyCode::F16),
|
||||
keysyms::XKB_KEY_F17 => Some(VirtualKeyCode::F17),
|
||||
keysyms::XKB_KEY_F18 => Some(VirtualKeyCode::F18),
|
||||
keysyms::XKB_KEY_F19 => Some(VirtualKeyCode::F19),
|
||||
keysyms::XKB_KEY_F20 => Some(VirtualKeyCode::F20),
|
||||
keysyms::XKB_KEY_F21 => Some(VirtualKeyCode::F21),
|
||||
keysyms::XKB_KEY_F22 => Some(VirtualKeyCode::F22),
|
||||
keysyms::XKB_KEY_F23 => Some(VirtualKeyCode::F23),
|
||||
keysyms::XKB_KEY_F24 => Some(VirtualKeyCode::F24),
|
||||
// Flow control.
|
||||
keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot),
|
||||
keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll),
|
||||
keysyms::XKB_KEY_Pause => Some(VirtualKeyCode::Pause),
|
||||
keysyms::XKB_KEY_Insert => Some(VirtualKeyCode::Insert),
|
||||
keysyms::XKB_KEY_Home => Some(VirtualKeyCode::Home),
|
||||
keysyms::XKB_KEY_Delete => Some(VirtualKeyCode::Delete),
|
||||
keysyms::XKB_KEY_End => Some(VirtualKeyCode::End),
|
||||
keysyms::XKB_KEY_Page_Down => Some(VirtualKeyCode::PageDown),
|
||||
keysyms::XKB_KEY_Page_Up => Some(VirtualKeyCode::PageUp),
|
||||
// Arrows.
|
||||
keysyms::XKB_KEY_Left => Some(VirtualKeyCode::Left),
|
||||
keysyms::XKB_KEY_Up => Some(VirtualKeyCode::Up),
|
||||
keysyms::XKB_KEY_Right => Some(VirtualKeyCode::Right),
|
||||
keysyms::XKB_KEY_Down => Some(VirtualKeyCode::Down),
|
||||
|
||||
keysyms::XKB_KEY_BackSpace => Some(VirtualKeyCode::Back),
|
||||
keysyms::XKB_KEY_Return => Some(VirtualKeyCode::Return),
|
||||
keysyms::XKB_KEY_space => Some(VirtualKeyCode::Space),
|
||||
|
||||
keysyms::XKB_KEY_Multi_key => Some(VirtualKeyCode::Compose),
|
||||
keysyms::XKB_KEY_caret => Some(VirtualKeyCode::Caret),
|
||||
|
||||
// Keypad.
|
||||
keysyms::XKB_KEY_Num_Lock => Some(VirtualKeyCode::Numlock),
|
||||
keysyms::XKB_KEY_KP_0 => Some(VirtualKeyCode::Numpad0),
|
||||
keysyms::XKB_KEY_KP_1 => Some(VirtualKeyCode::Numpad1),
|
||||
keysyms::XKB_KEY_KP_2 => Some(VirtualKeyCode::Numpad2),
|
||||
keysyms::XKB_KEY_KP_3 => Some(VirtualKeyCode::Numpad3),
|
||||
keysyms::XKB_KEY_KP_4 => Some(VirtualKeyCode::Numpad4),
|
||||
keysyms::XKB_KEY_KP_5 => Some(VirtualKeyCode::Numpad5),
|
||||
keysyms::XKB_KEY_KP_6 => Some(VirtualKeyCode::Numpad6),
|
||||
keysyms::XKB_KEY_KP_7 => Some(VirtualKeyCode::Numpad7),
|
||||
keysyms::XKB_KEY_KP_8 => Some(VirtualKeyCode::Numpad8),
|
||||
keysyms::XKB_KEY_KP_9 => Some(VirtualKeyCode::Numpad9),
|
||||
// Misc.
|
||||
// => Some(VirtualKeyCode::AbntC1),
|
||||
// => Some(VirtualKeyCode::AbntC2),
|
||||
keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Plus),
|
||||
keysyms::XKB_KEY_apostrophe => Some(VirtualKeyCode::Apostrophe),
|
||||
// => Some(VirtualKeyCode::Apps),
|
||||
keysyms::XKB_KEY_at => Some(VirtualKeyCode::At),
|
||||
// => Some(VirtualKeyCode::Ax),
|
||||
keysyms::XKB_KEY_backslash => Some(VirtualKeyCode::Backslash),
|
||||
keysyms::XKB_KEY_XF86Calculator => Some(VirtualKeyCode::Calculator),
|
||||
// => Some(VirtualKeyCode::Capital),
|
||||
keysyms::XKB_KEY_colon => Some(VirtualKeyCode::Colon),
|
||||
keysyms::XKB_KEY_comma => Some(VirtualKeyCode::Comma),
|
||||
// => Some(VirtualKeyCode::Convert),
|
||||
keysyms::XKB_KEY_equal => Some(VirtualKeyCode::Equals),
|
||||
keysyms::XKB_KEY_grave => Some(VirtualKeyCode::Grave),
|
||||
// => Some(VirtualKeyCode::Kana),
|
||||
keysyms::XKB_KEY_Kanji => Some(VirtualKeyCode::Kanji),
|
||||
keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt),
|
||||
keysyms::XKB_KEY_bracketleft => Some(VirtualKeyCode::LBracket),
|
||||
keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl),
|
||||
keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift),
|
||||
keysyms::XKB_KEY_Super_L => Some(VirtualKeyCode::LWin),
|
||||
keysyms::XKB_KEY_XF86Mail => Some(VirtualKeyCode::Mail),
|
||||
// => Some(VirtualKeyCode::MediaSelect),
|
||||
// => Some(VirtualKeyCode::MediaStop),
|
||||
keysyms::XKB_KEY_minus => Some(VirtualKeyCode::Minus),
|
||||
keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Asterisk),
|
||||
keysyms::XKB_KEY_XF86AudioMute => Some(VirtualKeyCode::Mute),
|
||||
// => Some(VirtualKeyCode::MyComputer),
|
||||
keysyms::XKB_KEY_XF86AudioNext => Some(VirtualKeyCode::NextTrack),
|
||||
// => Some(VirtualKeyCode::NoConvert),
|
||||
keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma),
|
||||
keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter),
|
||||
keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals),
|
||||
keysyms::XKB_KEY_KP_Add => Some(VirtualKeyCode::NumpadAdd),
|
||||
keysyms::XKB_KEY_KP_Subtract => Some(VirtualKeyCode::NumpadSubtract),
|
||||
keysyms::XKB_KEY_KP_Multiply => Some(VirtualKeyCode::NumpadMultiply),
|
||||
keysyms::XKB_KEY_KP_Divide => Some(VirtualKeyCode::NumpadDivide),
|
||||
keysyms::XKB_KEY_KP_Decimal => Some(VirtualKeyCode::NumpadDecimal),
|
||||
keysyms::XKB_KEY_KP_Page_Up => Some(VirtualKeyCode::PageUp),
|
||||
keysyms::XKB_KEY_KP_Page_Down => Some(VirtualKeyCode::PageDown),
|
||||
keysyms::XKB_KEY_KP_Home => Some(VirtualKeyCode::Home),
|
||||
keysyms::XKB_KEY_KP_End => Some(VirtualKeyCode::End),
|
||||
keysyms::XKB_KEY_KP_Left => Some(VirtualKeyCode::Left),
|
||||
keysyms::XKB_KEY_KP_Up => Some(VirtualKeyCode::Up),
|
||||
keysyms::XKB_KEY_KP_Right => Some(VirtualKeyCode::Right),
|
||||
keysyms::XKB_KEY_KP_Down => Some(VirtualKeyCode::Down),
|
||||
// => Some(VirtualKeyCode::OEM102),
|
||||
keysyms::XKB_KEY_period => Some(VirtualKeyCode::Period),
|
||||
// => Some(VirtualKeyCode::Playpause),
|
||||
keysyms::XKB_KEY_XF86PowerOff => Some(VirtualKeyCode::Power),
|
||||
keysyms::XKB_KEY_XF86AudioPrev => Some(VirtualKeyCode::PrevTrack),
|
||||
keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt),
|
||||
keysyms::XKB_KEY_bracketright => Some(VirtualKeyCode::RBracket),
|
||||
keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl),
|
||||
keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift),
|
||||
keysyms::XKB_KEY_Super_R => Some(VirtualKeyCode::RWin),
|
||||
keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon),
|
||||
keysyms::XKB_KEY_slash => Some(VirtualKeyCode::Slash),
|
||||
keysyms::XKB_KEY_XF86Sleep => Some(VirtualKeyCode::Sleep),
|
||||
// => Some(VirtualKeyCode::Stop),
|
||||
// => Some(VirtualKeyCode::Sysrq),
|
||||
keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab),
|
||||
keysyms::XKB_KEY_ISO_Left_Tab => Some(VirtualKeyCode::Tab),
|
||||
keysyms::XKB_KEY_underscore => Some(VirtualKeyCode::Underline),
|
||||
// => Some(VirtualKeyCode::Unlabeled),
|
||||
keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown),
|
||||
keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(VirtualKeyCode::VolumeUp),
|
||||
// => Some(VirtualKeyCode::Wake),
|
||||
// => Some(VirtualKeyCode::Webback),
|
||||
// => Some(VirtualKeyCode::WebFavorites),
|
||||
// => Some(VirtualKeyCode::WebForward),
|
||||
// => Some(VirtualKeyCode::WebHome),
|
||||
// => Some(VirtualKeyCode::WebRefresh),
|
||||
// => Some(VirtualKeyCode::WebSearch),
|
||||
// => Some(VirtualKeyCode::WebStop),
|
||||
keysyms::XKB_KEY_yen => Some(VirtualKeyCode::Yen),
|
||||
keysyms::XKB_KEY_XF86Copy => Some(VirtualKeyCode::Copy),
|
||||
keysyms::XKB_KEY_XF86Paste => Some(VirtualKeyCode::Paste),
|
||||
keysyms::XKB_KEY_XF86Cut => Some(VirtualKeyCode::Cut),
|
||||
// Fallback.
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,242 +1,373 @@
|
|||
//! The keyboard input handling.
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
|
||||
use calloop::timer::{TimeoutAction, Timer};
|
||||
use calloop::{LoopHandle, RegistrationToken};
|
||||
use log::warn;
|
||||
|
||||
use sctk::reexports::client::delegate_dispatch;
|
||||
use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
|
||||
use sctk::reexports::client::protocol::wl_keyboard::{
|
||||
Event as WlKeyboardEvent, KeyState as WlKeyState, KeymapFormat as WlKeymapFormat,
|
||||
};
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum};
|
||||
|
||||
use sctk::seat::keyboard::{KeyEvent, KeyboardData, KeyboardDataExt, KeyboardHandler, Modifiers};
|
||||
use sctk::seat::SeatState;
|
||||
|
||||
use crate::event::{ElementState, ModifiersState, WindowEvent};
|
||||
use crate::event::{ElementState, WindowEvent};
|
||||
use crate::keyboard::ModifiersState;
|
||||
|
||||
use crate::platform_impl::common::xkb_state::KbdState;
|
||||
use crate::platform_impl::wayland::event_loop::sink::EventSink;
|
||||
use crate::platform_impl::wayland::seat::WinitSeatState;
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
use crate::platform_impl::wayland::{self, DeviceId, WindowId};
|
||||
|
||||
mod keymap;
|
||||
|
||||
impl WinitState {
|
||||
pub fn handle_key_input(
|
||||
&mut self,
|
||||
keyboard: &WlKeyboard,
|
||||
event: KeyEvent,
|
||||
state: ElementState,
|
||||
impl Dispatch<WlKeyboard, KeyboardData, WinitState> for WinitState {
|
||||
fn event(
|
||||
state: &mut WinitState,
|
||||
wl_keyboard: &WlKeyboard,
|
||||
event: <WlKeyboard as Proxy>::Event,
|
||||
data: &KeyboardData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<WinitState>,
|
||||
) {
|
||||
let window_id = match *keyboard.winit_data().window_id.lock().unwrap() {
|
||||
Some(window_id) => window_id,
|
||||
let seat_state = match state.seats.get_mut(&data.seat.id()) {
|
||||
Some(seat_state) => seat_state,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let virtual_keycode = keymap::keysym_to_vkey(event.keysym);
|
||||
|
||||
let seat_state = self.seats.get(&keyboard.seat().id()).unwrap();
|
||||
let modifiers = seat_state.modifiers;
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
#[allow(deprecated)]
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||
DeviceId,
|
||||
)),
|
||||
input: crate::event::KeyboardInput {
|
||||
state,
|
||||
scancode: event.raw_code,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
match event {
|
||||
WlKeyboardEvent::Keymap { format, fd, size } => match format {
|
||||
WEnum::Value(format) => match format {
|
||||
WlKeymapFormat::NoKeymap => {
|
||||
warn!("non-xkb compatible keymap")
|
||||
}
|
||||
WlKeymapFormat::XkbV1 => unsafe {
|
||||
seat_state
|
||||
.keyboard_state
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.xkb_state
|
||||
.init_with_fd(fd, size as usize);
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
WEnum::Unknown(value) => {
|
||||
warn!("unknown keymap format 0x{:x}", value)
|
||||
}
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
WlKeyboardEvent::Enter { surface, .. } => {
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
|
||||
// Don't send utf8 chars on release.
|
||||
if state == ElementState::Released {
|
||||
return;
|
||||
}
|
||||
// Mark the window as focused.
|
||||
match state.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().set_has_focus(true),
|
||||
None => return,
|
||||
};
|
||||
|
||||
if let Some(txt) = event.utf8 {
|
||||
for ch in txt.chars() {
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::ReceivedCharacter(ch), window_id);
|
||||
// Drop the repeat, if there were any.
|
||||
seat_state.keyboard_state.as_mut().unwrap().current_repeat = None;
|
||||
|
||||
// The keyboard focus is considered as general focus.
|
||||
state
|
||||
.events_sink
|
||||
.push_window_event(WindowEvent::Focused(true), window_id);
|
||||
|
||||
*data.window_id.lock().unwrap() = Some(window_id);
|
||||
|
||||
// HACK: this is just for GNOME not fixing their ordering issue of modifiers.
|
||||
if std::mem::take(&mut seat_state.modifiers_pending) {
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(seat_state.modifiers.into()),
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
WlKeyboardEvent::Leave { surface, .. } => {
|
||||
let window_id = wayland::make_wid(&surface);
|
||||
|
||||
// NOTE: we should drop the repeat regardless whethere it was for the present
|
||||
// window of for the window which just went gone.
|
||||
seat_state.keyboard_state.as_mut().unwrap().current_repeat = None;
|
||||
|
||||
// NOTE: The check whether the window exists is essential as we might get a
|
||||
// nil surface, regardless of what protocol says.
|
||||
match state.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().set_has_focus(false),
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Notify that no modifiers are being pressed.
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
|
||||
window_id,
|
||||
);
|
||||
|
||||
// We don't need to update it above, because the next `Enter` will overwrite
|
||||
// anyway.
|
||||
*data.window_id.lock().unwrap() = None;
|
||||
|
||||
state
|
||||
.events_sink
|
||||
.push_window_event(WindowEvent::Focused(false), window_id);
|
||||
}
|
||||
WlKeyboardEvent::Key {
|
||||
key,
|
||||
state: key_state,
|
||||
..
|
||||
} if key_state == WEnum::Value(WlKeyState::Pressed) => {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
key,
|
||||
ElementState::Pressed,
|
||||
false,
|
||||
);
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
let delay = match keyboard_state.repeat_info {
|
||||
RepeatInfo::Repeat { delay, .. } => delay,
|
||||
RepeatInfo::Disable => return,
|
||||
};
|
||||
|
||||
if !keyboard_state.xkb_state.key_repeats(key) {
|
||||
return;
|
||||
}
|
||||
|
||||
keyboard_state.current_repeat = Some(key);
|
||||
|
||||
// NOTE terminate ongoing timer and start a new timer.
|
||||
|
||||
if let Some(token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(token);
|
||||
}
|
||||
|
||||
let timer = Timer::from_duration(delay);
|
||||
let wl_keyboard = wl_keyboard.clone();
|
||||
keyboard_state.repeat_token = keyboard_state
|
||||
.loop_handle
|
||||
.insert_source(timer, move |_, _, state| {
|
||||
let data = wl_keyboard.data::<KeyboardData>().unwrap();
|
||||
let seat_state = state.seats.get_mut(&data.seat.id()).unwrap();
|
||||
|
||||
// NOTE: The removed on event source is batched, but key change to
|
||||
// `None` is instant.
|
||||
let repeat_keycode =
|
||||
match seat_state.keyboard_state.as_ref().unwrap().current_repeat {
|
||||
Some(repeat_keycode) => repeat_keycode,
|
||||
None => return TimeoutAction::Drop,
|
||||
};
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
repeat_keycode,
|
||||
ElementState::Pressed,
|
||||
true,
|
||||
);
|
||||
|
||||
// NOTE: the gap could change dynamically while repeat is going.
|
||||
match seat_state.keyboard_state.as_ref().unwrap().repeat_info {
|
||||
RepeatInfo::Repeat { gap, .. } => TimeoutAction::ToDuration(gap),
|
||||
RepeatInfo::Disable => TimeoutAction::Drop,
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
WlKeyboardEvent::Key {
|
||||
key,
|
||||
state: key_state,
|
||||
..
|
||||
} if key_state == WEnum::Value(WlKeyState::Released) => {
|
||||
let key = key + 8;
|
||||
|
||||
key_input(
|
||||
seat_state,
|
||||
&mut state.events_sink,
|
||||
data,
|
||||
key,
|
||||
ElementState::Released,
|
||||
false,
|
||||
);
|
||||
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
if keyboard_state.repeat_info != RepeatInfo::Disable
|
||||
&& keyboard_state.xkb_state.key_repeats(key)
|
||||
&& Some(key) == keyboard_state.current_repeat
|
||||
{
|
||||
keyboard_state.current_repeat = None;
|
||||
}
|
||||
}
|
||||
WlKeyboardEvent::Modifiers {
|
||||
mods_depressed,
|
||||
mods_latched,
|
||||
mods_locked,
|
||||
group,
|
||||
..
|
||||
} => {
|
||||
let xkb_state = &mut seat_state.keyboard_state.as_mut().unwrap().xkb_state;
|
||||
xkb_state.update_modifiers(mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
||||
seat_state.modifiers = xkb_state.mods_state().into();
|
||||
|
||||
// HACK: part of the workaround from `WlKeyboardEvent::Enter`.
|
||||
let window_id = match *data.window_id.lock().unwrap() {
|
||||
Some(window_id) => window_id,
|
||||
None => {
|
||||
seat_state.modifiers_pending = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
state.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(seat_state.modifiers.into()),
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
WlKeyboardEvent::RepeatInfo { rate, delay } => {
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
keyboard_state.repeat_info = if rate == 0 {
|
||||
// Stop the repeat once we get a disable event.
|
||||
keyboard_state.current_repeat = None;
|
||||
if let Some(repeat_token) = keyboard_state.repeat_token.take() {
|
||||
keyboard_state.loop_handle.remove(repeat_token);
|
||||
}
|
||||
RepeatInfo::Disable
|
||||
} else {
|
||||
let gap = Duration::from_micros(1_000_000 / rate as u64);
|
||||
let delay = Duration::from_millis(delay as u64);
|
||||
RepeatInfo::Repeat { gap, delay }
|
||||
};
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardHandler for WinitState {
|
||||
fn enter(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
surface: &WlSurface,
|
||||
_serial: u32,
|
||||
_raw: &[u32],
|
||||
_keysyms: &[u32],
|
||||
) {
|
||||
let window_id = wayland::make_wid(surface);
|
||||
/// The state of the keyboard on the current seat.
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardState {
|
||||
/// The underlying WlKeyboard.
|
||||
pub keyboard: WlKeyboard,
|
||||
|
||||
// Mark the window as focused.
|
||||
match self.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().set_has_focus(true),
|
||||
None => return,
|
||||
};
|
||||
/// Loop handle to handle key repeat.
|
||||
pub loop_handle: LoopHandle<'static, WinitState>,
|
||||
|
||||
// Window gained focus.
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::Focused(true), window_id);
|
||||
/// The state of the keyboard.
|
||||
pub xkb_state: KbdState,
|
||||
|
||||
*keyboard.winit_data().window_id.lock().unwrap() = Some(window_id);
|
||||
/// The information about the repeat rate obtained from the compositor.
|
||||
pub repeat_info: RepeatInfo,
|
||||
|
||||
// NOTE: GNOME still hasn't violates the specification wrt ordering of such
|
||||
// events. See https://gitlab.gnome.org/GNOME/mutter/-/issues/2231.
|
||||
let seat_state = self.seats.get_mut(&keyboard.seat().id()).unwrap();
|
||||
if std::mem::take(&mut seat_state.modifiers_pending) {
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(seat_state.modifiers),
|
||||
window_id,
|
||||
);
|
||||
/// The token of the current handle inside the calloop's event loop.
|
||||
pub repeat_token: Option<RegistrationToken>,
|
||||
|
||||
/// The current repeat raw key.
|
||||
pub current_repeat: Option<u32>,
|
||||
}
|
||||
|
||||
impl KeyboardState {
|
||||
pub fn new(keyboard: WlKeyboard, loop_handle: LoopHandle<'static, WinitState>) -> Self {
|
||||
Self {
|
||||
keyboard,
|
||||
loop_handle,
|
||||
xkb_state: KbdState::new().unwrap(),
|
||||
repeat_info: RepeatInfo::default(),
|
||||
repeat_token: None,
|
||||
current_repeat: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn leave(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
surface: &WlSurface,
|
||||
_serial: u32,
|
||||
) {
|
||||
let window_id = wayland::make_wid(surface);
|
||||
|
||||
// XXX The check whether the window exists is essential as we might get a nil surface...
|
||||
match self.windows.get_mut().get(&window_id) {
|
||||
Some(window) => window.lock().unwrap().set_has_focus(false),
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Notify that no modifiers are being pressed.
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::ModifiersChanged(ModifiersState::empty()),
|
||||
window_id,
|
||||
);
|
||||
|
||||
*keyboard.winit_data().window_id.lock().unwrap() = None;
|
||||
|
||||
// Window lost focus.
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::Focused(false), window_id);
|
||||
}
|
||||
|
||||
fn press_key(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
_serial: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
self.handle_key_input(keyboard, event, ElementState::Pressed);
|
||||
}
|
||||
|
||||
fn release_key(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
_serial: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
self.handle_key_input(keyboard, event, ElementState::Released);
|
||||
}
|
||||
|
||||
// FIXME(kchibisov): recent spec suggested that this event could be sent
|
||||
// without any focus to indicate modifiers for the pointer, so update
|
||||
// for will be required once https://github.com/rust-windowing/winit/issues/2768
|
||||
// wrt winit design of the event is resolved.
|
||||
fn update_modifiers(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
keyboard: &WlKeyboard,
|
||||
_serial: u32,
|
||||
modifiers: Modifiers,
|
||||
) {
|
||||
let modifiers = ModifiersState::from(modifiers);
|
||||
let seat_state = self.seats.get_mut(&keyboard.seat().id()).unwrap();
|
||||
seat_state.modifiers = modifiers;
|
||||
|
||||
// NOTE: part of the workaround from `fn enter`, see it above.
|
||||
let window_id = match *keyboard.winit_data().window_id.lock().unwrap() {
|
||||
Some(window_id) => window_id,
|
||||
None => {
|
||||
seat_state.modifiers_pending = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.events_sink
|
||||
.push_window_event(WindowEvent::ModifiersChanged(modifiers), window_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// The extension to KeyboardData used to store the `window_id`.
|
||||
pub struct WinitKeyboardData {
|
||||
impl Drop for KeyboardState {
|
||||
fn drop(&mut self) {
|
||||
if self.keyboard.version() >= 3 {
|
||||
self.keyboard.release();
|
||||
}
|
||||
|
||||
if let Some(token) = self.repeat_token.take() {
|
||||
self.loop_handle.remove(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The rate at which a pressed key is repeated.
|
||||
#[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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Keyboard user data.
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardData {
|
||||
/// The currently focused window surface. Could be `None` on bugged compositors, like mutter.
|
||||
window_id: Mutex<Option<WindowId>>,
|
||||
|
||||
/// The original keyboard date.
|
||||
keyboard_data: KeyboardData<WinitState>,
|
||||
/// The seat used to create this keyboard.
|
||||
seat: WlSeat,
|
||||
}
|
||||
|
||||
impl WinitKeyboardData {
|
||||
impl KeyboardData {
|
||||
pub fn new(seat: WlSeat) -> Self {
|
||||
Self {
|
||||
window_id: Default::default(),
|
||||
keyboard_data: KeyboardData::new(seat),
|
||||
seat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardDataExt for WinitKeyboardData {
|
||||
type State = WinitState;
|
||||
fn key_input(
|
||||
seat_state: &mut WinitSeatState,
|
||||
event_sink: &mut EventSink,
|
||||
data: &KeyboardData,
|
||||
keycode: u32,
|
||||
state: ElementState,
|
||||
repeat: bool,
|
||||
) {
|
||||
let window_id = match *data.window_id.lock().unwrap() {
|
||||
Some(window_id) => window_id,
|
||||
None => return,
|
||||
};
|
||||
|
||||
fn keyboard_data(&self) -> &KeyboardData<Self::State> {
|
||||
&self.keyboard_data
|
||||
}
|
||||
let keyboard_state = seat_state.keyboard_state.as_mut().unwrap();
|
||||
|
||||
fn keyboard_data_mut(&mut self) -> &mut KeyboardData<Self::State> {
|
||||
&mut self.keyboard_data
|
||||
}
|
||||
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
||||
let event = keyboard_state
|
||||
.xkb_state
|
||||
.process_key_event(keycode, state, repeat);
|
||||
|
||||
event_sink.push_window_event(
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
}
|
||||
|
||||
pub trait WinitKeyboardDataExt {
|
||||
fn winit_data(&self) -> &WinitKeyboardData;
|
||||
|
||||
fn seat(&self) -> &WlSeat {
|
||||
self.winit_data().keyboard_data().seat()
|
||||
}
|
||||
}
|
||||
|
||||
impl WinitKeyboardDataExt for WlKeyboard {
|
||||
fn winit_data(&self) -> &WinitKeyboardData {
|
||||
self.data::<WinitKeyboardData>()
|
||||
.expect("failed to get keyboard data.")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Modifiers> for ModifiersState {
|
||||
fn from(mods: Modifiers) -> ModifiersState {
|
||||
let mut wl_mods = ModifiersState::empty();
|
||||
wl_mods.set(ModifiersState::SHIFT, mods.shift);
|
||||
wl_mods.set(ModifiersState::CTRL, mods.ctrl);
|
||||
wl_mods.set(ModifiersState::ALT, mods.alt);
|
||||
wl_mods.set(ModifiersState::LOGO, mods.logo);
|
||||
wl_mods
|
||||
}
|
||||
}
|
||||
|
||||
delegate_dispatch!(WinitState: [ WlKeyboard: WinitKeyboardData] => SeatState);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use std::sync::Arc;
|
|||
|
||||
use fnv::FnvHashMap;
|
||||
|
||||
use sctk::reexports::client::protocol::wl_keyboard::WlKeyboard;
|
||||
use sctk::reexports::client::protocol::wl_seat::WlSeat;
|
||||
use sctk::reexports::client::protocol::wl_touch::WlTouch;
|
||||
use sctk::reexports::client::{Connection, Proxy, QueueHandle};
|
||||
|
|
@ -14,7 +13,7 @@ use sctk::reexports::protocols::wp::text_input::zv3::client::zwp_text_input_v3::
|
|||
use sctk::seat::pointer::{ThemeSpec, ThemedPointer};
|
||||
use sctk::seat::{Capability as SeatCapability, SeatHandler, SeatState};
|
||||
|
||||
use crate::event::{ElementState, ModifiersState};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::platform_impl::wayland::state::WinitState;
|
||||
|
||||
mod keyboard;
|
||||
|
|
@ -26,7 +25,7 @@ pub use pointer::relative_pointer::RelativePointerState;
|
|||
pub use pointer::{PointerConstraintsState, WinitPointerData, WinitPointerDataExt};
|
||||
pub use text_input::{TextInputState, ZwpTextInputV3Ext};
|
||||
|
||||
use keyboard::WinitKeyboardData;
|
||||
use keyboard::{KeyboardData, KeyboardState};
|
||||
use text_input::TextInputData;
|
||||
use touch::TouchPoint;
|
||||
|
||||
|
|
@ -91,20 +90,9 @@ impl SeatHandler for WinitState {
|
|||
seat_state.touch = self.seat_state.get_touch(queue_handle, &seat).ok();
|
||||
}
|
||||
SeatCapability::Keyboard if seat_state.keyboard_state.is_none() => {
|
||||
let keyboard = self
|
||||
.seat_state
|
||||
.get_keyboard_with_repeat_with_data(
|
||||
queue_handle,
|
||||
&seat,
|
||||
WinitKeyboardData::new(seat.clone()),
|
||||
self.loop_handle.clone(),
|
||||
Box::new(|state, keyboard, event| {
|
||||
state.handle_key_input(keyboard, event, ElementState::Pressed);
|
||||
}),
|
||||
)
|
||||
.expect("failed to create keyboard with present capability.");
|
||||
|
||||
seat_state.keyboard_state = Some(KeyboardState { keyboard });
|
||||
let keyboard = seat.get_keyboard(queue_handle, KeyboardData::new(seat.clone()));
|
||||
seat_state.keyboard_state =
|
||||
Some(KeyboardState::new(keyboard, self.loop_handle.clone()));
|
||||
}
|
||||
SeatCapability::Pointer if seat_state.pointer.is_none() => {
|
||||
let surface = self.compositor_state.create_surface(queue_handle);
|
||||
|
|
@ -192,11 +180,7 @@ impl SeatHandler for WinitState {
|
|||
}
|
||||
}
|
||||
SeatCapability::Keyboard => {
|
||||
if let Some(keyboard_state) = seat_state.keyboard_state.take() {
|
||||
if keyboard_state.keyboard.version() >= 3 {
|
||||
keyboard_state.keyboard.release();
|
||||
}
|
||||
}
|
||||
seat_state.keyboard_state = None;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -225,10 +209,4 @@ impl SeatHandler for WinitState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KeyboardState {
|
||||
/// The underlying WlKeyboard.
|
||||
pub keyboard: WlKeyboard,
|
||||
}
|
||||
|
||||
sctk::delegate_seat!(WinitState);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ impl PointerHandler for WinitState {
|
|||
) {
|
||||
let seat = pointer.winit_data().seat();
|
||||
let seat_state = self.seats.get(&seat.id()).unwrap();
|
||||
let modifiers = seat_state.modifiers;
|
||||
|
||||
let device_id = crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(DeviceId));
|
||||
|
||||
|
|
@ -130,7 +129,6 @@ impl PointerHandler for WinitState {
|
|||
WindowEvent::CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
|
|
@ -151,7 +149,6 @@ impl PointerHandler for WinitState {
|
|||
WindowEvent::CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
|
|
@ -177,7 +174,6 @@ impl PointerHandler for WinitState {
|
|||
device_id,
|
||||
state,
|
||||
button,
|
||||
modifiers,
|
||||
},
|
||||
window_id,
|
||||
);
|
||||
|
|
@ -231,7 +227,6 @@ impl PointerHandler for WinitState {
|
|||
device_id,
|
||||
delta,
|
||||
phase,
|
||||
modifiers,
|
||||
},
|
||||
window_id,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,23 +1,19 @@
|
|||
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice, sync::Arc};
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
|
||||
use libc::{c_char, c_int, c_long, c_ulong};
|
||||
|
||||
use super::{
|
||||
events, ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd,
|
||||
DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
|
||||
XExtension,
|
||||
ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd, DndState,
|
||||
GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, XExtension,
|
||||
};
|
||||
|
||||
use util::modifiers::{ModifierKeyState, ModifierKeymap};
|
||||
|
||||
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event::{
|
||||
DeviceEvent, ElementState, Event, Ime, KeyboardInput, ModifiersState, TouchPhase,
|
||||
WindowEvent,
|
||||
},
|
||||
event::{DeviceEvent, ElementState, Event, Ime, RawKeyEvent, TouchPhase, WindowEvent},
|
||||
event_loop::EventLoopWindowTarget as RootELW,
|
||||
keyboard::ModifiersState,
|
||||
platform_impl::platform::common::{keymap, xkb_state::KbdState},
|
||||
};
|
||||
|
||||
/// The X11 documentation states: "Keycodes lie in the inclusive range `[8, 255]`".
|
||||
|
|
@ -30,9 +26,9 @@ pub(super) struct EventProcessor<T: 'static> {
|
|||
pub(super) randr_event_offset: c_int,
|
||||
pub(super) devices: RefCell<HashMap<DeviceId, Device>>,
|
||||
pub(super) xi2ext: XExtension,
|
||||
pub(super) xkbext: XExtension,
|
||||
pub(super) target: Rc<RootELW<T>>,
|
||||
pub(super) mod_keymap: ModifierKeymap,
|
||||
pub(super) device_mod_state: ModifierKeyState,
|
||||
pub(super) kb_state: KbdState,
|
||||
// Number of touch events currently in progress
|
||||
pub(super) num_touch: u32,
|
||||
pub(super) first_touch: Option<u64>,
|
||||
|
|
@ -134,47 +130,8 @@ impl<T: 'static> EventProcessor<T> {
|
|||
return;
|
||||
}
|
||||
|
||||
// We can't call a `&mut self` method because of the above borrow,
|
||||
// so we use this macro for repeated modifier state updates.
|
||||
macro_rules! update_modifiers {
|
||||
( $state:expr , $modifier:expr ) => {{
|
||||
match ($state, $modifier) {
|
||||
(state, modifier) => {
|
||||
if let Some(modifiers) =
|
||||
self.device_mod_state.update_state(&state, modifier)
|
||||
{
|
||||
if let Some(window_id) = self.active_window {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window_id),
|
||||
event: WindowEvent::ModifiersChanged(modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
let event_type = xev.get_type();
|
||||
match event_type {
|
||||
ffi::MappingNotify => {
|
||||
let mapping: &ffi::XMappingEvent = xev.as_ref();
|
||||
|
||||
if mapping.request == ffi::MappingModifier
|
||||
|| mapping.request == ffi::MappingKeyboard
|
||||
{
|
||||
unsafe {
|
||||
(wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut());
|
||||
}
|
||||
wt.xconn
|
||||
.check_errors()
|
||||
.expect("Failed to call XRefreshKeyboardMapping");
|
||||
|
||||
self.mod_keymap.reset_from_x_connection(&wt.xconn);
|
||||
self.device_mod_state.update_keymap(&self.mod_keymap);
|
||||
}
|
||||
}
|
||||
|
||||
ffi::ClientMessage => {
|
||||
let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
|
||||
|
||||
|
|
@ -577,90 +534,35 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
}
|
||||
|
||||
ffi::KeyPress | ffi::KeyRelease => {
|
||||
use crate::event::ElementState::{Pressed, Released};
|
||||
|
||||
// Note that in compose/pre-edit sequences, this will always be Released.
|
||||
let state = if xev.get_type() == ffi::KeyPress {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
|
||||
// Note that in compose/pre-edit sequences, we'll always receive KeyRelease events
|
||||
ffi::KeyPress => {
|
||||
let xkev: &mut ffi::XKeyEvent = xev.as_mut();
|
||||
|
||||
let window = xkev.window;
|
||||
let window_id = mkwid(window);
|
||||
|
||||
// Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable
|
||||
// value, though this should only be an issue under multiseat configurations.
|
||||
let device = util::VIRTUAL_CORE_KEYBOARD;
|
||||
let device_id = mkdid(device);
|
||||
let keycode = xkev.keycode;
|
||||
let written = if let Some(ic) = wt.ime.borrow().get_context(window) {
|
||||
wt.xconn.lookup_utf8(ic, xkev)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
|
||||
// a keycode of 0.
|
||||
if keycode != 0 && !self.is_composing {
|
||||
let scancode = keycode - KEYCODE_OFFSET as u32;
|
||||
let keysym = wt.xconn.lookup_keysym(xkev);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
update_modifiers!(
|
||||
ModifiersState::from_x11_mask(xkev.state),
|
||||
self.mod_keymap.get_modifier(xkev.keycode as ffi::KeyCode)
|
||||
);
|
||||
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
#[allow(deprecated)]
|
||||
callback(Event::WindowEvent {
|
||||
// If we're composing right now, send the string we've got from X11 via
|
||||
// Ime::Commit.
|
||||
if self.is_composing && xkev.keycode == 0 && !written.is_empty() {
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
};
|
||||
callback(event);
|
||||
|
||||
if state == Pressed {
|
||||
let written = if let Some(ic) = wt.ime.borrow().get_context(window) {
|
||||
wt.xconn.lookup_utf8(ic, xkev)
|
||||
} else {
|
||||
return;
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Ime(Ime::Commit(written)),
|
||||
};
|
||||
|
||||
// If we're composing right now, send the string we've got from X11 via
|
||||
// Ime::Commit.
|
||||
if self.is_composing && keycode == 0 && !written.is_empty() {
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Ime(Ime::Preedit(String::new(), None)),
|
||||
};
|
||||
callback(event);
|
||||
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Ime(Ime::Commit(written)),
|
||||
};
|
||||
|
||||
self.is_composing = false;
|
||||
callback(event);
|
||||
} else {
|
||||
for chr in written.chars() {
|
||||
let event = Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ReceivedCharacter(chr),
|
||||
};
|
||||
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
self.is_composing = false;
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -696,9 +598,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
return;
|
||||
}
|
||||
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
update_modifiers!(modifiers, None);
|
||||
|
||||
let state = if xev.evtype == ffi::XI_ButtonPress {
|
||||
Pressed
|
||||
} else {
|
||||
|
|
@ -711,7 +610,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
state,
|
||||
button: Left,
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
ffi::Button2 => callback(Event::WindowEvent {
|
||||
|
|
@ -720,7 +618,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
state,
|
||||
button: Middle,
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
ffi::Button3 => callback(Event::WindowEvent {
|
||||
|
|
@ -729,7 +626,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
state,
|
||||
button: Right,
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
|
||||
|
|
@ -750,7 +646,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
_ => unreachable!(),
|
||||
},
|
||||
phase: TouchPhase::Moved,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -762,7 +657,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
device_id,
|
||||
state,
|
||||
button: Other(x as u16),
|
||||
modifiers,
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
|
@ -773,9 +667,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
let window_id = mkwid(xev.event);
|
||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
update_modifiers!(modifiers, None);
|
||||
|
||||
let cursor_moved = self.with_window(xev.event, |window| {
|
||||
let mut shared_state_lock = window.shared_state_lock();
|
||||
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
||||
|
|
@ -788,7 +679,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
} else if cursor_moved.is_none() {
|
||||
|
|
@ -835,7 +725,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
},
|
||||
phase: TouchPhase::Moved,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
|
|
@ -889,25 +778,11 @@ impl<T: 'static> EventProcessor<T> {
|
|||
|
||||
let position = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||
|
||||
// The mods field on this event isn't actually populated, so query the
|
||||
// pointer device. In the future, we can likely remove this round-trip by
|
||||
// relying on `Xkb` for modifier values.
|
||||
//
|
||||
// This needs to only be done after confirming the window still exists,
|
||||
// since otherwise we risk getting a `BadWindow` error if the window was
|
||||
// dropped with queued events.
|
||||
let modifiers = wt
|
||||
.xconn
|
||||
.query_pointer(xev.event, xev.deviceid)
|
||||
.expect("Failed to query pointer device")
|
||||
.get_modifier_state();
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -935,10 +810,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
.focus(xev.event)
|
||||
.expect("Failed to focus input context");
|
||||
|
||||
let modifiers = ModifiersState::from_x11(&xev.mods);
|
||||
|
||||
self.device_mod_state.update_state(&modifiers, None);
|
||||
|
||||
if self.active_window != Some(xev.event) {
|
||||
self.active_window = Some(xev.event);
|
||||
|
||||
|
|
@ -956,10 +827,12 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: Focused(true),
|
||||
});
|
||||
|
||||
let modifiers: crate::keyboard::ModifiersState =
|
||||
self.kb_state.mods_state().into();
|
||||
if !modifiers.is_empty() {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(modifiers),
|
||||
event: WindowEvent::ModifiersChanged(modifiers.into()),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -977,7 +850,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: CursorMoved {
|
||||
device_id: mkdid(pointer_id),
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -986,8 +858,7 @@ impl<T: 'static> EventProcessor<T> {
|
|||
wt,
|
||||
window_id,
|
||||
ElementState::Pressed,
|
||||
&self.mod_keymap,
|
||||
&mut self.device_mod_state,
|
||||
&mut self.kb_state,
|
||||
&mut callback,
|
||||
);
|
||||
}
|
||||
|
|
@ -1013,14 +884,15 @@ impl<T: 'static> EventProcessor<T> {
|
|||
wt,
|
||||
window_id,
|
||||
ElementState::Released,
|
||||
&self.mod_keymap,
|
||||
&mut self.device_mod_state,
|
||||
&mut self.kb_state,
|
||||
&mut callback,
|
||||
);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::ModifiersChanged(ModifiersState::empty()),
|
||||
event: WindowEvent::ModifiersChanged(
|
||||
ModifiersState::empty().into(),
|
||||
),
|
||||
});
|
||||
|
||||
if let Some(window) = self.with_window(xev.event, Arc::clone) {
|
||||
|
|
@ -1045,7 +917,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
};
|
||||
if self.window_exists(xev.event) {
|
||||
let id = xev.detail as u64;
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
let location = PhysicalPosition::new(xev.event_x, xev.event_y);
|
||||
|
||||
// Mouse cursor position changes when touch events are received.
|
||||
|
|
@ -1057,7 +928,6 @@ impl<T: 'static> EventProcessor<T> {
|
|||
event: WindowEvent::CursorMoved {
|
||||
device_id: mkdid(util::VIRTUAL_CORE_POINTER),
|
||||
position: location.cast(),
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1143,6 +1013,39 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
}
|
||||
|
||||
// The regular KeyPress event has a problem where if you press a dead key, a KeyPress
|
||||
// event won't be emitted. XInput 2 does not have this problem.
|
||||
ffi::XI_KeyPress | ffi::XI_KeyRelease if !self.is_composing => {
|
||||
if let Some(active_window) = self.active_window {
|
||||
let state = if xev.evtype == ffi::XI_KeyPress {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
|
||||
let xkev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
// We use `self.active_window` here as `xkev.event` has a completely different
|
||||
// value for some reason.
|
||||
let window_id = mkwid(active_window);
|
||||
|
||||
let device_id = mkdid(xkev.deviceid);
|
||||
let keycode = xkev.detail as u32;
|
||||
|
||||
let repeat = xkev.flags & ffi::XIKeyRepeat == ffi::XIKeyRepeat;
|
||||
let event = self.kb_state.process_key_event(keycode, state, repeat);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
|
||||
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
|
|
@ -1153,46 +1056,19 @@ impl<T: 'static> EventProcessor<T> {
|
|||
};
|
||||
|
||||
let device_id = mkdid(xev.sourceid);
|
||||
let keycode = xev.detail;
|
||||
let scancode = keycode - KEYCODE_OFFSET as i32;
|
||||
if scancode < 0 {
|
||||
let keycode = xev.detail as u32;
|
||||
if keycode < KEYCODE_OFFSET as u32 {
|
||||
return;
|
||||
}
|
||||
let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
let modifiers = self.device_mod_state.modifiers();
|
||||
let physical_key = keymap::raw_keycode_to_keycode(keycode);
|
||||
|
||||
#[allow(deprecated)]
|
||||
callback(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: DeviceEvent::Key(KeyboardInput {
|
||||
scancode: scancode as u32,
|
||||
virtual_keycode,
|
||||
event: DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key,
|
||||
state,
|
||||
modifiers,
|
||||
}),
|
||||
});
|
||||
|
||||
if let Some(modifier) =
|
||||
self.mod_keymap.get_modifier(keycode as ffi::KeyCode)
|
||||
{
|
||||
self.device_mod_state.key_event(
|
||||
state,
|
||||
keycode as ffi::KeyCode,
|
||||
modifier,
|
||||
);
|
||||
|
||||
let new_modifiers = self.device_mod_state.modifiers();
|
||||
|
||||
if modifiers != new_modifiers {
|
||||
if let Some(window_id) = self.active_window {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window_id),
|
||||
event: WindowEvent::ModifiersChanged(new_modifiers),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_HierarchyChanged => {
|
||||
|
|
@ -1222,6 +1098,55 @@ impl<T: 'static> EventProcessor<T> {
|
|||
}
|
||||
}
|
||||
_ => {
|
||||
if event_type == self.xkbext.first_event_id {
|
||||
let xev = unsafe { &*(xev as *const _ as *const ffi::XkbAnyEvent) };
|
||||
match xev.xkb_type {
|
||||
ffi::XkbNewKeyboardNotify => {
|
||||
let xev = unsafe {
|
||||
&*(xev as *const _ as *const ffi::XkbNewKeyboardNotifyEvent)
|
||||
};
|
||||
let keycodes_changed_flag = 0x1;
|
||||
let geometry_changed_flag = 0x1 << 1;
|
||||
|
||||
let keycodes_changed =
|
||||
util::has_flag(xev.changed, keycodes_changed_flag);
|
||||
let geometry_changed =
|
||||
util::has_flag(xev.changed, geometry_changed_flag);
|
||||
|
||||
if xev.device == self.kb_state.core_keyboard_id
|
||||
&& (keycodes_changed || geometry_changed)
|
||||
{
|
||||
unsafe { self.kb_state.init_with_x11_keymap() };
|
||||
}
|
||||
}
|
||||
ffi::XkbStateNotify => {
|
||||
let xev =
|
||||
unsafe { &*(xev as *const _ as *const ffi::XkbStateNotifyEvent) };
|
||||
|
||||
let prev_mods = self.kb_state.mods_state();
|
||||
self.kb_state.update_modifiers(
|
||||
xev.base_mods,
|
||||
xev.latched_mods,
|
||||
xev.locked_mods,
|
||||
xev.base_group as u32,
|
||||
xev.latched_group as u32,
|
||||
xev.locked_group as u32,
|
||||
);
|
||||
let new_mods = self.kb_state.mods_state();
|
||||
if prev_mods != new_mods {
|
||||
if let Some(window) = self.active_window {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(window),
|
||||
event: WindowEvent::ModifiersChanged(
|
||||
Into::<ModifiersState>::into(new_mods).into(),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if event_type == self.randr_event_offset {
|
||||
// In the future, it would be quite easy to emit monitor hotplug events.
|
||||
let prev_list = monitor::invalidate_cached_monitor_list();
|
||||
|
|
@ -1345,14 +1270,12 @@ impl<T: 'static> EventProcessor<T> {
|
|||
wt: &super::EventLoopWindowTarget<T>,
|
||||
window_id: crate::window::WindowId,
|
||||
state: ElementState,
|
||||
mod_keymap: &ModifierKeymap,
|
||||
device_mod_state: &mut ModifierKeyState,
|
||||
kb_state: &mut KbdState,
|
||||
callback: &mut F,
|
||||
) where
|
||||
F: FnMut(Event<'_, T>),
|
||||
{
|
||||
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
|
||||
let modifiers = device_mod_state.modifiers();
|
||||
|
||||
// Update modifiers state and emit key events based on which keys are currently pressed.
|
||||
for keycode in wt
|
||||
|
|
@ -1361,29 +1284,13 @@ impl<T: 'static> EventProcessor<T> {
|
|||
.into_iter()
|
||||
.filter(|k| *k >= KEYCODE_OFFSET)
|
||||
{
|
||||
let scancode = (keycode - KEYCODE_OFFSET) as u32;
|
||||
let keysym = wt.xconn.keycode_to_keysym(keycode);
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
if let Some(modifier) = mod_keymap.get_modifier(keycode as ffi::KeyCode) {
|
||||
device_mod_state.key_event(
|
||||
ElementState::Pressed,
|
||||
keycode as ffi::KeyCode,
|
||||
modifier,
|
||||
);
|
||||
}
|
||||
|
||||
#[allow(deprecated)]
|
||||
let keycode = keycode as u32;
|
||||
let event = kb_state.process_key_event(keycode, state, false);
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
input: KeyboardInput {
|
||||
scancode,
|
||||
state,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
event,
|
||||
is_synthetic: true,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
mod dnd;
|
||||
mod event_processor;
|
||||
mod events;
|
||||
pub mod ffi;
|
||||
mod ime;
|
||||
mod monitor;
|
||||
|
|
@ -42,8 +41,8 @@ use self::{
|
|||
dnd::{Dnd, DndState},
|
||||
event_processor::EventProcessor,
|
||||
ime::{Ime, ImeCreationError, ImeReceiver, ImeRequest, ImeSender},
|
||||
util::modifiers::ModifierKeymap,
|
||||
};
|
||||
use super::common::xkb_state::KbdState;
|
||||
use crate::{
|
||||
error::OsError as RootOsError,
|
||||
event::{Event, StartCause},
|
||||
|
|
@ -202,6 +201,27 @@ impl<T: 'static> EventLoop<T> {
|
|||
ext
|
||||
};
|
||||
|
||||
let xkbext = {
|
||||
let mut ext = XExtension::default();
|
||||
|
||||
let res = unsafe {
|
||||
(xconn.xlib.XkbQueryExtension)(
|
||||
xconn.display,
|
||||
&mut ext.opcode,
|
||||
&mut ext.first_event_id,
|
||||
&mut ext.first_error_id,
|
||||
&mut 1,
|
||||
&mut 0,
|
||||
)
|
||||
};
|
||||
|
||||
if res == ffi::False {
|
||||
panic!("X server missing XKB extension");
|
||||
}
|
||||
|
||||
ext
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
|
|
@ -219,9 +239,6 @@ impl<T: 'static> EventLoop<T> {
|
|||
|
||||
xconn.update_cached_wm_info(root);
|
||||
|
||||
let mut mod_keymap = ModifierKeymap::new();
|
||||
mod_keymap.reset_from_x_connection(&xconn);
|
||||
|
||||
let poll = Poll::new().unwrap();
|
||||
let waker = Arc::new(Waker::new(poll.registry(), USER_REDRAW_TOKEN).unwrap());
|
||||
|
||||
|
|
@ -232,6 +249,10 @@ impl<T: 'static> EventLoop<T> {
|
|||
let (user_sender, user_channel) = std::sync::mpsc::channel();
|
||||
let (redraw_sender, redraw_channel) = std::sync::mpsc::channel();
|
||||
|
||||
let kb_state =
|
||||
KbdState::from_x11_xkb(unsafe { (xconn.xlib_xcb.XGetXCBConnection)(xconn.display) })
|
||||
.unwrap();
|
||||
|
||||
let window_target = EventLoopWindowTarget {
|
||||
ime,
|
||||
root,
|
||||
|
|
@ -264,8 +285,8 @@ impl<T: 'static> EventLoop<T> {
|
|||
ime_receiver,
|
||||
ime_event_receiver,
|
||||
xi2ext,
|
||||
mod_keymap,
|
||||
device_mod_state: Default::default(),
|
||||
xkbext,
|
||||
kb_state,
|
||||
num_touch: 0,
|
||||
first_touch: None,
|
||||
active_window: None,
|
||||
|
|
@ -279,6 +300,15 @@ impl<T: 'static> EventLoop<T> {
|
|||
.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
|
||||
.queue();
|
||||
|
||||
get_xtarget(&target)
|
||||
.xconn
|
||||
.select_xkb_events(
|
||||
0x100, // Use the "core keyboard device"
|
||||
ffi::XkbNewKeyboardNotifyMask | ffi::XkbStateNotifyMask,
|
||||
)
|
||||
.unwrap()
|
||||
.queue();
|
||||
|
||||
event_processor.init_device(ffi::XIAllDevices);
|
||||
|
||||
EventLoop {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use std::{slice, str};
|
||||
|
||||
use super::*;
|
||||
use crate::event::ModifiersState;
|
||||
|
||||
pub const VIRTUAL_CORE_POINTER: c_int = 2;
|
||||
pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
|
||||
|
|
@ -11,21 +10,6 @@ pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
|
|||
// To test if `lookup_utf8` works correctly, set this to 1.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
impl ModifiersState {
|
||||
pub(crate) fn from_x11(state: &ffi::XIModifierState) -> Self {
|
||||
ModifiersState::from_x11_mask(state.effective as c_uint)
|
||||
}
|
||||
|
||||
pub(crate) fn from_x11_mask(mask: c_uint) -> Self {
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::ALT, mask & ffi::Mod1Mask != 0);
|
||||
m.set(ModifiersState::SHIFT, mask & ffi::ShiftMask != 0);
|
||||
m.set(ModifiersState::CTRL, mask & ffi::ControlMask != 0);
|
||||
m.set(ModifiersState::LOGO, mask & ffi::Mod4Mask != 0);
|
||||
m
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Some of these fields are not used, but may be of use in the future.
|
||||
pub struct PointerState<'a> {
|
||||
xconn: &'a XConnection,
|
||||
|
|
@ -36,17 +20,10 @@ pub struct PointerState<'a> {
|
|||
pub win_x: c_double,
|
||||
pub win_y: c_double,
|
||||
buttons: ffi::XIButtonState,
|
||||
modifiers: ffi::XIModifierState,
|
||||
pub group: ffi::XIGroupState,
|
||||
pub relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl<'a> PointerState<'a> {
|
||||
pub fn get_modifier_state(&self) -> ModifiersState {
|
||||
ModifiersState::from_x11(&self.modifiers)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for PointerState<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !self.buttons.mask.is_null() {
|
||||
|
|
@ -81,12 +58,12 @@ impl XConnection {
|
|||
Flusher::new(self)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher<'_>> {
|
||||
let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) };
|
||||
if status == ffi::True {
|
||||
Some(Flusher::new(self))
|
||||
} else {
|
||||
error!("Could not select XKB events: The XKB extension is not initialized!");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -133,7 +110,6 @@ impl XConnection {
|
|||
win_x,
|
||||
win_y,
|
||||
buttons,
|
||||
modifiers,
|
||||
group,
|
||||
relative_to_window,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{iter::Enumerate, ptr, slice::Iter};
|
||||
use std::{iter::Enumerate, slice::Iter};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
@ -62,20 +62,6 @@ impl Iterator for KeymapIter<'_> {
|
|||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn keycode_to_keysym(&self, keycode: ffi::KeyCode) -> ffi::KeySym {
|
||||
unsafe { (self.xlib.XKeycodeToKeysym)(self.display, keycode, 0) }
|
||||
}
|
||||
|
||||
pub fn lookup_keysym(&self, xkev: &mut ffi::XKeyEvent) -> ffi::KeySym {
|
||||
let mut keysym = 0;
|
||||
|
||||
unsafe {
|
||||
(self.xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut());
|
||||
}
|
||||
|
||||
keysym
|
||||
}
|
||||
|
||||
pub fn query_keymap(&self) -> Keymap {
|
||||
let mut keys = [0; 32];
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ mod icon;
|
|||
mod input;
|
||||
pub mod keys;
|
||||
mod memory;
|
||||
pub mod modifiers;
|
||||
mod randr;
|
||||
mod window_property;
|
||||
mod wm;
|
||||
|
|
|
|||
|
|
@ -245,7 +245,6 @@ impl UnownedWindow {
|
|||
| ffi::StructureNotifyMask
|
||||
| ffi::VisibilityChangeMask
|
||||
| ffi::KeyPressMask
|
||||
| ffi::KeyReleaseMask
|
||||
| ffi::KeymapStateMask
|
||||
| ffi::ButtonPressMask
|
||||
| ffi::ButtonReleaseMask
|
||||
|
|
@ -448,17 +447,17 @@ impl UnownedWindow {
|
|||
|
||||
// Select XInput2 events
|
||||
let mask = ffi::XI_MotionMask
|
||||
| ffi::XI_ButtonPressMask
|
||||
| ffi::XI_ButtonReleaseMask
|
||||
//| ffi::XI_KeyPressMask
|
||||
//| ffi::XI_KeyReleaseMask
|
||||
| ffi::XI_EnterMask
|
||||
| ffi::XI_LeaveMask
|
||||
| ffi::XI_FocusInMask
|
||||
| ffi::XI_FocusOutMask
|
||||
| ffi::XI_TouchBeginMask
|
||||
| ffi::XI_TouchUpdateMask
|
||||
| ffi::XI_TouchEndMask;
|
||||
| ffi::XI_ButtonPressMask
|
||||
| ffi::XI_ButtonReleaseMask
|
||||
| ffi::XI_KeyPressMask
|
||||
| ffi::XI_KeyReleaseMask
|
||||
| ffi::XI_EnterMask
|
||||
| ffi::XI_LeaveMask
|
||||
| ffi::XI_FocusInMask
|
||||
| ffi::XI_FocusOutMask
|
||||
| ffi::XI_TouchBeginMask
|
||||
| ffi::XI_TouchUpdateMask
|
||||
| ffi::XI_TouchEndMask;
|
||||
xconn
|
||||
.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask)
|
||||
.queue();
|
||||
|
|
|
|||
|
|
@ -67,35 +67,6 @@ extern_methods!(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn keyEventWithType(
|
||||
type_: NSEventType,
|
||||
location: NSPoint,
|
||||
modifier_flags: NSEventModifierFlags,
|
||||
timestamp: NSTimeInterval,
|
||||
window_num: NSInteger,
|
||||
context: Option<&NSObject>,
|
||||
characters: &NSString,
|
||||
characters_ignoring_modifiers: &NSString,
|
||||
is_a_repeat: bool,
|
||||
scancode: c_ushort,
|
||||
) -> Id<Self, Shared> {
|
||||
unsafe {
|
||||
msg_send_id![
|
||||
Self::class(),
|
||||
keyEventWithType: type_,
|
||||
location: location,
|
||||
modifierFlags: modifier_flags,
|
||||
timestamp: timestamp,
|
||||
windowNumber: window_num,
|
||||
context: context,
|
||||
characters: characters,
|
||||
charactersIgnoringModifiers: characters_ignoring_modifiers,
|
||||
isARepeat: is_a_repeat,
|
||||
keyCode: scancode,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[sel(locationInWindow)]
|
||||
pub fn locationInWindow(&self) -> NSPoint;
|
||||
|
||||
|
|
@ -109,12 +80,8 @@ extern_methods!(
|
|||
#[sel(type)]
|
||||
pub fn type_(&self) -> NSEventType;
|
||||
|
||||
// In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character,
|
||||
// and there is no easy way to navtively retrieve the layout-dependent character.
|
||||
// In winit, we use keycode to refer to the key's character, and so this function aligns
|
||||
// AppKit's terminology with ours.
|
||||
#[sel(keyCode)]
|
||||
pub fn scancode(&self) -> c_ushort;
|
||||
pub fn key_code(&self) -> c_ushort;
|
||||
|
||||
#[sel(magnification)]
|
||||
pub fn magnification(&self) -> CGFloat;
|
||||
|
|
@ -169,6 +136,26 @@ extern_methods!(
|
|||
unsafe { msg_send_id![self, charactersIgnoringModifiers] }
|
||||
}
|
||||
|
||||
pub fn lshift_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELSHIFTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rshift_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERSHIFTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lctrl_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELCTLKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rctrl_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERCTLKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lalt_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELALTKEYMASK != 0
|
||||
|
|
@ -178,6 +165,16 @@ extern_methods!(
|
|||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERALTKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn lcmd_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICELCMDKEYMASK != 0
|
||||
}
|
||||
|
||||
pub fn rcmd_pressed(&self) -> bool {
|
||||
let raw_modifiers = self.modifierFlags().bits() as u32;
|
||||
raw_modifiers & NX_DEVICERCMDKEYMASK != 0
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -187,8 +184,14 @@ unsafe impl NSCopying for NSEvent {
|
|||
}
|
||||
|
||||
// The values are from the https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDSystem/IOKit/hidsystem/IOLLEvent.h#L258-L259
|
||||
const NX_DEVICELCTLKEYMASK: u32 = 0x00000001;
|
||||
const NX_DEVICELSHIFTKEYMASK: u32 = 0x00000002;
|
||||
const NX_DEVICERSHIFTKEYMASK: u32 = 0x00000004;
|
||||
const NX_DEVICELCMDKEYMASK: u32 = 0x00000008;
|
||||
const NX_DEVICERCMDKEYMASK: u32 = 0x00000010;
|
||||
const NX_DEVICELALTKEYMASK: u32 = 0x00000020;
|
||||
const NX_DEVICERALTKEYMASK: u32 = 0x00000040;
|
||||
const NX_DEVICERCTLKEYMASK: u32 = 0x00002000;
|
||||
|
||||
bitflags! {
|
||||
pub struct NSEventModifierFlags: NSUInteger {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,25 @@
|
|||
use std::os::raw::c_ushort;
|
||||
use std::ffi::c_void;
|
||||
|
||||
use core_foundation::{
|
||||
base::CFRelease,
|
||||
data::{CFDataGetBytePtr, CFDataRef},
|
||||
};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use super::appkit::{NSEvent, NSEventModifierFlags};
|
||||
use super::window::WinitWindow;
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent},
|
||||
platform_impl::platform::{util::Never, DEVICE_ID},
|
||||
event::{ElementState, Event, KeyEvent, Modifiers},
|
||||
keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
|
||||
},
|
||||
platform::{modifier_supplement::KeyEventExtModifierSupplement, scancode::KeyCodeExtScancode},
|
||||
platform_impl::platform::{
|
||||
ffi,
|
||||
util::{get_kbd_type, Never},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -25,268 +37,586 @@ pub(crate) enum EventProxy {
|
|||
},
|
||||
}
|
||||
|
||||
pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
|
||||
// We only translate keys that are affected by keyboard layout.
|
||||
//
|
||||
// Note that since keys are translated in a somewhat "dumb" way (reading character)
|
||||
// there is a concern that some combination, i.e. Cmd+char, causes the wrong
|
||||
// letter to be received, and so we receive the wrong key.
|
||||
//
|
||||
// Implementation reference: https://github.com/WebKit/webkit/blob/82bae82cf0f329dbe21059ef0986c4e92fea4ba6/Source/WebCore/platform/cocoa/KeyEventCocoa.mm#L626
|
||||
Some(match c {
|
||||
'a' | 'A' => VirtualKeyCode::A,
|
||||
'b' | 'B' => VirtualKeyCode::B,
|
||||
'c' | 'C' => VirtualKeyCode::C,
|
||||
'd' | 'D' => VirtualKeyCode::D,
|
||||
'e' | 'E' => VirtualKeyCode::E,
|
||||
'f' | 'F' => VirtualKeyCode::F,
|
||||
'g' | 'G' => VirtualKeyCode::G,
|
||||
'h' | 'H' => VirtualKeyCode::H,
|
||||
'i' | 'I' => VirtualKeyCode::I,
|
||||
'j' | 'J' => VirtualKeyCode::J,
|
||||
'k' | 'K' => VirtualKeyCode::K,
|
||||
'l' | 'L' => VirtualKeyCode::L,
|
||||
'm' | 'M' => VirtualKeyCode::M,
|
||||
'n' | 'N' => VirtualKeyCode::N,
|
||||
'o' | 'O' => VirtualKeyCode::O,
|
||||
'p' | 'P' => VirtualKeyCode::P,
|
||||
'q' | 'Q' => VirtualKeyCode::Q,
|
||||
'r' | 'R' => VirtualKeyCode::R,
|
||||
's' | 'S' => VirtualKeyCode::S,
|
||||
't' | 'T' => VirtualKeyCode::T,
|
||||
'u' | 'U' => VirtualKeyCode::U,
|
||||
'v' | 'V' => VirtualKeyCode::V,
|
||||
'w' | 'W' => VirtualKeyCode::W,
|
||||
'x' | 'X' => VirtualKeyCode::X,
|
||||
'y' | 'Y' => VirtualKeyCode::Y,
|
||||
'z' | 'Z' => VirtualKeyCode::Z,
|
||||
'1' | '!' => VirtualKeyCode::Key1,
|
||||
'2' | '@' => VirtualKeyCode::Key2,
|
||||
'3' | '#' => VirtualKeyCode::Key3,
|
||||
'4' | '$' => VirtualKeyCode::Key4,
|
||||
'5' | '%' => VirtualKeyCode::Key5,
|
||||
'6' | '^' => VirtualKeyCode::Key6,
|
||||
'7' | '&' => VirtualKeyCode::Key7,
|
||||
'8' | '*' => VirtualKeyCode::Key8,
|
||||
'9' | '(' => VirtualKeyCode::Key9,
|
||||
'0' | ')' => VirtualKeyCode::Key0,
|
||||
'=' | '+' => VirtualKeyCode::Equals,
|
||||
'-' | '_' => VirtualKeyCode::Minus,
|
||||
']' | '}' => VirtualKeyCode::RBracket,
|
||||
'[' | '{' => VirtualKeyCode::LBracket,
|
||||
'\'' | '"' => VirtualKeyCode::Apostrophe,
|
||||
';' | ':' => VirtualKeyCode::Semicolon,
|
||||
'\\' | '|' => VirtualKeyCode::Backslash,
|
||||
',' | '<' => VirtualKeyCode::Comma,
|
||||
'/' | '?' => VirtualKeyCode::Slash,
|
||||
'.' | '>' => VirtualKeyCode::Period,
|
||||
'`' | '~' => VirtualKeyCode::Grave,
|
||||
_ => return None,
|
||||
})
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub text_with_all_modifiers: Option<SmolStr>,
|
||||
pub key_without_modifiers: Key,
|
||||
}
|
||||
|
||||
pub fn scancode_to_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
|
||||
Some(match scancode {
|
||||
0x00 => VirtualKeyCode::A,
|
||||
0x01 => VirtualKeyCode::S,
|
||||
0x02 => VirtualKeyCode::D,
|
||||
0x03 => VirtualKeyCode::F,
|
||||
0x04 => VirtualKeyCode::H,
|
||||
0x05 => VirtualKeyCode::G,
|
||||
0x06 => VirtualKeyCode::Z,
|
||||
0x07 => VirtualKeyCode::X,
|
||||
0x08 => VirtualKeyCode::C,
|
||||
0x09 => VirtualKeyCode::V,
|
||||
//0x0a => World 1,
|
||||
0x0b => VirtualKeyCode::B,
|
||||
0x0c => VirtualKeyCode::Q,
|
||||
0x0d => VirtualKeyCode::W,
|
||||
0x0e => VirtualKeyCode::E,
|
||||
0x0f => VirtualKeyCode::R,
|
||||
0x10 => VirtualKeyCode::Y,
|
||||
0x11 => VirtualKeyCode::T,
|
||||
0x12 => VirtualKeyCode::Key1,
|
||||
0x13 => VirtualKeyCode::Key2,
|
||||
0x14 => VirtualKeyCode::Key3,
|
||||
0x15 => VirtualKeyCode::Key4,
|
||||
0x16 => VirtualKeyCode::Key6,
|
||||
0x17 => VirtualKeyCode::Key5,
|
||||
0x18 => VirtualKeyCode::Equals,
|
||||
0x19 => VirtualKeyCode::Key9,
|
||||
0x1a => VirtualKeyCode::Key7,
|
||||
0x1b => VirtualKeyCode::Minus,
|
||||
0x1c => VirtualKeyCode::Key8,
|
||||
0x1d => VirtualKeyCode::Key0,
|
||||
0x1e => VirtualKeyCode::RBracket,
|
||||
0x1f => VirtualKeyCode::O,
|
||||
0x20 => VirtualKeyCode::U,
|
||||
0x21 => VirtualKeyCode::LBracket,
|
||||
0x22 => VirtualKeyCode::I,
|
||||
0x23 => VirtualKeyCode::P,
|
||||
0x24 => VirtualKeyCode::Return,
|
||||
0x25 => VirtualKeyCode::L,
|
||||
0x26 => VirtualKeyCode::J,
|
||||
0x27 => VirtualKeyCode::Apostrophe,
|
||||
0x28 => VirtualKeyCode::K,
|
||||
0x29 => VirtualKeyCode::Semicolon,
|
||||
0x2a => VirtualKeyCode::Backslash,
|
||||
0x2b => VirtualKeyCode::Comma,
|
||||
0x2c => VirtualKeyCode::Slash,
|
||||
0x2d => VirtualKeyCode::N,
|
||||
0x2e => VirtualKeyCode::M,
|
||||
0x2f => VirtualKeyCode::Period,
|
||||
0x30 => VirtualKeyCode::Tab,
|
||||
0x31 => VirtualKeyCode::Space,
|
||||
0x32 => VirtualKeyCode::Grave,
|
||||
0x33 => VirtualKeyCode::Back,
|
||||
//0x34 => unkown,
|
||||
0x35 => VirtualKeyCode::Escape,
|
||||
0x36 => VirtualKeyCode::RWin,
|
||||
0x37 => VirtualKeyCode::LWin,
|
||||
0x38 => VirtualKeyCode::LShift,
|
||||
//0x39 => Caps lock,
|
||||
0x3a => VirtualKeyCode::LAlt,
|
||||
0x3b => VirtualKeyCode::LControl,
|
||||
0x3c => VirtualKeyCode::RShift,
|
||||
0x3d => VirtualKeyCode::RAlt,
|
||||
0x3e => VirtualKeyCode::RControl,
|
||||
//0x3f => Fn key,
|
||||
0x40 => VirtualKeyCode::F17,
|
||||
0x41 => VirtualKeyCode::NumpadDecimal,
|
||||
//0x42 -> unkown,
|
||||
0x43 => VirtualKeyCode::NumpadMultiply,
|
||||
//0x44 => unkown,
|
||||
0x45 => VirtualKeyCode::NumpadAdd,
|
||||
//0x46 => unkown,
|
||||
0x47 => VirtualKeyCode::Numlock,
|
||||
//0x48 => KeypadClear,
|
||||
0x49 => VirtualKeyCode::VolumeUp,
|
||||
0x4a => VirtualKeyCode::VolumeDown,
|
||||
0x4b => VirtualKeyCode::NumpadDivide,
|
||||
0x4c => VirtualKeyCode::NumpadEnter,
|
||||
//0x4d => unkown,
|
||||
0x4e => VirtualKeyCode::NumpadSubtract,
|
||||
0x4f => VirtualKeyCode::F18,
|
||||
0x50 => VirtualKeyCode::F19,
|
||||
0x51 => VirtualKeyCode::NumpadEquals,
|
||||
0x52 => VirtualKeyCode::Numpad0,
|
||||
0x53 => VirtualKeyCode::Numpad1,
|
||||
0x54 => VirtualKeyCode::Numpad2,
|
||||
0x55 => VirtualKeyCode::Numpad3,
|
||||
0x56 => VirtualKeyCode::Numpad4,
|
||||
0x57 => VirtualKeyCode::Numpad5,
|
||||
0x58 => VirtualKeyCode::Numpad6,
|
||||
0x59 => VirtualKeyCode::Numpad7,
|
||||
0x5a => VirtualKeyCode::F20,
|
||||
0x5b => VirtualKeyCode::Numpad8,
|
||||
0x5c => VirtualKeyCode::Numpad9,
|
||||
0x5d => VirtualKeyCode::Yen,
|
||||
//0x5e => JIS Ro,
|
||||
//0x5f => unkown,
|
||||
0x60 => VirtualKeyCode::F5,
|
||||
0x61 => VirtualKeyCode::F6,
|
||||
0x62 => VirtualKeyCode::F7,
|
||||
0x63 => VirtualKeyCode::F3,
|
||||
0x64 => VirtualKeyCode::F8,
|
||||
0x65 => VirtualKeyCode::F9,
|
||||
//0x66 => JIS Eisuu (macOS),
|
||||
0x67 => VirtualKeyCode::F11,
|
||||
//0x68 => JIS Kanna (macOS),
|
||||
0x69 => VirtualKeyCode::F13,
|
||||
0x6a => VirtualKeyCode::F16,
|
||||
0x6b => VirtualKeyCode::F14,
|
||||
//0x6c => unkown,
|
||||
0x6d => VirtualKeyCode::F10,
|
||||
//0x6e => unkown,
|
||||
0x6f => VirtualKeyCode::F12,
|
||||
//0x70 => unkown,
|
||||
0x71 => VirtualKeyCode::F15,
|
||||
0x72 => VirtualKeyCode::Insert,
|
||||
0x73 => VirtualKeyCode::Home,
|
||||
0x74 => VirtualKeyCode::PageUp,
|
||||
0x75 => VirtualKeyCode::Delete,
|
||||
0x76 => VirtualKeyCode::F4,
|
||||
0x77 => VirtualKeyCode::End,
|
||||
0x78 => VirtualKeyCode::F2,
|
||||
0x79 => VirtualKeyCode::PageDown,
|
||||
0x7a => VirtualKeyCode::F1,
|
||||
0x7b => VirtualKeyCode::Left,
|
||||
0x7c => VirtualKeyCode::Right,
|
||||
0x7d => VirtualKeyCode::Down,
|
||||
0x7e => VirtualKeyCode::Up,
|
||||
//0x7f => unkown,
|
||||
0xa => VirtualKeyCode::Caret,
|
||||
_ => return None,
|
||||
})
|
||||
impl KeyEventExtModifierSupplement for KeyEvent {
|
||||
fn text_with_all_modifiers(&self) -> Option<&str> {
|
||||
self.platform_specific
|
||||
.text_with_all_modifiers
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
}
|
||||
|
||||
fn key_without_modifiers(&self) -> Key {
|
||||
self.platform_specific.key_without_modifiers.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_modifierless_char(scancode: u16) -> Key {
|
||||
let mut string = [0; 16];
|
||||
let input_source;
|
||||
let layout;
|
||||
unsafe {
|
||||
input_source = ffi::TISCopyCurrentKeyboardLayoutInputSource();
|
||||
if input_source.is_null() {
|
||||
log::error!("`TISCopyCurrentKeyboardLayoutInputSource` returned null ptr");
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
let layout_data =
|
||||
ffi::TISGetInputSourceProperty(input_source, ffi::kTISPropertyUnicodeKeyLayoutData);
|
||||
if layout_data.is_null() {
|
||||
CFRelease(input_source as *mut c_void);
|
||||
log::error!("`TISGetInputSourceProperty` returned null ptr");
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
layout = CFDataGetBytePtr(layout_data as CFDataRef) as *const ffi::UCKeyboardLayout;
|
||||
}
|
||||
let keyboard_type = get_kbd_type();
|
||||
|
||||
let mut result_len = 0;
|
||||
let mut dead_keys = 0;
|
||||
let modifiers = 0;
|
||||
let translate_result = unsafe {
|
||||
ffi::UCKeyTranslate(
|
||||
layout,
|
||||
scancode,
|
||||
ffi::kUCKeyActionDisplay,
|
||||
modifiers,
|
||||
keyboard_type as u32,
|
||||
ffi::kUCKeyTranslateNoDeadKeysMask,
|
||||
&mut dead_keys,
|
||||
string.len() as ffi::UniCharCount,
|
||||
&mut result_len,
|
||||
string.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
CFRelease(input_source as *mut c_void);
|
||||
}
|
||||
if translate_result != 0 {
|
||||
log::error!(
|
||||
"`UCKeyTranslate` returned with the non-zero value: {}",
|
||||
translate_result
|
||||
);
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
if result_len == 0 {
|
||||
log::error!("`UCKeyTranslate` was succesful but gave a string of 0 length.");
|
||||
return Key::Unidentified(NativeKey::MacOS(scancode));
|
||||
}
|
||||
let chars = String::from_utf16_lossy(&string[0..result_len as usize]);
|
||||
Key::Character(SmolStr::new(chars))
|
||||
}
|
||||
|
||||
fn get_logical_key_char(ns_event: &NSEvent, modifierless_chars: &str) -> Key {
|
||||
let string = ns_event
|
||||
.charactersIgnoringModifiers()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(String::new);
|
||||
if string.is_empty() {
|
||||
// Probably a dead key
|
||||
let first_char = modifierless_chars.chars().next();
|
||||
return Key::Dead(first_char);
|
||||
}
|
||||
Key::Character(SmolStr::new(string))
|
||||
}
|
||||
|
||||
/// Create `KeyEvent` for the given `NSEvent`.
|
||||
///
|
||||
/// This function shouldn't be called when the IME input is in process.
|
||||
pub(crate) fn create_key_event(
|
||||
ns_event: &NSEvent,
|
||||
is_press: bool,
|
||||
is_repeat: bool,
|
||||
key_override: Option<KeyCode>,
|
||||
) -> KeyEvent {
|
||||
use ElementState::{Pressed, Released};
|
||||
let state = if is_press { Pressed } else { Released };
|
||||
|
||||
let scancode = ns_event.key_code();
|
||||
let mut physical_key = key_override.unwrap_or_else(|| KeyCode::from_scancode(scancode as u32));
|
||||
|
||||
let text_with_all_modifiers: Option<SmolStr> = if key_override.is_some() {
|
||||
None
|
||||
} else {
|
||||
let characters = ns_event
|
||||
.characters()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(String::new);
|
||||
if characters.is_empty() {
|
||||
None
|
||||
} else {
|
||||
if matches!(physical_key, KeyCode::Unidentified(_)) {
|
||||
// The key may be one of the funky function keys
|
||||
physical_key = extra_function_key_to_code(scancode, &characters);
|
||||
}
|
||||
Some(SmolStr::new(characters))
|
||||
}
|
||||
};
|
||||
|
||||
let key_from_code = code_to_key(physical_key, scancode);
|
||||
let (logical_key, key_without_modifiers) = if matches!(key_from_code, Key::Unidentified(_)) {
|
||||
let key_without_modifiers = get_modifierless_char(scancode);
|
||||
|
||||
let modifiers = NSEvent::modifierFlags(ns_event);
|
||||
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSControlKeyMask);
|
||||
|
||||
let logical_key = match text_with_all_modifiers.as_ref() {
|
||||
// Only checking for ctrl here, not checking for alt because we DO want to
|
||||
// include its effect in the key. For example if -on the Germay layout- one
|
||||
// presses alt+8, the logical key should be "{"
|
||||
// Also not checking if this is a release event because then this issue would
|
||||
// still affect the key release.
|
||||
Some(text) if !has_ctrl => Key::Character(text.clone()),
|
||||
_ => {
|
||||
let modifierless_chars = match key_without_modifiers.as_ref() {
|
||||
Key::Character(ch) => ch,
|
||||
_ => "",
|
||||
};
|
||||
get_logical_key_char(ns_event, modifierless_chars)
|
||||
}
|
||||
};
|
||||
|
||||
(logical_key, key_without_modifiers)
|
||||
} else {
|
||||
(key_from_code.clone(), key_from_code)
|
||||
};
|
||||
|
||||
let text = if is_press {
|
||||
logical_key.to_text().map(SmolStr::new)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let location = code_to_location(physical_key);
|
||||
|
||||
KeyEvent {
|
||||
location,
|
||||
logical_key,
|
||||
physical_key,
|
||||
repeat: is_repeat,
|
||||
state,
|
||||
text,
|
||||
platform_specific: KeyEventExtra {
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code_to_key(code: KeyCode, scancode: u16) -> Key {
|
||||
match code {
|
||||
KeyCode::Enter => Key::Enter,
|
||||
KeyCode::Tab => Key::Tab,
|
||||
KeyCode::Space => Key::Space,
|
||||
KeyCode::Backspace => Key::Backspace,
|
||||
KeyCode::Escape => Key::Escape,
|
||||
KeyCode::SuperRight => Key::Super,
|
||||
KeyCode::SuperLeft => Key::Super,
|
||||
KeyCode::ShiftLeft => Key::Shift,
|
||||
KeyCode::AltLeft => Key::Alt,
|
||||
KeyCode::ControlLeft => Key::Control,
|
||||
KeyCode::ShiftRight => Key::Shift,
|
||||
KeyCode::AltRight => Key::Alt,
|
||||
KeyCode::ControlRight => Key::Control,
|
||||
|
||||
KeyCode::NumLock => Key::NumLock,
|
||||
KeyCode::AudioVolumeUp => Key::AudioVolumeUp,
|
||||
KeyCode::AudioVolumeDown => Key::AudioVolumeDown,
|
||||
|
||||
// Other numpad keys all generate text on macOS (if I understand correctly)
|
||||
KeyCode::NumpadEnter => Key::Enter,
|
||||
|
||||
KeyCode::F1 => Key::F1,
|
||||
KeyCode::F2 => Key::F2,
|
||||
KeyCode::F3 => Key::F3,
|
||||
KeyCode::F4 => Key::F4,
|
||||
KeyCode::F5 => Key::F5,
|
||||
KeyCode::F6 => Key::F6,
|
||||
KeyCode::F7 => Key::F7,
|
||||
KeyCode::F8 => Key::F8,
|
||||
KeyCode::F9 => Key::F9,
|
||||
KeyCode::F10 => Key::F10,
|
||||
KeyCode::F11 => Key::F11,
|
||||
KeyCode::F12 => Key::F12,
|
||||
KeyCode::F13 => Key::F13,
|
||||
KeyCode::F14 => Key::F14,
|
||||
KeyCode::F15 => Key::F15,
|
||||
KeyCode::F16 => Key::F16,
|
||||
KeyCode::F17 => Key::F17,
|
||||
KeyCode::F18 => Key::F18,
|
||||
KeyCode::F19 => Key::F19,
|
||||
KeyCode::F20 => Key::F20,
|
||||
|
||||
KeyCode::Insert => Key::Insert,
|
||||
KeyCode::Home => Key::Home,
|
||||
KeyCode::PageUp => Key::PageUp,
|
||||
KeyCode::Delete => Key::Delete,
|
||||
KeyCode::End => Key::End,
|
||||
KeyCode::PageDown => Key::PageDown,
|
||||
KeyCode::ArrowLeft => Key::ArrowLeft,
|
||||
KeyCode::ArrowRight => Key::ArrowRight,
|
||||
KeyCode::ArrowDown => Key::ArrowDown,
|
||||
KeyCode::ArrowUp => Key::ArrowUp,
|
||||
_ => Key::Unidentified(NativeKey::MacOS(scancode)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code_to_location(code: KeyCode) -> KeyLocation {
|
||||
match code {
|
||||
KeyCode::SuperRight => KeyLocation::Right,
|
||||
KeyCode::SuperLeft => KeyLocation::Left,
|
||||
KeyCode::ShiftLeft => KeyLocation::Left,
|
||||
KeyCode::AltLeft => KeyLocation::Left,
|
||||
KeyCode::ControlLeft => KeyLocation::Left,
|
||||
KeyCode::ShiftRight => KeyLocation::Right,
|
||||
KeyCode::AltRight => KeyLocation::Right,
|
||||
KeyCode::ControlRight => KeyLocation::Right,
|
||||
|
||||
KeyCode::NumLock => KeyLocation::Numpad,
|
||||
KeyCode::NumpadDecimal => KeyLocation::Numpad,
|
||||
KeyCode::NumpadMultiply => KeyLocation::Numpad,
|
||||
KeyCode::NumpadAdd => KeyLocation::Numpad,
|
||||
KeyCode::NumpadDivide => KeyLocation::Numpad,
|
||||
KeyCode::NumpadEnter => KeyLocation::Numpad,
|
||||
KeyCode::NumpadSubtract => KeyLocation::Numpad,
|
||||
KeyCode::NumpadEqual => KeyLocation::Numpad,
|
||||
KeyCode::Numpad0 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad1 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad2 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad3 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad4 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad5 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad6 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad7 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad8 => KeyLocation::Numpad,
|
||||
KeyCode::Numpad9 => KeyLocation::Numpad,
|
||||
|
||||
_ => KeyLocation::Standard,
|
||||
}
|
||||
}
|
||||
|
||||
// While F1-F20 have scancodes we can match on, we have to check against UTF-16
|
||||
// constants for the rest.
|
||||
// https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ
|
||||
pub fn check_function_keys(string: &str) -> Option<VirtualKeyCode> {
|
||||
pub fn extra_function_key_to_code(scancode: u16, string: &str) -> KeyCode {
|
||||
if let Some(ch) = string.encode_utf16().next() {
|
||||
return Some(match ch {
|
||||
0xf718 => VirtualKeyCode::F21,
|
||||
0xf719 => VirtualKeyCode::F22,
|
||||
0xf71a => VirtualKeyCode::F23,
|
||||
0xf71b => VirtualKeyCode::F24,
|
||||
_ => return None,
|
||||
});
|
||||
match ch {
|
||||
0xf718 => KeyCode::F21,
|
||||
0xf719 => KeyCode::F22,
|
||||
0xf71a => KeyCode::F23,
|
||||
0xf71b => KeyCode::F24,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::MacOS(scancode)),
|
||||
}
|
||||
} else {
|
||||
KeyCode::Unidentified(NativeKeyCode::MacOS(scancode))
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub(super) fn event_mods(event: &NSEvent) -> ModifiersState {
|
||||
pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
|
||||
let flags = event.modifierFlags();
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(
|
||||
let mut state = ModifiersState::empty();
|
||||
let mut pressed_mods = ModifiersKeys::empty();
|
||||
|
||||
state.set(
|
||||
ModifiersState::SHIFT,
|
||||
flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||
);
|
||||
m.set(
|
||||
ModifiersState::CTRL,
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LSHIFT, event.lshift_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RSHIFT, event.rshift_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::CONTROL,
|
||||
flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||
);
|
||||
m.set(
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LCONTROL, event.lctrl_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RCONTROL, event.rctrl_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::ALT,
|
||||
flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||
);
|
||||
m.set(
|
||||
ModifiersState::LOGO,
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LALT, event.lalt_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RALT, event.ralt_pressed());
|
||||
|
||||
state.set(
|
||||
ModifiersState::SUPER,
|
||||
flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||
);
|
||||
m
|
||||
}
|
||||
|
||||
pub(super) fn modifier_event(
|
||||
event: &NSEvent,
|
||||
keymask: NSEventModifierFlags,
|
||||
was_key_pressed: bool,
|
||||
) -> Option<WindowEvent<'static>> {
|
||||
if !was_key_pressed && event.modifierFlags().contains(keymask)
|
||||
|| was_key_pressed && !event.modifierFlags().contains(keymask)
|
||||
{
|
||||
let state = if was_key_pressed {
|
||||
ElementState::Released
|
||||
} else {
|
||||
ElementState::Pressed
|
||||
};
|
||||
pressed_mods.set(ModifiersKeys::LSUPER, event.lcmd_pressed());
|
||||
pressed_mods.set(ModifiersKeys::RSUPER, event.rcmd_pressed());
|
||||
|
||||
let scancode = event.scancode();
|
||||
let virtual_keycode = scancode_to_keycode(scancode);
|
||||
#[allow(deprecated)]
|
||||
Some(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode: scancode as _,
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
is_synthetic: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
Modifiers {
|
||||
state,
|
||||
pressed_mods,
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyCodeExtScancode for KeyCode {
|
||||
fn to_scancode(self) -> Option<u32> {
|
||||
match self {
|
||||
KeyCode::KeyA => Some(0x00),
|
||||
KeyCode::KeyS => Some(0x01),
|
||||
KeyCode::KeyD => Some(0x02),
|
||||
KeyCode::KeyF => Some(0x03),
|
||||
KeyCode::KeyH => Some(0x04),
|
||||
KeyCode::KeyG => Some(0x05),
|
||||
KeyCode::KeyZ => Some(0x06),
|
||||
KeyCode::KeyX => Some(0x07),
|
||||
KeyCode::KeyC => Some(0x08),
|
||||
KeyCode::KeyV => Some(0x09),
|
||||
KeyCode::KeyB => Some(0x0b),
|
||||
KeyCode::KeyQ => Some(0x0c),
|
||||
KeyCode::KeyW => Some(0x0d),
|
||||
KeyCode::KeyE => Some(0x0e),
|
||||
KeyCode::KeyR => Some(0x0f),
|
||||
KeyCode::KeyY => Some(0x10),
|
||||
KeyCode::KeyT => Some(0x11),
|
||||
KeyCode::Digit1 => Some(0x12),
|
||||
KeyCode::Digit2 => Some(0x13),
|
||||
KeyCode::Digit3 => Some(0x14),
|
||||
KeyCode::Digit4 => Some(0x15),
|
||||
KeyCode::Digit6 => Some(0x16),
|
||||
KeyCode::Digit5 => Some(0x17),
|
||||
KeyCode::Equal => Some(0x18),
|
||||
KeyCode::Digit9 => Some(0x19),
|
||||
KeyCode::Digit7 => Some(0x1a),
|
||||
KeyCode::Minus => Some(0x1b),
|
||||
KeyCode::Digit8 => Some(0x1c),
|
||||
KeyCode::Digit0 => Some(0x1d),
|
||||
KeyCode::BracketRight => Some(0x1e),
|
||||
KeyCode::KeyO => Some(0x1f),
|
||||
KeyCode::KeyU => Some(0x20),
|
||||
KeyCode::BracketLeft => Some(0x21),
|
||||
KeyCode::KeyI => Some(0x22),
|
||||
KeyCode::KeyP => Some(0x23),
|
||||
KeyCode::Enter => Some(0x24),
|
||||
KeyCode::KeyL => Some(0x25),
|
||||
KeyCode::KeyJ => Some(0x26),
|
||||
KeyCode::Quote => Some(0x27),
|
||||
KeyCode::KeyK => Some(0x28),
|
||||
KeyCode::Semicolon => Some(0x29),
|
||||
KeyCode::Backslash => Some(0x2a),
|
||||
KeyCode::Comma => Some(0x2b),
|
||||
KeyCode::Slash => Some(0x2c),
|
||||
KeyCode::KeyN => Some(0x2d),
|
||||
KeyCode::KeyM => Some(0x2e),
|
||||
KeyCode::Period => Some(0x2f),
|
||||
KeyCode::Tab => Some(0x30),
|
||||
KeyCode::Space => Some(0x31),
|
||||
KeyCode::Backquote => Some(0x32),
|
||||
KeyCode::Backspace => Some(0x33),
|
||||
KeyCode::Escape => Some(0x35),
|
||||
KeyCode::SuperRight => Some(0x36),
|
||||
KeyCode::SuperLeft => Some(0x37),
|
||||
KeyCode::ShiftLeft => Some(0x38),
|
||||
KeyCode::AltLeft => Some(0x3a),
|
||||
KeyCode::ControlLeft => Some(0x3b),
|
||||
KeyCode::ShiftRight => Some(0x3c),
|
||||
KeyCode::AltRight => Some(0x3d),
|
||||
KeyCode::ControlRight => Some(0x3e),
|
||||
KeyCode::F17 => Some(0x40),
|
||||
KeyCode::NumpadDecimal => Some(0x41),
|
||||
KeyCode::NumpadMultiply => Some(0x43),
|
||||
KeyCode::NumpadAdd => Some(0x45),
|
||||
KeyCode::NumLock => Some(0x47),
|
||||
KeyCode::AudioVolumeUp => Some(0x49),
|
||||
KeyCode::AudioVolumeDown => Some(0x4a),
|
||||
KeyCode::NumpadDivide => Some(0x4b),
|
||||
KeyCode::NumpadEnter => Some(0x4c),
|
||||
KeyCode::NumpadSubtract => Some(0x4e),
|
||||
KeyCode::F18 => Some(0x4f),
|
||||
KeyCode::F19 => Some(0x50),
|
||||
KeyCode::NumpadEqual => Some(0x51),
|
||||
KeyCode::Numpad0 => Some(0x52),
|
||||
KeyCode::Numpad1 => Some(0x53),
|
||||
KeyCode::Numpad2 => Some(0x54),
|
||||
KeyCode::Numpad3 => Some(0x55),
|
||||
KeyCode::Numpad4 => Some(0x56),
|
||||
KeyCode::Numpad5 => Some(0x57),
|
||||
KeyCode::Numpad6 => Some(0x58),
|
||||
KeyCode::Numpad7 => Some(0x59),
|
||||
KeyCode::F20 => Some(0x5a),
|
||||
KeyCode::Numpad8 => Some(0x5b),
|
||||
KeyCode::Numpad9 => Some(0x5c),
|
||||
KeyCode::IntlYen => Some(0x5d),
|
||||
KeyCode::F5 => Some(0x60),
|
||||
KeyCode::F6 => Some(0x61),
|
||||
KeyCode::F7 => Some(0x62),
|
||||
KeyCode::F3 => Some(0x63),
|
||||
KeyCode::F8 => Some(0x64),
|
||||
KeyCode::F9 => Some(0x65),
|
||||
KeyCode::F11 => Some(0x67),
|
||||
KeyCode::F13 => Some(0x69),
|
||||
KeyCode::F16 => Some(0x6a),
|
||||
KeyCode::F14 => Some(0x6b),
|
||||
KeyCode::F10 => Some(0x6d),
|
||||
KeyCode::F12 => Some(0x6f),
|
||||
KeyCode::F15 => Some(0x71),
|
||||
KeyCode::Insert => Some(0x72),
|
||||
KeyCode::Home => Some(0x73),
|
||||
KeyCode::PageUp => Some(0x74),
|
||||
KeyCode::Delete => Some(0x75),
|
||||
KeyCode::F4 => Some(0x76),
|
||||
KeyCode::End => Some(0x77),
|
||||
KeyCode::F2 => Some(0x78),
|
||||
KeyCode::PageDown => Some(0x79),
|
||||
KeyCode::F1 => Some(0x7a),
|
||||
KeyCode::ArrowLeft => Some(0x7b),
|
||||
KeyCode::ArrowRight => Some(0x7c),
|
||||
KeyCode::ArrowDown => Some(0x7d),
|
||||
KeyCode::ArrowUp => Some(0x7e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_scancode(scancode: u32) -> KeyCode {
|
||||
match scancode {
|
||||
0x00 => KeyCode::KeyA,
|
||||
0x01 => KeyCode::KeyS,
|
||||
0x02 => KeyCode::KeyD,
|
||||
0x03 => KeyCode::KeyF,
|
||||
0x04 => KeyCode::KeyH,
|
||||
0x05 => KeyCode::KeyG,
|
||||
0x06 => KeyCode::KeyZ,
|
||||
0x07 => KeyCode::KeyX,
|
||||
0x08 => KeyCode::KeyC,
|
||||
0x09 => KeyCode::KeyV,
|
||||
//0x0a => World 1,
|
||||
0x0b => KeyCode::KeyB,
|
||||
0x0c => KeyCode::KeyQ,
|
||||
0x0d => KeyCode::KeyW,
|
||||
0x0e => KeyCode::KeyE,
|
||||
0x0f => KeyCode::KeyR,
|
||||
0x10 => KeyCode::KeyY,
|
||||
0x11 => KeyCode::KeyT,
|
||||
0x12 => KeyCode::Digit1,
|
||||
0x13 => KeyCode::Digit2,
|
||||
0x14 => KeyCode::Digit3,
|
||||
0x15 => KeyCode::Digit4,
|
||||
0x16 => KeyCode::Digit6,
|
||||
0x17 => KeyCode::Digit5,
|
||||
0x18 => KeyCode::Equal,
|
||||
0x19 => KeyCode::Digit9,
|
||||
0x1a => KeyCode::Digit7,
|
||||
0x1b => KeyCode::Minus,
|
||||
0x1c => KeyCode::Digit8,
|
||||
0x1d => KeyCode::Digit0,
|
||||
0x1e => KeyCode::BracketRight,
|
||||
0x1f => KeyCode::KeyO,
|
||||
0x20 => KeyCode::KeyU,
|
||||
0x21 => KeyCode::BracketLeft,
|
||||
0x22 => KeyCode::KeyI,
|
||||
0x23 => KeyCode::KeyP,
|
||||
0x24 => KeyCode::Enter,
|
||||
0x25 => KeyCode::KeyL,
|
||||
0x26 => KeyCode::KeyJ,
|
||||
0x27 => KeyCode::Quote,
|
||||
0x28 => KeyCode::KeyK,
|
||||
0x29 => KeyCode::Semicolon,
|
||||
0x2a => KeyCode::Backslash,
|
||||
0x2b => KeyCode::Comma,
|
||||
0x2c => KeyCode::Slash,
|
||||
0x2d => KeyCode::KeyN,
|
||||
0x2e => KeyCode::KeyM,
|
||||
0x2f => KeyCode::Period,
|
||||
0x30 => KeyCode::Tab,
|
||||
0x31 => KeyCode::Space,
|
||||
0x32 => KeyCode::Backquote,
|
||||
0x33 => KeyCode::Backspace,
|
||||
//0x34 => unknown,
|
||||
0x35 => KeyCode::Escape,
|
||||
0x36 => KeyCode::SuperRight,
|
||||
0x37 => KeyCode::SuperLeft,
|
||||
0x38 => KeyCode::ShiftLeft,
|
||||
0x39 => KeyCode::CapsLock,
|
||||
0x3a => KeyCode::AltLeft,
|
||||
0x3b => KeyCode::ControlLeft,
|
||||
0x3c => KeyCode::ShiftRight,
|
||||
0x3d => KeyCode::AltRight,
|
||||
0x3e => KeyCode::ControlRight,
|
||||
0x3f => KeyCode::Fn,
|
||||
0x40 => KeyCode::F17,
|
||||
0x41 => KeyCode::NumpadDecimal,
|
||||
//0x42 -> unknown,
|
||||
0x43 => KeyCode::NumpadMultiply,
|
||||
//0x44 => unknown,
|
||||
0x45 => KeyCode::NumpadAdd,
|
||||
//0x46 => unknown,
|
||||
0x47 => KeyCode::NumLock,
|
||||
//0x48 => KeyCode::NumpadClear,
|
||||
|
||||
// TODO: (Artur) for me, kVK_VolumeUp is 0x48
|
||||
// macOS 10.11
|
||||
// /System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Headers/Events.h
|
||||
0x49 => KeyCode::AudioVolumeUp,
|
||||
0x4a => KeyCode::AudioVolumeDown,
|
||||
0x4b => KeyCode::NumpadDivide,
|
||||
0x4c => KeyCode::NumpadEnter,
|
||||
//0x4d => unknown,
|
||||
0x4e => KeyCode::NumpadSubtract,
|
||||
0x4f => KeyCode::F18,
|
||||
0x50 => KeyCode::F19,
|
||||
0x51 => KeyCode::NumpadEqual,
|
||||
0x52 => KeyCode::Numpad0,
|
||||
0x53 => KeyCode::Numpad1,
|
||||
0x54 => KeyCode::Numpad2,
|
||||
0x55 => KeyCode::Numpad3,
|
||||
0x56 => KeyCode::Numpad4,
|
||||
0x57 => KeyCode::Numpad5,
|
||||
0x58 => KeyCode::Numpad6,
|
||||
0x59 => KeyCode::Numpad7,
|
||||
0x5a => KeyCode::F20,
|
||||
0x5b => KeyCode::Numpad8,
|
||||
0x5c => KeyCode::Numpad9,
|
||||
0x5d => KeyCode::IntlYen,
|
||||
//0x5e => JIS Ro,
|
||||
//0x5f => unknown,
|
||||
0x60 => KeyCode::F5,
|
||||
0x61 => KeyCode::F6,
|
||||
0x62 => KeyCode::F7,
|
||||
0x63 => KeyCode::F3,
|
||||
0x64 => KeyCode::F8,
|
||||
0x65 => KeyCode::F9,
|
||||
//0x66 => JIS Eisuu (macOS),
|
||||
0x67 => KeyCode::F11,
|
||||
//0x68 => JIS Kanna (macOS),
|
||||
0x69 => KeyCode::F13,
|
||||
0x6a => KeyCode::F16,
|
||||
0x6b => KeyCode::F14,
|
||||
//0x6c => unknown,
|
||||
0x6d => KeyCode::F10,
|
||||
//0x6e => unknown,
|
||||
0x6f => KeyCode::F12,
|
||||
//0x70 => unknown,
|
||||
0x71 => KeyCode::F15,
|
||||
0x72 => KeyCode::Insert,
|
||||
0x73 => KeyCode::Home,
|
||||
0x74 => KeyCode::PageUp,
|
||||
0x75 => KeyCode::Delete,
|
||||
0x76 => KeyCode::F4,
|
||||
0x77 => KeyCode::End,
|
||||
0x78 => KeyCode::F2,
|
||||
0x79 => KeyCode::PageDown,
|
||||
0x7a => KeyCode::F1,
|
||||
0x7b => KeyCode::ArrowLeft,
|
||||
0x7c => KeyCode::ArrowRight,
|
||||
0x7d => KeyCode::ArrowDown,
|
||||
0x7e => KeyCode::ArrowUp,
|
||||
//0x7f => unknown,
|
||||
|
||||
// 0xA is the caret (^) an macOS's German QERTZ layout. This key is at the same location as
|
||||
// backquote (`) on Windows' US layout.
|
||||
0xa => KeyCode::Backquote,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::MacOS(scancode as u16)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,3 +156,48 @@ mod core_video {
|
|||
}
|
||||
|
||||
pub use core_video::*;
|
||||
#[repr(transparent)]
|
||||
pub struct TISInputSource(std::ffi::c_void);
|
||||
pub type TISInputSourceRef = *mut TISInputSource;
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct UCKeyboardLayout(std::ffi::c_void);
|
||||
|
||||
pub type OptionBits = u32;
|
||||
pub type UniCharCount = std::os::raw::c_ulong;
|
||||
pub type UniChar = std::os::raw::c_ushort;
|
||||
pub type OSStatus = i32;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const kUCKeyActionDisplay: u16 = 3;
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const kUCKeyTranslateNoDeadKeysMask: OptionBits = 1;
|
||||
|
||||
#[link(name = "Carbon", kind = "framework")]
|
||||
extern "C" {
|
||||
pub static kTISPropertyUnicodeKeyLayoutData: CFStringRef;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn TISGetInputSourceProperty(
|
||||
inputSource: TISInputSourceRef,
|
||||
propertyKey: CFStringRef,
|
||||
) -> *mut c_void;
|
||||
|
||||
pub fn TISCopyCurrentKeyboardLayoutInputSource() -> TISInputSourceRef;
|
||||
|
||||
pub fn LMGetKbdType() -> u8;
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn UCKeyTranslate(
|
||||
keyLayoutPtr: *const UCKeyboardLayout,
|
||||
virtualKeyCode: u16,
|
||||
keyAction: u16,
|
||||
modifierKeyState: u32,
|
||||
keyboardType: u32,
|
||||
keyTranslateOptions: OptionBits,
|
||||
deadKeyState: *mut u32,
|
||||
maxStringLength: UniCharCount,
|
||||
actualStringLength: *mut UniCharCount,
|
||||
unicodeString: *mut UniChar,
|
||||
) -> OSStatus;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ use std::{fmt, ops::Deref};
|
|||
use self::window::WinitWindow;
|
||||
use self::window_delegate::WinitWindowDelegate;
|
||||
pub(crate) use self::{
|
||||
event::KeyEventExtra,
|
||||
event_loop::{
|
||||
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -209,3 +209,7 @@ pub(crate) fn set_ime_position_sync(window: &WinitWindow, logical_spot: LogicalP
|
|||
unsafe { Id::from_shared(window.view()) }.set_ime_position(logical_spot);
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn get_kbd_type() -> u8 {
|
||||
run_on_main(|| unsafe { ffi::LMGetKbdType() })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use std::{boxed::Box, os::raw::*, ptr, str, sync::Mutex};
|
||||
use std::{
|
||||
boxed::Box,
|
||||
collections::{HashMap, VecDeque},
|
||||
os::raw::*,
|
||||
ptr, str,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
use objc2::declare::{Ivar, IvarDrop};
|
||||
use objc2::foundation::{
|
||||
|
|
@ -11,23 +17,21 @@ use objc2::rc::{Id, Owned, Shared, WeakId};
|
|||
use objc2::runtime::{Object, Sel};
|
||||
use objc2::{class, declare_class, msg_send, msg_send_id, sel, ClassType};
|
||||
|
||||
use super::appkit::{
|
||||
NSApp, NSCursor, NSEvent, NSEventModifierFlags, NSEventPhase, NSResponder, NSTrackingRectTag,
|
||||
NSView,
|
||||
use super::{
|
||||
appkit::{NSApp, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTrackingRectTag, NSView},
|
||||
event::{code_to_key, code_to_location},
|
||||
};
|
||||
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
event::{
|
||||
DeviceEvent, ElementState, Event, Ime, KeyboardInput, ModifiersState, MouseButton,
|
||||
MouseScrollDelta, TouchPhase, VirtualKeyCode, WindowEvent,
|
||||
DeviceEvent, ElementState, Event, Ime, Modifiers, MouseButton, MouseScrollDelta,
|
||||
TouchPhase, WindowEvent,
|
||||
},
|
||||
keyboard::{Key, KeyCode, KeyLocation, ModifiersState},
|
||||
platform::scancode::KeyCodeExtScancode,
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
event::{
|
||||
char_to_keycode, check_function_keys, event_mods, modifier_event, scancode_to_keycode,
|
||||
EventWrapper,
|
||||
},
|
||||
event::{create_key_event, event_mods, EventWrapper},
|
||||
util,
|
||||
window::WinitWindow,
|
||||
DEVICE_ID,
|
||||
|
|
@ -66,12 +70,60 @@ enum ImeState {
|
|||
Commited,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct ModLocationMask: u8 {
|
||||
const LEFT = 1;
|
||||
const RIGHT = 2;
|
||||
}
|
||||
}
|
||||
impl ModLocationMask {
|
||||
fn from_location(loc: KeyLocation) -> ModLocationMask {
|
||||
match loc {
|
||||
KeyLocation::Left => ModLocationMask::LEFT,
|
||||
KeyLocation::Right => ModLocationMask::RIGHT,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_to_modifier(key: &Key) -> ModifiersState {
|
||||
match key {
|
||||
Key::Alt => ModifiersState::ALT,
|
||||
Key::Control => ModifiersState::CONTROL,
|
||||
Key::Super => ModifiersState::SUPER,
|
||||
Key::Shift => ModifiersState::SHIFT,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_right_modifier_code(key: &Key) -> KeyCode {
|
||||
match key {
|
||||
Key::Alt => KeyCode::AltRight,
|
||||
Key::Control => KeyCode::ControlRight,
|
||||
Key::Shift => KeyCode::ShiftRight,
|
||||
Key::Super => KeyCode::SuperRight,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_left_modifier_code(key: &Key) -> KeyCode {
|
||||
match key {
|
||||
Key::Alt => KeyCode::AltLeft,
|
||||
Key::Control => KeyCode::ControlLeft,
|
||||
Key::Shift => KeyCode::ShiftLeft,
|
||||
Key::Super => KeyCode::SuperLeft,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct ViewState {
|
||||
pub cursor_state: Mutex<CursorState>,
|
||||
ime_position: LogicalPosition<f64>,
|
||||
pub(super) modifiers: ModifiersState,
|
||||
pub(super) modifiers: Modifiers,
|
||||
phys_modifiers: HashMap<Key, ModLocationMask>,
|
||||
tracking_rect: Option<NSTrackingRectTag>,
|
||||
// phys_modifiers: HashSet<KeyCode>,
|
||||
ime_state: ImeState,
|
||||
input_source: String,
|
||||
|
||||
|
|
@ -85,54 +137,6 @@ pub(super) struct ViewState {
|
|||
forward_key_to_app: bool,
|
||||
}
|
||||
|
||||
fn get_characters(event: &NSEvent, ignore_modifiers: bool) -> String {
|
||||
if ignore_modifiers {
|
||||
event.charactersIgnoringModifiers()
|
||||
} else {
|
||||
event.characters()
|
||||
}
|
||||
.expect("expected characters to be non-null")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
// As defined in: https://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
|
||||
fn is_corporate_character(c: char) -> bool {
|
||||
matches!(c,
|
||||
'\u{F700}'..='\u{F747}'
|
||||
| '\u{F802}'..='\u{F84F}'
|
||||
| '\u{F850}'
|
||||
| '\u{F85C}'
|
||||
| '\u{F85D}'
|
||||
| '\u{F85F}'
|
||||
| '\u{F860}'..='\u{F86B}'
|
||||
| '\u{F870}'..='\u{F8FF}'
|
||||
)
|
||||
}
|
||||
|
||||
// Retrieves a layout-independent keycode given an event.
|
||||
fn retrieve_keycode(event: &NSEvent) -> Option<VirtualKeyCode> {
|
||||
#[inline]
|
||||
fn get_code(ev: &NSEvent, raw: bool) -> Option<VirtualKeyCode> {
|
||||
let characters = get_characters(ev, raw);
|
||||
characters.chars().next().and_then(char_to_keycode)
|
||||
}
|
||||
|
||||
// Cmd switches Roman letters for Dvorak-QWERTY layout, so we try modified characters first.
|
||||
// If we don't get a match, then we fall back to unmodified characters.
|
||||
let code = get_code(event, false).or_else(|| get_code(event, true));
|
||||
|
||||
// We've checked all layout related keys, so fall through to scancode.
|
||||
// Reaching this code means that the key is layout-independent (e.g. Backspace, Return).
|
||||
//
|
||||
// We're additionally checking here for F21-F24 keys, since their keycode
|
||||
// can vary, but we know that they are encoded
|
||||
// in characters property.
|
||||
code.or_else(|| {
|
||||
let scancode = event.scancode();
|
||||
scancode_to_keycode(scancode).or_else(|| check_function_keys(&get_characters(event, true)))
|
||||
})
|
||||
}
|
||||
|
||||
declare_class!(
|
||||
#[derive(Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
|
|
@ -162,6 +166,7 @@ declare_class!(
|
|||
cursor_state: Default::default(),
|
||||
ime_position: LogicalPosition::new(0.0, 0.0),
|
||||
modifiers: Default::default(),
|
||||
phys_modifiers: Default::default(),
|
||||
tracking_rect: None,
|
||||
ime_state: ImeState::Disabled,
|
||||
input_source: String::new(),
|
||||
|
|
@ -471,16 +476,6 @@ declare_class!(
|
|||
}
|
||||
|
||||
// Get the characters from the event.
|
||||
let ev_mods = event_mods(event);
|
||||
let ignore_alt_characters = match self.window().option_as_alt() {
|
||||
OptionAsAlt::OnlyLeft if event.lalt_pressed() => true,
|
||||
OptionAsAlt::OnlyRight if event.ralt_pressed() => true,
|
||||
OptionAsAlt::Both if ev_mods.alt() => true,
|
||||
_ => false,
|
||||
} && !ev_mods.ctrl()
|
||||
&& !ev_mods.logo();
|
||||
|
||||
let characters = get_characters(event, ignore_alt_characters);
|
||||
let old_ime_state = self.state.ime_state;
|
||||
self.state.forward_key_to_app = false;
|
||||
|
||||
|
|
@ -491,13 +486,7 @@ declare_class!(
|
|||
// `doCommandBySelector`. (doCommandBySelector means that the keyboard input
|
||||
// is not handled by IME and should be handled by the application)
|
||||
if self.state.ime_allowed {
|
||||
let new_event = if ignore_alt_characters {
|
||||
replace_event_chars(event, &characters)
|
||||
} else {
|
||||
event.copy()
|
||||
};
|
||||
|
||||
let events_for_nsview = NSArray::from_slice(&[new_event]);
|
||||
let events_for_nsview = NSArray::from_slice(&[event.copy()]);
|
||||
unsafe { self.interpretKeyEvents(&events_for_nsview) };
|
||||
|
||||
// If the text was commited we must treat the next keyboard event as IME related.
|
||||
|
|
@ -507,10 +496,7 @@ declare_class!(
|
|||
}
|
||||
}
|
||||
|
||||
let scancode = event.scancode() as u32;
|
||||
let virtual_keycode = retrieve_keycode(event);
|
||||
|
||||
self.update_potentially_stale_modifiers(event);
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
let had_ime_input = match self.state.ime_state {
|
||||
ImeState::Commited => {
|
||||
|
|
@ -524,89 +510,36 @@ declare_class!(
|
|||
};
|
||||
|
||||
if !had_ime_input || self.state.forward_key_to_app {
|
||||
#[allow(deprecated)]
|
||||
let key_event = create_key_event(event, true, event.is_a_repeat(), None);
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: ElementState::Pressed,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers: ev_mods,
|
||||
},
|
||||
event: key_event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
|
||||
for character in characters.chars().filter(|c| !is_corporate_character(*c)) {
|
||||
self.queue_event(WindowEvent::ReceivedCharacter(character));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[sel(keyUp:)]
|
||||
fn key_up(&mut self, event: &NSEvent) {
|
||||
trace_scope!("keyUp:");
|
||||
let scancode = event.scancode() as u32;
|
||||
let virtual_keycode = retrieve_keycode(event);
|
||||
|
||||
self.update_potentially_stale_modifiers(event);
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
// We want to send keyboard input when we are currently in the ground state.
|
||||
if matches!(self.state.ime_state, ImeState::Ground | ImeState::Disabled) {
|
||||
#[allow(deprecated)]
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: ElementState::Released,
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
event: create_key_event(event, false, false, None),
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[sel(flagsChanged:)]
|
||||
fn flags_changed(&mut self, event: &NSEvent) {
|
||||
fn flags_changed(&mut self, ns_event: &NSEvent) {
|
||||
trace_scope!("flagsChanged:");
|
||||
|
||||
if let Some(window_event) = modifier_event(
|
||||
event,
|
||||
NSEventModifierFlags::NSShiftKeyMask,
|
||||
self.state.modifiers.shift(),
|
||||
) {
|
||||
self.state.modifiers.toggle(ModifiersState::SHIFT);
|
||||
self.queue_event(window_event);
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(
|
||||
event,
|
||||
NSEventModifierFlags::NSControlKeyMask,
|
||||
self.state.modifiers.ctrl(),
|
||||
) {
|
||||
self.state.modifiers.toggle(ModifiersState::CTRL);
|
||||
self.queue_event(window_event);
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(
|
||||
event,
|
||||
NSEventModifierFlags::NSCommandKeyMask,
|
||||
self.state.modifiers.logo(),
|
||||
) {
|
||||
self.state.modifiers.toggle(ModifiersState::LOGO);
|
||||
self.queue_event(window_event);
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(
|
||||
event,
|
||||
NSEventModifierFlags::NSAlternateKeyMask,
|
||||
self.state.modifiers.alt(),
|
||||
) {
|
||||
self.state.modifiers.toggle(ModifiersState::ALT);
|
||||
self.queue_event(window_event);
|
||||
}
|
||||
|
||||
self.queue_event(WindowEvent::ModifiersChanged(self.state.modifiers));
|
||||
self.update_modifiers(ns_event, true);
|
||||
}
|
||||
|
||||
#[sel(insertTab:)]
|
||||
|
|
@ -636,25 +569,17 @@ declare_class!(
|
|||
#[sel(cancelOperation:)]
|
||||
fn cancel_operation(&mut self, _sender: *const Object) {
|
||||
trace_scope!("cancelOperation:");
|
||||
let scancode = 0x2f;
|
||||
let virtual_keycode = scancode_to_keycode(scancode);
|
||||
debug_assert_eq!(virtual_keycode, Some(VirtualKeyCode::Period));
|
||||
|
||||
let event = NSApp()
|
||||
.currentEvent()
|
||||
.expect("could not find current event");
|
||||
|
||||
self.update_potentially_stale_modifiers(&event);
|
||||
self.update_modifiers(&event, false);
|
||||
let event = create_key_event(&event, true, event.is_a_repeat(), None);
|
||||
|
||||
#[allow(deprecated)]
|
||||
self.queue_event(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: ElementState::Pressed,
|
||||
scancode: scancode as _,
|
||||
virtual_keycode,
|
||||
modifiers: event_mods(&event),
|
||||
},
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
|
|
@ -778,14 +703,13 @@ declare_class!(
|
|||
},
|
||||
};
|
||||
|
||||
self.update_potentially_stale_modifiers(event);
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
self.queue_device_event(DeviceEvent::MouseWheel { delta });
|
||||
self.queue_event(WindowEvent::MouseWheel {
|
||||
device_id: DEVICE_ID,
|
||||
delta,
|
||||
phase,
|
||||
modifiers: event_mods(event),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -947,25 +871,120 @@ impl WinitView {
|
|||
}
|
||||
|
||||
// Update `state.modifiers` if `event` has something different
|
||||
fn update_potentially_stale_modifiers(&mut self, event: &NSEvent) {
|
||||
let event_modifiers = event_mods(event);
|
||||
if self.state.modifiers != event_modifiers {
|
||||
self.state.modifiers = event_modifiers;
|
||||
fn update_modifiers(&mut self, ns_event: &NSEvent, is_flags_changed_event: bool) {
|
||||
use ElementState::{Pressed, Released};
|
||||
|
||||
self.queue_event(WindowEvent::ModifiersChanged(self.state.modifiers));
|
||||
let current_modifiers = event_mods(ns_event);
|
||||
let prev_modifiers = self.state.modifiers;
|
||||
|
||||
self.state.modifiers = current_modifiers;
|
||||
|
||||
// This function was called form the flagsChanged event, which is triggered
|
||||
// when the user presses/releases a modifier even if the same kind of modifier
|
||||
// has already been pressed
|
||||
if is_flags_changed_event {
|
||||
let scancode = ns_event.key_code();
|
||||
let keycode = KeyCode::from_scancode(scancode as u32);
|
||||
|
||||
// We'll correct the `is_press` later.
|
||||
let mut event = create_key_event(ns_event, false, false, Some(keycode));
|
||||
|
||||
let key = code_to_key(keycode, scancode);
|
||||
let event_modifier = key_to_modifier(&key);
|
||||
event.physical_key = keycode;
|
||||
event.logical_key = key.clone();
|
||||
event.location = code_to_location(keycode);
|
||||
let location_mask = ModLocationMask::from_location(event.location);
|
||||
|
||||
let phys_mod = self
|
||||
.state
|
||||
.phys_modifiers
|
||||
.entry(key)
|
||||
.or_insert(ModLocationMask::empty());
|
||||
|
||||
let is_active = current_modifiers.state().contains(event_modifier);
|
||||
let mut events = VecDeque::with_capacity(2);
|
||||
|
||||
// There is no API for getting whether the button was pressed or released
|
||||
// during this event. For this reason we have to do a bit of magic below
|
||||
// to come up with a good guess whether this key was pressed or released.
|
||||
// (This is not trivial because there are multiple buttons that may affect
|
||||
// the same modifier)
|
||||
if !is_active {
|
||||
event.state = Released;
|
||||
if phys_mod.contains(ModLocationMask::LEFT) {
|
||||
let mut event = event.clone();
|
||||
event.location = KeyLocation::Left;
|
||||
event.physical_key = get_left_modifier_code(&event.logical_key);
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
if phys_mod.contains(ModLocationMask::RIGHT) {
|
||||
event.location = KeyLocation::Right;
|
||||
event.physical_key = get_right_modifier_code(&event.logical_key);
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
*phys_mod = ModLocationMask::empty();
|
||||
} else {
|
||||
// is_active
|
||||
if *phys_mod == location_mask {
|
||||
// Here we hit a contradiction:
|
||||
// The modifier state was "changed" to active,
|
||||
// yet the only pressed modifier key was the one that we
|
||||
// just got a change event for.
|
||||
// This seemingly means that the only pressed modifier is now released,
|
||||
// but at the same time the modifier became active.
|
||||
//
|
||||
// But this scenario is possible if we released modifiers
|
||||
// while the application was not in focus. (Because we don't
|
||||
// get informed of modifier key events while the application
|
||||
// is not focused)
|
||||
|
||||
// In this case we prioritize the information
|
||||
// about the current modifier state which means
|
||||
// that the button was pressed.
|
||||
event.state = Pressed;
|
||||
} else {
|
||||
phys_mod.toggle(location_mask);
|
||||
let is_pressed = phys_mod.contains(location_mask);
|
||||
event.state = if is_pressed { Pressed } else { Released };
|
||||
}
|
||||
|
||||
events.push_back(WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
event,
|
||||
is_synthetic: false,
|
||||
});
|
||||
}
|
||||
|
||||
for event in events {
|
||||
self.queue_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
if prev_modifiers == current_modifiers {
|
||||
return;
|
||||
}
|
||||
|
||||
self.queue_event(WindowEvent::ModifiersChanged(self.state.modifiers));
|
||||
}
|
||||
|
||||
fn mouse_click(&mut self, event: &NSEvent, button_state: ElementState) {
|
||||
let button = mouse_button(event);
|
||||
|
||||
self.update_potentially_stale_modifiers(event);
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
self.queue_event(WindowEvent::MouseInput {
|
||||
device_id: DEVICE_ID,
|
||||
state: button_state,
|
||||
button,
|
||||
modifiers: event_mods(event),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -990,12 +1009,11 @@ impl WinitView {
|
|||
let y = view_rect.size.height as f64 - view_point.y as f64;
|
||||
let logical_position = LogicalPosition::new(x, y);
|
||||
|
||||
self.update_potentially_stale_modifiers(event);
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
self.queue_event(WindowEvent::CursorMoved {
|
||||
device_id: DEVICE_ID,
|
||||
position: logical_position.to_physical(self.scale_factor()),
|
||||
modifiers: event_mods(event),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1012,21 +1030,3 @@ fn mouse_button(event: &NSEvent) -> MouseButton {
|
|||
n => MouseButton::Other(n as u16),
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_event_chars(event: &NSEvent, characters: &str) -> Id<NSEvent, Shared> {
|
||||
let ns_chars = NSString::from_str(characters);
|
||||
let chars_ignoring_mods = event.charactersIgnoringModifiers().unwrap();
|
||||
|
||||
NSEvent::keyEventWithType(
|
||||
event.type_(),
|
||||
event.locationInWindow(),
|
||||
event.modifierFlags(),
|
||||
event.timestamp(),
|
||||
event.window_number(),
|
||||
None,
|
||||
&ns_chars,
|
||||
&chars_ignoring_mods,
|
||||
event.is_a_repeat(),
|
||||
event.scancode(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ use crate::{
|
|||
error::{ExternalError, NotSupportedError, OsError as RootOsError},
|
||||
event::WindowEvent,
|
||||
icon::Icon,
|
||||
platform::macos::{OptionAsAlt, WindowExtMacOS},
|
||||
platform::macos::WindowExtMacOS,
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
appkit::NSWindowOrderingMode,
|
||||
|
|
@ -85,7 +85,6 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
|||
pub disallow_hidpi: bool,
|
||||
pub has_shadow: bool,
|
||||
pub accepts_first_mouse: bool,
|
||||
pub option_as_alt: OptionAsAlt,
|
||||
}
|
||||
|
||||
impl Default for PlatformSpecificWindowBuilderAttributes {
|
||||
|
|
@ -101,7 +100,6 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
|
|||
disallow_hidpi: false,
|
||||
has_shadow: true,
|
||||
accepts_first_mouse: true,
|
||||
option_as_alt: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -162,9 +160,6 @@ pub struct SharedState {
|
|||
|
||||
/// The current resize incerments for the window content.
|
||||
pub(crate) resize_increments: NSSize,
|
||||
|
||||
/// The state of the `Option` as `Alt`.
|
||||
pub(crate) option_as_alt: OptionAsAlt,
|
||||
}
|
||||
|
||||
impl SharedState {
|
||||
|
|
@ -374,8 +369,6 @@ impl WinitWindow {
|
|||
this.center();
|
||||
}
|
||||
|
||||
this.set_option_as_alt(pl_attrs.option_as_alt);
|
||||
|
||||
Id::into_shared(this)
|
||||
})
|
||||
})
|
||||
|
|
@ -1271,6 +1264,10 @@ impl WinitWindow {
|
|||
pub fn title(&self) -> String {
|
||||
self.title_().to_string()
|
||||
}
|
||||
|
||||
pub fn reset_dead_keys(&self) {
|
||||
// (Artur) I couldn't find a way to implement this.
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowExtMacOS for WinitWindow {
|
||||
|
|
@ -1379,16 +1376,6 @@ impl WindowExtMacOS for WinitWindow {
|
|||
fn set_document_edited(&self, edited: bool) {
|
||||
self.setDocumentEdited(edited)
|
||||
}
|
||||
|
||||
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
|
||||
let mut shared_state_lock = self.shared_state.lock().unwrap();
|
||||
shared_state_lock.option_as_alt = option_as_alt;
|
||||
}
|
||||
|
||||
fn option_as_alt(&self) -> OptionAsAlt {
|
||||
let shared_state_lock = self.shared_state.lock().unwrap();
|
||||
shared_state_lock.option_as_alt
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_ns_theme() -> Theme {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ use super::appkit::{
|
|||
};
|
||||
use crate::{
|
||||
dpi::{LogicalPosition, LogicalSize},
|
||||
event::{Event, ModifiersState, WindowEvent},
|
||||
event::{Event, WindowEvent},
|
||||
keyboard::ModifiersState,
|
||||
platform_impl::platform::{
|
||||
app_state::AppState,
|
||||
event::{EventProxy, EventWrapper},
|
||||
|
|
@ -170,9 +171,11 @@ declare_class!(
|
|||
let mut view = unsafe { Id::from_shared(self.window.view()) };
|
||||
|
||||
// Both update the state and emit a ModifiersChanged event.
|
||||
if !view.state.modifiers.is_empty() {
|
||||
view.state.modifiers = ModifiersState::empty();
|
||||
self.queue_event(WindowEvent::ModifiersChanged(view.state.modifiers));
|
||||
if !view.state.modifiers.state().is_empty() {
|
||||
view.state.modifiers = ModifiersState::empty().into();
|
||||
self.queue_event(WindowEvent::ModifiersChanged(
|
||||
ModifiersState::empty().into(),
|
||||
));
|
||||
}
|
||||
|
||||
self.queue_event(WindowEvent::Focused(false));
|
||||
|
|
|
|||
|
|
@ -12,99 +12,102 @@ use orbclient::{
|
|||
use raw_window_handle::{OrbitalDisplayHandle, RawDisplayHandle};
|
||||
|
||||
use crate::{
|
||||
event::{self, StartCause, VirtualKeyCode},
|
||||
event::{self, Ime, Modifiers, StartCause},
|
||||
event_loop::{self, ControlFlow},
|
||||
keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NativeKey, NativeKeyCode,
|
||||
},
|
||||
window::WindowId as RootWindowId,
|
||||
};
|
||||
|
||||
use super::{
|
||||
DeviceId, MonitorHandle, PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket,
|
||||
WindowId, WindowProperties,
|
||||
DeviceId, KeyEventExtra, MonitorHandle, PlatformSpecificEventLoopAttributes, RedoxSocket,
|
||||
TimeSocket, WindowId, WindowProperties,
|
||||
};
|
||||
|
||||
fn convert_scancode(scancode: u8) -> Option<VirtualKeyCode> {
|
||||
fn convert_scancode(scancode: u8) -> KeyCode {
|
||||
match scancode {
|
||||
orbclient::K_A => Some(VirtualKeyCode::A),
|
||||
orbclient::K_B => Some(VirtualKeyCode::B),
|
||||
orbclient::K_C => Some(VirtualKeyCode::C),
|
||||
orbclient::K_D => Some(VirtualKeyCode::D),
|
||||
orbclient::K_E => Some(VirtualKeyCode::E),
|
||||
orbclient::K_F => Some(VirtualKeyCode::F),
|
||||
orbclient::K_G => Some(VirtualKeyCode::G),
|
||||
orbclient::K_H => Some(VirtualKeyCode::H),
|
||||
orbclient::K_I => Some(VirtualKeyCode::I),
|
||||
orbclient::K_J => Some(VirtualKeyCode::J),
|
||||
orbclient::K_K => Some(VirtualKeyCode::K),
|
||||
orbclient::K_L => Some(VirtualKeyCode::L),
|
||||
orbclient::K_M => Some(VirtualKeyCode::M),
|
||||
orbclient::K_N => Some(VirtualKeyCode::N),
|
||||
orbclient::K_O => Some(VirtualKeyCode::O),
|
||||
orbclient::K_P => Some(VirtualKeyCode::P),
|
||||
orbclient::K_Q => Some(VirtualKeyCode::Q),
|
||||
orbclient::K_R => Some(VirtualKeyCode::R),
|
||||
orbclient::K_S => Some(VirtualKeyCode::S),
|
||||
orbclient::K_T => Some(VirtualKeyCode::T),
|
||||
orbclient::K_U => Some(VirtualKeyCode::U),
|
||||
orbclient::K_V => Some(VirtualKeyCode::V),
|
||||
orbclient::K_W => Some(VirtualKeyCode::W),
|
||||
orbclient::K_X => Some(VirtualKeyCode::X),
|
||||
orbclient::K_Y => Some(VirtualKeyCode::Y),
|
||||
orbclient::K_Z => Some(VirtualKeyCode::Z),
|
||||
orbclient::K_0 => Some(VirtualKeyCode::Key0),
|
||||
orbclient::K_1 => Some(VirtualKeyCode::Key1),
|
||||
orbclient::K_2 => Some(VirtualKeyCode::Key2),
|
||||
orbclient::K_3 => Some(VirtualKeyCode::Key3),
|
||||
orbclient::K_4 => Some(VirtualKeyCode::Key4),
|
||||
orbclient::K_5 => Some(VirtualKeyCode::Key5),
|
||||
orbclient::K_6 => Some(VirtualKeyCode::Key6),
|
||||
orbclient::K_7 => Some(VirtualKeyCode::Key7),
|
||||
orbclient::K_8 => Some(VirtualKeyCode::Key8),
|
||||
orbclient::K_9 => Some(VirtualKeyCode::Key9),
|
||||
orbclient::K_A => KeyCode::KeyA,
|
||||
orbclient::K_B => KeyCode::KeyB,
|
||||
orbclient::K_C => KeyCode::KeyC,
|
||||
orbclient::K_D => KeyCode::KeyD,
|
||||
orbclient::K_E => KeyCode::KeyE,
|
||||
orbclient::K_F => KeyCode::KeyF,
|
||||
orbclient::K_G => KeyCode::KeyG,
|
||||
orbclient::K_H => KeyCode::KeyH,
|
||||
orbclient::K_I => KeyCode::KeyI,
|
||||
orbclient::K_J => KeyCode::KeyJ,
|
||||
orbclient::K_K => KeyCode::KeyK,
|
||||
orbclient::K_L => KeyCode::KeyL,
|
||||
orbclient::K_M => KeyCode::KeyM,
|
||||
orbclient::K_N => KeyCode::KeyN,
|
||||
orbclient::K_O => KeyCode::KeyO,
|
||||
orbclient::K_P => KeyCode::KeyP,
|
||||
orbclient::K_Q => KeyCode::KeyQ,
|
||||
orbclient::K_R => KeyCode::KeyR,
|
||||
orbclient::K_S => KeyCode::KeyS,
|
||||
orbclient::K_T => KeyCode::KeyT,
|
||||
orbclient::K_U => KeyCode::KeyU,
|
||||
orbclient::K_V => KeyCode::KeyV,
|
||||
orbclient::K_W => KeyCode::KeyW,
|
||||
orbclient::K_X => KeyCode::KeyX,
|
||||
orbclient::K_Y => KeyCode::KeyY,
|
||||
orbclient::K_Z => KeyCode::KeyZ,
|
||||
orbclient::K_0 => KeyCode::Digit0,
|
||||
orbclient::K_1 => KeyCode::Digit1,
|
||||
orbclient::K_2 => KeyCode::Digit2,
|
||||
orbclient::K_3 => KeyCode::Digit3,
|
||||
orbclient::K_4 => KeyCode::Digit4,
|
||||
orbclient::K_5 => KeyCode::Digit5,
|
||||
orbclient::K_6 => KeyCode::Digit6,
|
||||
orbclient::K_7 => KeyCode::Digit7,
|
||||
orbclient::K_8 => KeyCode::Digit8,
|
||||
orbclient::K_9 => KeyCode::Digit9,
|
||||
|
||||
orbclient::K_TICK => Some(VirtualKeyCode::Grave),
|
||||
orbclient::K_MINUS => Some(VirtualKeyCode::Minus),
|
||||
orbclient::K_EQUALS => Some(VirtualKeyCode::Equals),
|
||||
orbclient::K_BACKSLASH => Some(VirtualKeyCode::Backslash),
|
||||
orbclient::K_BRACE_OPEN => Some(VirtualKeyCode::LBracket),
|
||||
orbclient::K_BRACE_CLOSE => Some(VirtualKeyCode::RBracket),
|
||||
orbclient::K_SEMICOLON => Some(VirtualKeyCode::Semicolon),
|
||||
orbclient::K_QUOTE => Some(VirtualKeyCode::Apostrophe),
|
||||
orbclient::K_COMMA => Some(VirtualKeyCode::Comma),
|
||||
orbclient::K_PERIOD => Some(VirtualKeyCode::Period),
|
||||
orbclient::K_SLASH => Some(VirtualKeyCode::Slash),
|
||||
orbclient::K_BKSP => Some(VirtualKeyCode::Back),
|
||||
orbclient::K_SPACE => Some(VirtualKeyCode::Space),
|
||||
orbclient::K_TAB => Some(VirtualKeyCode::Tab),
|
||||
//orbclient::K_CAPS => Some(VirtualKeyCode::CAPS),
|
||||
orbclient::K_LEFT_SHIFT => Some(VirtualKeyCode::LShift),
|
||||
orbclient::K_RIGHT_SHIFT => Some(VirtualKeyCode::RShift),
|
||||
orbclient::K_CTRL => Some(VirtualKeyCode::LControl),
|
||||
orbclient::K_ALT => Some(VirtualKeyCode::LAlt),
|
||||
orbclient::K_ENTER => Some(VirtualKeyCode::Return),
|
||||
orbclient::K_ESC => Some(VirtualKeyCode::Escape),
|
||||
orbclient::K_F1 => Some(VirtualKeyCode::F1),
|
||||
orbclient::K_F2 => Some(VirtualKeyCode::F2),
|
||||
orbclient::K_F3 => Some(VirtualKeyCode::F3),
|
||||
orbclient::K_F4 => Some(VirtualKeyCode::F4),
|
||||
orbclient::K_F5 => Some(VirtualKeyCode::F5),
|
||||
orbclient::K_F6 => Some(VirtualKeyCode::F6),
|
||||
orbclient::K_F7 => Some(VirtualKeyCode::F7),
|
||||
orbclient::K_F8 => Some(VirtualKeyCode::F8),
|
||||
orbclient::K_F9 => Some(VirtualKeyCode::F9),
|
||||
orbclient::K_F10 => Some(VirtualKeyCode::F10),
|
||||
orbclient::K_HOME => Some(VirtualKeyCode::Home),
|
||||
orbclient::K_UP => Some(VirtualKeyCode::Up),
|
||||
orbclient::K_PGUP => Some(VirtualKeyCode::PageUp),
|
||||
orbclient::K_LEFT => Some(VirtualKeyCode::Left),
|
||||
orbclient::K_RIGHT => Some(VirtualKeyCode::Right),
|
||||
orbclient::K_END => Some(VirtualKeyCode::End),
|
||||
orbclient::K_DOWN => Some(VirtualKeyCode::Down),
|
||||
orbclient::K_PGDN => Some(VirtualKeyCode::PageDown),
|
||||
orbclient::K_DEL => Some(VirtualKeyCode::Delete),
|
||||
orbclient::K_F11 => Some(VirtualKeyCode::F11),
|
||||
orbclient::K_F12 => Some(VirtualKeyCode::F12),
|
||||
orbclient::K_TICK => KeyCode::Backquote,
|
||||
orbclient::K_MINUS => KeyCode::Minus,
|
||||
orbclient::K_EQUALS => KeyCode::Equal,
|
||||
orbclient::K_BACKSLASH => KeyCode::Backslash,
|
||||
orbclient::K_BRACE_OPEN => KeyCode::BracketLeft,
|
||||
orbclient::K_BRACE_CLOSE => KeyCode::BracketRight,
|
||||
orbclient::K_SEMICOLON => KeyCode::Semicolon,
|
||||
orbclient::K_QUOTE => KeyCode::Quote,
|
||||
orbclient::K_COMMA => KeyCode::Comma,
|
||||
orbclient::K_PERIOD => KeyCode::Period,
|
||||
orbclient::K_SLASH => KeyCode::Slash,
|
||||
orbclient::K_BKSP => KeyCode::Backspace,
|
||||
orbclient::K_SPACE => KeyCode::Space,
|
||||
orbclient::K_TAB => KeyCode::Tab,
|
||||
//orbclient::K_CAPS => KeyCode::CAPS,
|
||||
orbclient::K_LEFT_SHIFT => KeyCode::ShiftLeft,
|
||||
orbclient::K_RIGHT_SHIFT => KeyCode::ShiftRight,
|
||||
orbclient::K_CTRL => KeyCode::ControlLeft,
|
||||
orbclient::K_ALT => KeyCode::AltLeft,
|
||||
orbclient::K_ENTER => KeyCode::Enter,
|
||||
orbclient::K_ESC => KeyCode::Escape,
|
||||
orbclient::K_F1 => KeyCode::F1,
|
||||
orbclient::K_F2 => KeyCode::F2,
|
||||
orbclient::K_F3 => KeyCode::F3,
|
||||
orbclient::K_F4 => KeyCode::F4,
|
||||
orbclient::K_F5 => KeyCode::F5,
|
||||
orbclient::K_F6 => KeyCode::F6,
|
||||
orbclient::K_F7 => KeyCode::F7,
|
||||
orbclient::K_F8 => KeyCode::F8,
|
||||
orbclient::K_F9 => KeyCode::F9,
|
||||
orbclient::K_F10 => KeyCode::F10,
|
||||
orbclient::K_HOME => KeyCode::Home,
|
||||
orbclient::K_UP => KeyCode::ArrowUp,
|
||||
orbclient::K_PGUP => KeyCode::PageUp,
|
||||
orbclient::K_LEFT => KeyCode::ArrowLeft,
|
||||
orbclient::K_RIGHT => KeyCode::ArrowRight,
|
||||
orbclient::K_END => KeyCode::End,
|
||||
orbclient::K_DOWN => KeyCode::ArrowDown,
|
||||
orbclient::K_PGDN => KeyCode::PageDown,
|
||||
orbclient::K_DEL => KeyCode::Delete,
|
||||
orbclient::K_F11 => KeyCode::F11,
|
||||
orbclient::K_F12 => KeyCode::F12,
|
||||
|
||||
_ => None,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::Unidentified),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,16 +150,16 @@ struct EventState {
|
|||
}
|
||||
|
||||
impl EventState {
|
||||
fn key(&mut self, vk: VirtualKeyCode, pressed: bool) {
|
||||
match vk {
|
||||
VirtualKeyCode::LShift => self.keyboard.set(KeyboardModifierState::LSHIFT, pressed),
|
||||
VirtualKeyCode::RShift => self.keyboard.set(KeyboardModifierState::RSHIFT, pressed),
|
||||
VirtualKeyCode::LControl => self.keyboard.set(KeyboardModifierState::LCTRL, pressed),
|
||||
VirtualKeyCode::RControl => self.keyboard.set(KeyboardModifierState::RCTRL, pressed),
|
||||
VirtualKeyCode::LAlt => self.keyboard.set(KeyboardModifierState::LALT, pressed),
|
||||
VirtualKeyCode::RAlt => self.keyboard.set(KeyboardModifierState::RALT, pressed),
|
||||
VirtualKeyCode::LWin => self.keyboard.set(KeyboardModifierState::LSUPER, pressed),
|
||||
VirtualKeyCode::RWin => self.keyboard.set(KeyboardModifierState::RSUPER, pressed),
|
||||
fn key(&mut self, code: KeyCode, pressed: bool) {
|
||||
match code {
|
||||
KeyCode::ShiftLeft => self.keyboard.set(KeyboardModifierState::LSHIFT, pressed),
|
||||
KeyCode::ShiftRight => self.keyboard.set(KeyboardModifierState::RSHIFT, pressed),
|
||||
KeyCode::ControlLeft => self.keyboard.set(KeyboardModifierState::LCTRL, pressed),
|
||||
KeyCode::ControlRight => self.keyboard.set(KeyboardModifierState::RCTRL, pressed),
|
||||
KeyCode::AltLeft => self.keyboard.set(KeyboardModifierState::LALT, pressed),
|
||||
KeyCode::AltRight => self.keyboard.set(KeyboardModifierState::RALT, pressed),
|
||||
KeyCode::SuperLeft => self.keyboard.set(KeyboardModifierState::LSUPER, pressed),
|
||||
KeyCode::SuperRight => self.keyboard.set(KeyboardModifierState::RSUPER, pressed),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -185,33 +188,78 @@ impl EventState {
|
|||
None
|
||||
}
|
||||
|
||||
fn modifiers(&self) -> event::ModifiersState {
|
||||
let mut modifiers = event::ModifiersState::empty();
|
||||
fn modifiers(&self) -> Modifiers {
|
||||
let mut state = ModifiersState::empty();
|
||||
let mut pressed_mods = ModifiersKeys::empty();
|
||||
|
||||
if self
|
||||
.keyboard
|
||||
.intersects(KeyboardModifierState::LSHIFT | KeyboardModifierState::RSHIFT)
|
||||
{
|
||||
modifiers |= event::ModifiersState::SHIFT;
|
||||
state |= ModifiersState::SHIFT;
|
||||
}
|
||||
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::LSHIFT,
|
||||
self.keyboard.contains(KeyboardModifierState::LSHIFT),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::RSHIFT,
|
||||
self.keyboard.contains(KeyboardModifierState::RSHIFT),
|
||||
);
|
||||
|
||||
if self
|
||||
.keyboard
|
||||
.intersects(KeyboardModifierState::LCTRL | KeyboardModifierState::RCTRL)
|
||||
{
|
||||
modifiers |= event::ModifiersState::CTRL;
|
||||
state |= ModifiersState::CONTROL;
|
||||
}
|
||||
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::LCONTROL,
|
||||
self.keyboard.contains(KeyboardModifierState::LCTRL),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::RCONTROL,
|
||||
self.keyboard.contains(KeyboardModifierState::RCTRL),
|
||||
);
|
||||
|
||||
if self
|
||||
.keyboard
|
||||
.intersects(KeyboardModifierState::LALT | KeyboardModifierState::RALT)
|
||||
{
|
||||
modifiers |= event::ModifiersState::ALT;
|
||||
state |= ModifiersState::ALT;
|
||||
}
|
||||
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::LALT,
|
||||
self.keyboard.contains(KeyboardModifierState::LALT),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::RALT,
|
||||
self.keyboard.contains(KeyboardModifierState::RALT),
|
||||
);
|
||||
|
||||
if self
|
||||
.keyboard
|
||||
.intersects(KeyboardModifierState::LSUPER | KeyboardModifierState::RSUPER)
|
||||
{
|
||||
modifiers |= event::ModifiersState::LOGO
|
||||
state |= ModifiersState::SUPER
|
||||
}
|
||||
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::LSUPER,
|
||||
self.keyboard.contains(KeyboardModifierState::LSUPER),
|
||||
);
|
||||
pressed_mods.set(
|
||||
ModifiersKeys::RSUPER,
|
||||
self.keyboard.contains(KeyboardModifierState::RSUPER),
|
||||
);
|
||||
|
||||
Modifiers {
|
||||
state,
|
||||
pressed_mods,
|
||||
}
|
||||
modifiers
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -277,32 +325,44 @@ impl<T: 'static> EventLoop<T> {
|
|||
pressed,
|
||||
}) => {
|
||||
if scancode != 0 {
|
||||
let vk_opt = convert_scancode(scancode);
|
||||
if let Some(vk) = vk_opt {
|
||||
event_state.key(vk, pressed);
|
||||
}
|
||||
event_handler(
|
||||
#[allow(deprecated)]
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
input: event::KeyboardInput {
|
||||
scancode: scancode as u32,
|
||||
state: element_state(pressed),
|
||||
virtual_keycode: vk_opt,
|
||||
modifiers: event_state.modifiers(),
|
||||
},
|
||||
is_synthetic: false,
|
||||
let code = convert_scancode(scancode);
|
||||
let modifiers_before = event_state.keyboard;
|
||||
event_state.key(code, pressed);
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
event: event::KeyEvent {
|
||||
logical_key: Key::Unidentified(NativeKey::Unidentified),
|
||||
physical_key: code,
|
||||
location: KeyLocation::Standard,
|
||||
state: element_state(pressed),
|
||||
repeat: false,
|
||||
text: None,
|
||||
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// If the state of the modifiers has changed, send the event.
|
||||
if modifiers_before != event_state.keyboard {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::ModifiersChanged(event_state.modifiers()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
EventOption::TextInput(TextInputEvent { character }) => {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::ReceivedCharacter(character),
|
||||
event: event::WindowEvent::Ime(Ime::Preedit("".into(), None)),
|
||||
});
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Ime(Ime::Commit(character.into())),
|
||||
});
|
||||
}
|
||||
EventOption::Mouse(MouseEvent { x, y }) => {
|
||||
|
|
@ -311,7 +371,6 @@ impl<T: 'static> EventLoop<T> {
|
|||
event: event::WindowEvent::CursorMoved {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
position: (x, y).into(),
|
||||
modifiers: event_state.modifiers(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -327,7 +386,6 @@ impl<T: 'static> EventLoop<T> {
|
|||
device_id: event::DeviceId(DeviceId),
|
||||
state,
|
||||
button,
|
||||
modifiers: event_state.modifiers(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -339,7 +397,6 @@ impl<T: 'static> EventLoop<T> {
|
|||
device_id: event::DeviceId(DeviceId),
|
||||
delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: event::TouchPhase::Moved,
|
||||
modifiers: event_state.modifiers(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,3 +251,6 @@ impl VideoMode {
|
|||
self.monitor.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {}
|
||||
|
|
|
|||
|
|
@ -166,6 +166,11 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset_dead_keys(&self) {
|
||||
// TODO?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
|
|
|
|||
|
|
@ -1,29 +1,56 @@
|
|||
use std::cell::RefCell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::clone::Clone;
|
||||
use std::collections::{vec_deque::IntoIter as VecDequeIter, VecDeque};
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
|
||||
use raw_window_handle::{RawDisplayHandle, WebDisplayHandle};
|
||||
|
||||
use super::{
|
||||
super::monitor::MonitorHandle, backend, device::DeviceId, proxy::EventLoopProxy, runner,
|
||||
super::{monitor::MonitorHandle, KeyEventExtra},
|
||||
backend,
|
||||
device::DeviceId,
|
||||
proxy::EventLoopProxy,
|
||||
runner,
|
||||
window::WindowId,
|
||||
};
|
||||
use crate::dpi::{PhysicalSize, Size};
|
||||
use crate::event::{
|
||||
DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, KeyboardInput, Touch, TouchPhase,
|
||||
DeviceEvent, DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase,
|
||||
WindowEvent,
|
||||
};
|
||||
use crate::keyboard::ModifiersState;
|
||||
use crate::window::{Theme, WindowId as RootWindowId};
|
||||
|
||||
#[derive(Default)]
|
||||
struct ModifiersShared(Rc<Cell<ModifiersState>>);
|
||||
|
||||
impl ModifiersShared {
|
||||
fn set(&self, new: ModifiersState) {
|
||||
self.0.set(new)
|
||||
}
|
||||
|
||||
fn get(&self) -> ModifiersState {
|
||||
self.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ModifiersShared {
|
||||
fn clone(&self) -> Self {
|
||||
Self(Rc::clone(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoopWindowTarget<T: 'static> {
|
||||
pub(crate) runner: runner::Shared<T>,
|
||||
modifiers: ModifiersShared,
|
||||
}
|
||||
|
||||
impl<T> Clone for EventLoopWindowTarget<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
runner: self.runner.clone(),
|
||||
modifiers: self.modifiers.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,6 +59,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
pub fn new() -> Self {
|
||||
Self {
|
||||
runner: runner::Shared::new(),
|
||||
modifiers: ModifiersShared::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +84,7 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
canvas: &Rc<RefCell<backend::Canvas>>,
|
||||
id: WindowId,
|
||||
prevent_default: bool,
|
||||
has_focus: Rc<RefCell<bool>>,
|
||||
has_focus: Rc<Cell<bool>>,
|
||||
) {
|
||||
self.runner.add_canvas(RootWindowId(id), canvas);
|
||||
let mut canvas = canvas.borrow_mut();
|
||||
|
|
@ -67,115 +95,185 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
|
||||
let runner = self.runner.clone();
|
||||
let has_focus_clone = has_focus.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
canvas.on_blur(move || {
|
||||
*has_focus_clone.borrow_mut() = false;
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Focused(false),
|
||||
has_focus_clone.set(false);
|
||||
|
||||
let clear_modifiers = (!modifiers.get().is_empty()).then(|| {
|
||||
modifiers.set(ModifiersState::empty());
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(ModifiersState::empty().into()),
|
||||
}
|
||||
});
|
||||
|
||||
runner.send_events(
|
||||
clear_modifiers
|
||||
.into_iter()
|
||||
.chain(iter::once(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Focused(false),
|
||||
})),
|
||||
);
|
||||
});
|
||||
|
||||
let runner = self.runner.clone();
|
||||
let has_focus_clone = has_focus.clone();
|
||||
canvas.on_focus(move || {
|
||||
*has_focus_clone.borrow_mut() = true;
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Focused(true),
|
||||
});
|
||||
if !has_focus_clone.replace(true) {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Focused(true),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let runner = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
canvas.on_keyboard_press(
|
||||
move |scancode, virtual_keycode, modifiers| {
|
||||
#[allow(deprecated)]
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
||||
input: KeyboardInput {
|
||||
scancode,
|
||||
state: ElementState::Pressed,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
move |physical_key, logical_key, text, location, repeat, active_modifiers| {
|
||||
let modifiers_changed = (modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
runner.send_events(
|
||||
iter::once(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
||||
event: KeyEvent {
|
||||
physical_key,
|
||||
logical_key,
|
||||
text,
|
||||
location,
|
||||
state: ElementState::Pressed,
|
||||
repeat,
|
||||
platform_specific: KeyEventExtra,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
})
|
||||
.chain(modifiers_changed),
|
||||
);
|
||||
},
|
||||
prevent_default,
|
||||
);
|
||||
|
||||
let runner = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
canvas.on_keyboard_release(
|
||||
move |scancode, virtual_keycode, modifiers| {
|
||||
#[allow(deprecated)]
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
||||
input: KeyboardInput {
|
||||
scancode,
|
||||
state: ElementState::Released,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
move |physical_key, logical_key, text, location, repeat, active_modifiers| {
|
||||
let modifiers_changed = (modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
runner.send_events(
|
||||
iter::once(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id: RootDeviceId(unsafe { DeviceId::dummy() }),
|
||||
event: KeyEvent {
|
||||
physical_key,
|
||||
logical_key,
|
||||
text,
|
||||
location,
|
||||
state: ElementState::Released,
|
||||
repeat,
|
||||
platform_specific: KeyEventExtra,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
})
|
||||
.chain(modifiers_changed),
|
||||
)
|
||||
},
|
||||
prevent_default,
|
||||
);
|
||||
|
||||
let runner = self.runner.clone();
|
||||
canvas.on_received_character(
|
||||
move |char_code| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
let modifiers = self.modifiers.clone();
|
||||
let has_focus_clone = has_focus.clone();
|
||||
canvas.on_cursor_leave(move |pointer_id, active_modifiers| {
|
||||
let modifiers_changed = (has_focus_clone.get() && modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
runner.send_events(modifiers_changed.into_iter().chain(iter::once(
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ReceivedCharacter(char_code),
|
||||
});
|
||||
},
|
||||
prevent_default,
|
||||
);
|
||||
|
||||
let runner = self.runner.clone();
|
||||
canvas.on_cursor_leave(move |pointer_id| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::CursorLeft {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
event: WindowEvent::CursorLeft {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
},
|
||||
},
|
||||
});
|
||||
)));
|
||||
});
|
||||
|
||||
let runner = self.runner.clone();
|
||||
canvas.on_cursor_enter(move |pointer_id| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::CursorEntered {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
let modifiers = self.modifiers.clone();
|
||||
let has_focus_clone = has_focus.clone();
|
||||
canvas.on_cursor_enter(move |pointer_id, active_modifiers| {
|
||||
let modifiers_changed = (has_focus_clone.get() && modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
runner.send_events(modifiers_changed.into_iter().chain(iter::once(
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::CursorEntered {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
},
|
||||
},
|
||||
});
|
||||
)));
|
||||
});
|
||||
|
||||
let runner = self.runner.clone();
|
||||
let runner_touch = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
let has_focus_clone = has_focus.clone();
|
||||
canvas.on_cursor_move(
|
||||
move |pointer_id, position, delta, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::CursorMoved {
|
||||
move |pointer_id, position, delta, active_modifiers| {
|
||||
let modifiers_changed =
|
||||
(has_focus_clone.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
runner.send_events(modifiers_changed.into_iter().chain([
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
position,
|
||||
},
|
||||
},
|
||||
Event::DeviceEvent {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
position,
|
||||
modifiers,
|
||||
event: DeviceEvent::MouseMotion {
|
||||
delta: (delta.x, delta.y),
|
||||
},
|
||||
},
|
||||
});
|
||||
runner.send_event(Event::DeviceEvent {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
event: DeviceEvent::MouseMotion {
|
||||
delta: (delta.x, delta.y),
|
||||
},
|
||||
});
|
||||
]));
|
||||
},
|
||||
move |device_id, location, force| {
|
||||
runner_touch.send_event(Event::WindowEvent {
|
||||
|
|
@ -194,36 +292,44 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
|
||||
let runner = self.runner.clone();
|
||||
let runner_touch = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
let has_focus_clone = has_focus.clone();
|
||||
canvas.on_mouse_press(
|
||||
move |pointer_id, position, button, modifiers| {
|
||||
*has_focus.borrow_mut() = true;
|
||||
move |pointer_id, position, button, active_modifiers| {
|
||||
let focus_changed =
|
||||
(!has_focus_clone.replace(true)).then_some(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Focused(true),
|
||||
});
|
||||
|
||||
let modifiers_changed = (modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
// A mouse down event may come in without any prior CursorMoved events,
|
||||
// therefore we should send a CursorMoved event to make sure that the
|
||||
// user code has the correct cursor position.
|
||||
runner.send_events(
|
||||
std::iter::once(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::Focused(true),
|
||||
})
|
||||
.chain(std::iter::once(Event::WindowEvent {
|
||||
runner.send_events(focus_changed.into_iter().chain(modifiers_changed).chain([
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
position,
|
||||
modifiers,
|
||||
},
|
||||
}))
|
||||
.chain(std::iter::once(Event::WindowEvent {
|
||||
},
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::MouseInput {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
state: ElementState::Pressed,
|
||||
button,
|
||||
modifiers,
|
||||
},
|
||||
})),
|
||||
);
|
||||
},
|
||||
]));
|
||||
},
|
||||
move |device_id, location, force| {
|
||||
runner_touch.send_event(Event::WindowEvent {
|
||||
|
|
@ -241,17 +347,29 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
|
||||
let runner = self.runner.clone();
|
||||
let runner_touch = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
let has_focus_clone = has_focus.clone();
|
||||
canvas.on_mouse_release(
|
||||
move |pointer_id, button, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::MouseInput {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
state: ElementState::Released,
|
||||
button,
|
||||
modifiers,
|
||||
move |pointer_id, button, active_modifiers| {
|
||||
let modifiers_changed =
|
||||
(has_focus_clone.get() && modifiers.get() != active_modifiers).then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
runner.send_events(modifiers_changed.into_iter().chain(iter::once(
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::MouseInput {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
state: ElementState::Released,
|
||||
button,
|
||||
},
|
||||
},
|
||||
});
|
||||
)));
|
||||
},
|
||||
move |device_id, location, force| {
|
||||
runner_touch.send_event(Event::WindowEvent {
|
||||
|
|
@ -268,17 +386,28 @@ impl<T> EventLoopWindowTarget<T> {
|
|||
);
|
||||
|
||||
let runner = self.runner.clone();
|
||||
let modifiers = self.modifiers.clone();
|
||||
canvas.on_mouse_wheel(
|
||||
move |pointer_id, delta, modifiers| {
|
||||
runner.send_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::MouseWheel {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
delta,
|
||||
phase: TouchPhase::Moved,
|
||||
modifiers,
|
||||
move |pointer_id, delta, active_modifiers| {
|
||||
let modifiers_changed = (has_focus.get() && modifiers.get() != active_modifiers)
|
||||
.then(|| {
|
||||
modifiers.set(active_modifiers);
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::ModifiersChanged(active_modifiers.into()),
|
||||
}
|
||||
});
|
||||
|
||||
runner.send_events(modifiers_changed.into_iter().chain(iter::once(
|
||||
Event::WindowEvent {
|
||||
window_id: RootWindowId(id),
|
||||
event: WindowEvent::MouseWheel {
|
||||
device_id: RootDeviceId(DeviceId(pointer_id)),
|
||||
delta,
|
||||
phase: TouchPhase::Moved,
|
||||
},
|
||||
},
|
||||
});
|
||||
)));
|
||||
},
|
||||
prevent_default,
|
||||
);
|
||||
|
|
|
|||
522
src/platform_impl/web/keyboard.rs
Normal file
522
src/platform_impl/web/keyboard.rs
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
use smol_str::SmolStr;
|
||||
|
||||
use crate::keyboard::{Key, KeyCode, NativeKey, NativeKeyCode};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub(crate) struct KeyEventExtra;
|
||||
|
||||
impl Key {
|
||||
pub(crate) fn from_key_attribute_value(kav: &str) -> Self {
|
||||
match kav {
|
||||
"Unidentified" => Key::Unidentified(NativeKey::Web(SmolStr::new(kav))),
|
||||
"Dead" => Key::Dead(None),
|
||||
"Alt" => Key::Alt,
|
||||
"AltGraph" => Key::AltGraph,
|
||||
"CapsLock" => Key::CapsLock,
|
||||
"Control" => Key::Control,
|
||||
"Fn" => Key::Fn,
|
||||
"FnLock" => Key::FnLock,
|
||||
"NumLock" => Key::NumLock,
|
||||
"ScrollLock" => Key::ScrollLock,
|
||||
"Shift" => Key::Shift,
|
||||
"Symbol" => Key::Symbol,
|
||||
"SymbolLock" => Key::SymbolLock,
|
||||
"Hyper" => Key::Hyper,
|
||||
"Meta" => Key::Super,
|
||||
"Enter" => Key::Enter,
|
||||
"Tab" => Key::Tab,
|
||||
" " => Key::Space,
|
||||
"ArrowDown" => Key::ArrowDown,
|
||||
"ArrowLeft" => Key::ArrowLeft,
|
||||
"ArrowRight" => Key::ArrowRight,
|
||||
"ArrowUp" => Key::ArrowUp,
|
||||
"End" => Key::End,
|
||||
"Home" => Key::Home,
|
||||
"PageDown" => Key::PageDown,
|
||||
"PageUp" => Key::PageUp,
|
||||
"Backspace" => Key::Backspace,
|
||||
"Clear" => Key::Clear,
|
||||
"Copy" => Key::Copy,
|
||||
"CrSel" => Key::CrSel,
|
||||
"Cut" => Key::Cut,
|
||||
"Delete" => Key::Delete,
|
||||
"EraseEof" => Key::EraseEof,
|
||||
"ExSel" => Key::ExSel,
|
||||
"Insert" => Key::Insert,
|
||||
"Paste" => Key::Paste,
|
||||
"Redo" => Key::Redo,
|
||||
"Undo" => Key::Undo,
|
||||
"Accept" => Key::Accept,
|
||||
"Again" => Key::Again,
|
||||
"Attn" => Key::Attn,
|
||||
"Cancel" => Key::Cancel,
|
||||
"ContextMenu" => Key::ContextMenu,
|
||||
"Escape" => Key::Escape,
|
||||
"Execute" => Key::Execute,
|
||||
"Find" => Key::Find,
|
||||
"Help" => Key::Help,
|
||||
"Pause" => Key::Pause,
|
||||
"Play" => Key::Play,
|
||||
"Props" => Key::Props,
|
||||
"Select" => Key::Select,
|
||||
"ZoomIn" => Key::ZoomIn,
|
||||
"ZoomOut" => Key::ZoomOut,
|
||||
"BrightnessDown" => Key::BrightnessDown,
|
||||
"BrightnessUp" => Key::BrightnessUp,
|
||||
"Eject" => Key::Eject,
|
||||
"LogOff" => Key::LogOff,
|
||||
"Power" => Key::Power,
|
||||
"PowerOff" => Key::PowerOff,
|
||||
"PrintScreen" => Key::PrintScreen,
|
||||
"Hibernate" => Key::Hibernate,
|
||||
"Standby" => Key::Standby,
|
||||
"WakeUp" => Key::WakeUp,
|
||||
"AllCandidates" => Key::AllCandidates,
|
||||
"Alphanumeric" => Key::Alphanumeric,
|
||||
"CodeInput" => Key::CodeInput,
|
||||
"Compose" => Key::Compose,
|
||||
"Convert" => Key::Convert,
|
||||
"FinalMode" => Key::FinalMode,
|
||||
"GroupFirst" => Key::GroupFirst,
|
||||
"GroupLast" => Key::GroupLast,
|
||||
"GroupNext" => Key::GroupNext,
|
||||
"GroupPrevious" => Key::GroupPrevious,
|
||||
"ModeChange" => Key::ModeChange,
|
||||
"NextCandidate" => Key::NextCandidate,
|
||||
"NonConvert" => Key::NonConvert,
|
||||
"PreviousCandidate" => Key::PreviousCandidate,
|
||||
"Process" => Key::Process,
|
||||
"SingleCandidate" => Key::SingleCandidate,
|
||||
"HangulMode" => Key::HangulMode,
|
||||
"HanjaMode" => Key::HanjaMode,
|
||||
"JunjaMode" => Key::JunjaMode,
|
||||
"Eisu" => Key::Eisu,
|
||||
"Hankaku" => Key::Hankaku,
|
||||
"Hiragana" => Key::Hiragana,
|
||||
"HiraganaKatakana" => Key::HiraganaKatakana,
|
||||
"KanaMode" => Key::KanaMode,
|
||||
"KanjiMode" => Key::KanjiMode,
|
||||
"Katakana" => Key::Katakana,
|
||||
"Romaji" => Key::Romaji,
|
||||
"Zenkaku" => Key::Zenkaku,
|
||||
"ZenkakuHankaku" => Key::ZenkakuHankaku,
|
||||
"Soft1" => Key::Soft1,
|
||||
"Soft2" => Key::Soft2,
|
||||
"Soft3" => Key::Soft3,
|
||||
"Soft4" => Key::Soft4,
|
||||
"ChannelDown" => Key::ChannelDown,
|
||||
"ChannelUp" => Key::ChannelUp,
|
||||
"Close" => Key::Close,
|
||||
"MailForward" => Key::MailForward,
|
||||
"MailReply" => Key::MailReply,
|
||||
"MailSend" => Key::MailSend,
|
||||
"MediaClose" => Key::MediaClose,
|
||||
"MediaFastForward" => Key::MediaFastForward,
|
||||
"MediaPause" => Key::MediaPause,
|
||||
"MediaPlay" => Key::MediaPlay,
|
||||
"MediaPlayPause" => Key::MediaPlayPause,
|
||||
"MediaRecord" => Key::MediaRecord,
|
||||
"MediaRewind" => Key::MediaRewind,
|
||||
"MediaStop" => Key::MediaStop,
|
||||
"MediaTrackNext" => Key::MediaTrackNext,
|
||||
"MediaTrackPrevious" => Key::MediaTrackPrevious,
|
||||
"New" => Key::New,
|
||||
"Open" => Key::Open,
|
||||
"Print" => Key::Print,
|
||||
"Save" => Key::Save,
|
||||
"SpellCheck" => Key::SpellCheck,
|
||||
"Key11" => Key::Key11,
|
||||
"Key12" => Key::Key12,
|
||||
"AudioBalanceLeft" => Key::AudioBalanceLeft,
|
||||
"AudioBalanceRight" => Key::AudioBalanceRight,
|
||||
"AudioBassBoostDown" => Key::AudioBassBoostDown,
|
||||
"AudioBassBoostToggle" => Key::AudioBassBoostToggle,
|
||||
"AudioBassBoostUp" => Key::AudioBassBoostUp,
|
||||
"AudioFaderFront" => Key::AudioFaderFront,
|
||||
"AudioFaderRear" => Key::AudioFaderRear,
|
||||
"AudioSurroundModeNext" => Key::AudioSurroundModeNext,
|
||||
"AudioTrebleDown" => Key::AudioTrebleDown,
|
||||
"AudioTrebleUp" => Key::AudioTrebleUp,
|
||||
"AudioVolumeDown" => Key::AudioVolumeDown,
|
||||
"AudioVolumeUp" => Key::AudioVolumeUp,
|
||||
"AudioVolumeMute" => Key::AudioVolumeMute,
|
||||
"MicrophoneToggle" => Key::MicrophoneToggle,
|
||||
"MicrophoneVolumeDown" => Key::MicrophoneVolumeDown,
|
||||
"MicrophoneVolumeUp" => Key::MicrophoneVolumeUp,
|
||||
"MicrophoneVolumeMute" => Key::MicrophoneVolumeMute,
|
||||
"SpeechCorrectionList" => Key::SpeechCorrectionList,
|
||||
"SpeechInputToggle" => Key::SpeechInputToggle,
|
||||
"LaunchApplication1" => Key::LaunchApplication1,
|
||||
"LaunchApplication2" => Key::LaunchApplication2,
|
||||
"LaunchCalendar" => Key::LaunchCalendar,
|
||||
"LaunchContacts" => Key::LaunchContacts,
|
||||
"LaunchMail" => Key::LaunchMail,
|
||||
"LaunchMediaPlayer" => Key::LaunchMediaPlayer,
|
||||
"LaunchMusicPlayer" => Key::LaunchMusicPlayer,
|
||||
"LaunchPhone" => Key::LaunchPhone,
|
||||
"LaunchScreenSaver" => Key::LaunchScreenSaver,
|
||||
"LaunchSpreadsheet" => Key::LaunchSpreadsheet,
|
||||
"LaunchWebBrowser" => Key::LaunchWebBrowser,
|
||||
"LaunchWebCam" => Key::LaunchWebCam,
|
||||
"LaunchWordProcessor" => Key::LaunchWordProcessor,
|
||||
"BrowserBack" => Key::BrowserBack,
|
||||
"BrowserFavorites" => Key::BrowserFavorites,
|
||||
"BrowserForward" => Key::BrowserForward,
|
||||
"BrowserHome" => Key::BrowserHome,
|
||||
"BrowserRefresh" => Key::BrowserRefresh,
|
||||
"BrowserSearch" => Key::BrowserSearch,
|
||||
"BrowserStop" => Key::BrowserStop,
|
||||
"AppSwitch" => Key::AppSwitch,
|
||||
"Call" => Key::Call,
|
||||
"Camera" => Key::Camera,
|
||||
"CameraFocus" => Key::CameraFocus,
|
||||
"EndCall" => Key::EndCall,
|
||||
"GoBack" => Key::GoBack,
|
||||
"GoHome" => Key::GoHome,
|
||||
"HeadsetHook" => Key::HeadsetHook,
|
||||
"LastNumberRedial" => Key::LastNumberRedial,
|
||||
"Notification" => Key::Notification,
|
||||
"MannerMode" => Key::MannerMode,
|
||||
"VoiceDial" => Key::VoiceDial,
|
||||
"TV" => Key::TV,
|
||||
"TV3DMode" => Key::TV3DMode,
|
||||
"TVAntennaCable" => Key::TVAntennaCable,
|
||||
"TVAudioDescription" => Key::TVAudioDescription,
|
||||
"TVAudioDescriptionMixDown" => Key::TVAudioDescriptionMixDown,
|
||||
"TVAudioDescriptionMixUp" => Key::TVAudioDescriptionMixUp,
|
||||
"TVContentsMenu" => Key::TVContentsMenu,
|
||||
"TVDataService" => Key::TVDataService,
|
||||
"TVInput" => Key::TVInput,
|
||||
"TVInputComponent1" => Key::TVInputComponent1,
|
||||
"TVInputComponent2" => Key::TVInputComponent2,
|
||||
"TVInputComposite1" => Key::TVInputComposite1,
|
||||
"TVInputComposite2" => Key::TVInputComposite2,
|
||||
"TVInputHDMI1" => Key::TVInputHDMI1,
|
||||
"TVInputHDMI2" => Key::TVInputHDMI2,
|
||||
"TVInputHDMI3" => Key::TVInputHDMI3,
|
||||
"TVInputHDMI4" => Key::TVInputHDMI4,
|
||||
"TVInputVGA1" => Key::TVInputVGA1,
|
||||
"TVMediaContext" => Key::TVMediaContext,
|
||||
"TVNetwork" => Key::TVNetwork,
|
||||
"TVNumberEntry" => Key::TVNumberEntry,
|
||||
"TVPower" => Key::TVPower,
|
||||
"TVRadioService" => Key::TVRadioService,
|
||||
"TVSatellite" => Key::TVSatellite,
|
||||
"TVSatelliteBS" => Key::TVSatelliteBS,
|
||||
"TVSatelliteCS" => Key::TVSatelliteCS,
|
||||
"TVSatelliteToggle" => Key::TVSatelliteToggle,
|
||||
"TVTerrestrialAnalog" => Key::TVTerrestrialAnalog,
|
||||
"TVTerrestrialDigital" => Key::TVTerrestrialDigital,
|
||||
"TVTimer" => Key::TVTimer,
|
||||
"AVRInput" => Key::AVRInput,
|
||||
"AVRPower" => Key::AVRPower,
|
||||
"ColorF0Red" => Key::ColorF0Red,
|
||||
"ColorF1Green" => Key::ColorF1Green,
|
||||
"ColorF2Yellow" => Key::ColorF2Yellow,
|
||||
"ColorF3Blue" => Key::ColorF3Blue,
|
||||
"ColorF4Grey" => Key::ColorF4Grey,
|
||||
"ColorF5Brown" => Key::ColorF5Brown,
|
||||
"ClosedCaptionToggle" => Key::ClosedCaptionToggle,
|
||||
"Dimmer" => Key::Dimmer,
|
||||
"DisplaySwap" => Key::DisplaySwap,
|
||||
"DVR" => Key::DVR,
|
||||
"Exit" => Key::Exit,
|
||||
"FavoriteClear0" => Key::FavoriteClear0,
|
||||
"FavoriteClear1" => Key::FavoriteClear1,
|
||||
"FavoriteClear2" => Key::FavoriteClear2,
|
||||
"FavoriteClear3" => Key::FavoriteClear3,
|
||||
"FavoriteRecall0" => Key::FavoriteRecall0,
|
||||
"FavoriteRecall1" => Key::FavoriteRecall1,
|
||||
"FavoriteRecall2" => Key::FavoriteRecall2,
|
||||
"FavoriteRecall3" => Key::FavoriteRecall3,
|
||||
"FavoriteStore0" => Key::FavoriteStore0,
|
||||
"FavoriteStore1" => Key::FavoriteStore1,
|
||||
"FavoriteStore2" => Key::FavoriteStore2,
|
||||
"FavoriteStore3" => Key::FavoriteStore3,
|
||||
"Guide" => Key::Guide,
|
||||
"GuideNextDay" => Key::GuideNextDay,
|
||||
"GuidePreviousDay" => Key::GuidePreviousDay,
|
||||
"Info" => Key::Info,
|
||||
"InstantReplay" => Key::InstantReplay,
|
||||
"Link" => Key::Link,
|
||||
"ListProgram" => Key::ListProgram,
|
||||
"LiveContent" => Key::LiveContent,
|
||||
"Lock" => Key::Lock,
|
||||
"MediaApps" => Key::MediaApps,
|
||||
"MediaAudioTrack" => Key::MediaAudioTrack,
|
||||
"MediaLast" => Key::MediaLast,
|
||||
"MediaSkipBackward" => Key::MediaSkipBackward,
|
||||
"MediaSkipForward" => Key::MediaSkipForward,
|
||||
"MediaStepBackward" => Key::MediaStepBackward,
|
||||
"MediaStepForward" => Key::MediaStepForward,
|
||||
"MediaTopMenu" => Key::MediaTopMenu,
|
||||
"NavigateIn" => Key::NavigateIn,
|
||||
"NavigateNext" => Key::NavigateNext,
|
||||
"NavigateOut" => Key::NavigateOut,
|
||||
"NavigatePrevious" => Key::NavigatePrevious,
|
||||
"NextFavoriteChannel" => Key::NextFavoriteChannel,
|
||||
"NextUserProfile" => Key::NextUserProfile,
|
||||
"OnDemand" => Key::OnDemand,
|
||||
"Pairing" => Key::Pairing,
|
||||
"PinPDown" => Key::PinPDown,
|
||||
"PinPMove" => Key::PinPMove,
|
||||
"PinPToggle" => Key::PinPToggle,
|
||||
"PinPUp" => Key::PinPUp,
|
||||
"PlaySpeedDown" => Key::PlaySpeedDown,
|
||||
"PlaySpeedReset" => Key::PlaySpeedReset,
|
||||
"PlaySpeedUp" => Key::PlaySpeedUp,
|
||||
"RandomToggle" => Key::RandomToggle,
|
||||
"RcLowBattery" => Key::RcLowBattery,
|
||||
"RecordSpeedNext" => Key::RecordSpeedNext,
|
||||
"RfBypass" => Key::RfBypass,
|
||||
"ScanChannelsToggle" => Key::ScanChannelsToggle,
|
||||
"ScreenModeNext" => Key::ScreenModeNext,
|
||||
"Settings" => Key::Settings,
|
||||
"SplitScreenToggle" => Key::SplitScreenToggle,
|
||||
"STBInput" => Key::STBInput,
|
||||
"STBPower" => Key::STBPower,
|
||||
"Subtitle" => Key::Subtitle,
|
||||
"Teletext" => Key::Teletext,
|
||||
"VideoModeNext" => Key::VideoModeNext,
|
||||
"Wink" => Key::Wink,
|
||||
"ZoomToggle" => Key::ZoomToggle,
|
||||
"F1" => Key::F1,
|
||||
"F2" => Key::F2,
|
||||
"F3" => Key::F3,
|
||||
"F4" => Key::F4,
|
||||
"F5" => Key::F5,
|
||||
"F6" => Key::F6,
|
||||
"F7" => Key::F7,
|
||||
"F8" => Key::F8,
|
||||
"F9" => Key::F9,
|
||||
"F10" => Key::F10,
|
||||
"F11" => Key::F11,
|
||||
"F12" => Key::F12,
|
||||
"F13" => Key::F13,
|
||||
"F14" => Key::F14,
|
||||
"F15" => Key::F15,
|
||||
"F16" => Key::F16,
|
||||
"F17" => Key::F17,
|
||||
"F18" => Key::F18,
|
||||
"F19" => Key::F19,
|
||||
"F20" => Key::F20,
|
||||
"F21" => Key::F21,
|
||||
"F22" => Key::F22,
|
||||
"F23" => Key::F23,
|
||||
"F24" => Key::F24,
|
||||
"F25" => Key::F25,
|
||||
"F26" => Key::F26,
|
||||
"F27" => Key::F27,
|
||||
"F28" => Key::F28,
|
||||
"F29" => Key::F29,
|
||||
"F30" => Key::F30,
|
||||
"F31" => Key::F31,
|
||||
"F32" => Key::F32,
|
||||
"F33" => Key::F33,
|
||||
"F34" => Key::F34,
|
||||
"F35" => Key::F35,
|
||||
string => Key::Character(SmolStr::new(string)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyCode {
|
||||
pub fn from_key_code_attribute_value(kcav: &str) -> Self {
|
||||
match kcav {
|
||||
"Backquote" => KeyCode::Backquote,
|
||||
"Backslash" => KeyCode::Backslash,
|
||||
"BracketLeft" => KeyCode::BracketLeft,
|
||||
"BracketRight" => KeyCode::BracketRight,
|
||||
"Comma" => KeyCode::Comma,
|
||||
"Digit0" => KeyCode::Digit0,
|
||||
"Digit1" => KeyCode::Digit1,
|
||||
"Digit2" => KeyCode::Digit2,
|
||||
"Digit3" => KeyCode::Digit3,
|
||||
"Digit4" => KeyCode::Digit4,
|
||||
"Digit5" => KeyCode::Digit5,
|
||||
"Digit6" => KeyCode::Digit6,
|
||||
"Digit7" => KeyCode::Digit7,
|
||||
"Digit8" => KeyCode::Digit8,
|
||||
"Digit9" => KeyCode::Digit9,
|
||||
"Equal" => KeyCode::Equal,
|
||||
"IntlBackslash" => KeyCode::IntlBackslash,
|
||||
"IntlRo" => KeyCode::IntlRo,
|
||||
"IntlYen" => KeyCode::IntlYen,
|
||||
"KeyA" => KeyCode::KeyA,
|
||||
"KeyB" => KeyCode::KeyB,
|
||||
"KeyC" => KeyCode::KeyC,
|
||||
"KeyD" => KeyCode::KeyD,
|
||||
"KeyE" => KeyCode::KeyE,
|
||||
"KeyF" => KeyCode::KeyF,
|
||||
"KeyG" => KeyCode::KeyG,
|
||||
"KeyH" => KeyCode::KeyH,
|
||||
"KeyI" => KeyCode::KeyI,
|
||||
"KeyJ" => KeyCode::KeyJ,
|
||||
"KeyK" => KeyCode::KeyK,
|
||||
"KeyL" => KeyCode::KeyL,
|
||||
"KeyM" => KeyCode::KeyM,
|
||||
"KeyN" => KeyCode::KeyN,
|
||||
"KeyO" => KeyCode::KeyO,
|
||||
"KeyP" => KeyCode::KeyP,
|
||||
"KeyQ" => KeyCode::KeyQ,
|
||||
"KeyR" => KeyCode::KeyR,
|
||||
"KeyS" => KeyCode::KeyS,
|
||||
"KeyT" => KeyCode::KeyT,
|
||||
"KeyU" => KeyCode::KeyU,
|
||||
"KeyV" => KeyCode::KeyV,
|
||||
"KeyW" => KeyCode::KeyW,
|
||||
"KeyX" => KeyCode::KeyX,
|
||||
"KeyY" => KeyCode::KeyY,
|
||||
"KeyZ" => KeyCode::KeyZ,
|
||||
"Minus" => KeyCode::Minus,
|
||||
"Period" => KeyCode::Period,
|
||||
"Quote" => KeyCode::Quote,
|
||||
"Semicolon" => KeyCode::Semicolon,
|
||||
"Slash" => KeyCode::Slash,
|
||||
"AltLeft" => KeyCode::AltLeft,
|
||||
"AltRight" => KeyCode::AltRight,
|
||||
"Backspace" => KeyCode::Backspace,
|
||||
"CapsLock" => KeyCode::CapsLock,
|
||||
"ContextMenu" => KeyCode::ContextMenu,
|
||||
"ControlLeft" => KeyCode::ControlLeft,
|
||||
"ControlRight" => KeyCode::ControlRight,
|
||||
"Enter" => KeyCode::Enter,
|
||||
"MetaLeft" => KeyCode::SuperLeft,
|
||||
"MetaRight" => KeyCode::SuperRight,
|
||||
"ShiftLeft" => KeyCode::ShiftLeft,
|
||||
"ShiftRight" => KeyCode::ShiftRight,
|
||||
"Space" => KeyCode::Space,
|
||||
"Tab" => KeyCode::Tab,
|
||||
"Convert" => KeyCode::Convert,
|
||||
"KanaMode" => KeyCode::KanaMode,
|
||||
"Lang1" => KeyCode::Lang1,
|
||||
"Lang2" => KeyCode::Lang2,
|
||||
"Lang3" => KeyCode::Lang3,
|
||||
"Lang4" => KeyCode::Lang4,
|
||||
"Lang5" => KeyCode::Lang5,
|
||||
"NonConvert" => KeyCode::NonConvert,
|
||||
"Delete" => KeyCode::Delete,
|
||||
"End" => KeyCode::End,
|
||||
"Help" => KeyCode::Help,
|
||||
"Home" => KeyCode::Home,
|
||||
"Insert" => KeyCode::Insert,
|
||||
"PageDown" => KeyCode::PageDown,
|
||||
"PageUp" => KeyCode::PageUp,
|
||||
"ArrowDown" => KeyCode::ArrowDown,
|
||||
"ArrowLeft" => KeyCode::ArrowLeft,
|
||||
"ArrowRight" => KeyCode::ArrowRight,
|
||||
"ArrowUp" => KeyCode::ArrowUp,
|
||||
"NumLock" => KeyCode::NumLock,
|
||||
"Numpad0" => KeyCode::Numpad0,
|
||||
"Numpad1" => KeyCode::Numpad1,
|
||||
"Numpad2" => KeyCode::Numpad2,
|
||||
"Numpad3" => KeyCode::Numpad3,
|
||||
"Numpad4" => KeyCode::Numpad4,
|
||||
"Numpad5" => KeyCode::Numpad5,
|
||||
"Numpad6" => KeyCode::Numpad6,
|
||||
"Numpad7" => KeyCode::Numpad7,
|
||||
"Numpad8" => KeyCode::Numpad8,
|
||||
"Numpad9" => KeyCode::Numpad9,
|
||||
"NumpadAdd" => KeyCode::NumpadAdd,
|
||||
"NumpadBackspace" => KeyCode::NumpadBackspace,
|
||||
"NumpadClear" => KeyCode::NumpadClear,
|
||||
"NumpadClearEntry" => KeyCode::NumpadClearEntry,
|
||||
"NumpadComma" => KeyCode::NumpadComma,
|
||||
"NumpadDecimal" => KeyCode::NumpadDecimal,
|
||||
"NumpadDivide" => KeyCode::NumpadDivide,
|
||||
"NumpadEnter" => KeyCode::NumpadEnter,
|
||||
"NumpadEqual" => KeyCode::NumpadEqual,
|
||||
"NumpadHash" => KeyCode::NumpadHash,
|
||||
"NumpadMemoryAdd" => KeyCode::NumpadMemoryAdd,
|
||||
"NumpadMemoryClear" => KeyCode::NumpadMemoryClear,
|
||||
"NumpadMemoryRecall" => KeyCode::NumpadMemoryRecall,
|
||||
"NumpadMemoryStore" => KeyCode::NumpadMemoryStore,
|
||||
"NumpadMemorySubtract" => KeyCode::NumpadMemorySubtract,
|
||||
"NumpadMultiply" => KeyCode::NumpadMultiply,
|
||||
"NumpadParenLeft" => KeyCode::NumpadParenLeft,
|
||||
"NumpadParenRight" => KeyCode::NumpadParenRight,
|
||||
"NumpadStar" => KeyCode::NumpadStar,
|
||||
"NumpadSubtract" => KeyCode::NumpadSubtract,
|
||||
"Escape" => KeyCode::Escape,
|
||||
"Fn" => KeyCode::Fn,
|
||||
"FnLock" => KeyCode::FnLock,
|
||||
"PrintScreen" => KeyCode::PrintScreen,
|
||||
"ScrollLock" => KeyCode::ScrollLock,
|
||||
"Pause" => KeyCode::Pause,
|
||||
"BrowserBack" => KeyCode::BrowserBack,
|
||||
"BrowserFavorites" => KeyCode::BrowserFavorites,
|
||||
"BrowserForward" => KeyCode::BrowserForward,
|
||||
"BrowserHome" => KeyCode::BrowserHome,
|
||||
"BrowserRefresh" => KeyCode::BrowserRefresh,
|
||||
"BrowserSearch" => KeyCode::BrowserSearch,
|
||||
"BrowserStop" => KeyCode::BrowserStop,
|
||||
"Eject" => KeyCode::Eject,
|
||||
"LaunchApp1" => KeyCode::LaunchApp1,
|
||||
"LaunchApp2" => KeyCode::LaunchApp2,
|
||||
"LaunchMail" => KeyCode::LaunchMail,
|
||||
"MediaPlayPause" => KeyCode::MediaPlayPause,
|
||||
"MediaSelect" => KeyCode::MediaSelect,
|
||||
"MediaStop" => KeyCode::MediaStop,
|
||||
"MediaTrackNext" => KeyCode::MediaTrackNext,
|
||||
"MediaTrackPrevious" => KeyCode::MediaTrackPrevious,
|
||||
"Power" => KeyCode::Power,
|
||||
"Sleep" => KeyCode::Sleep,
|
||||
"AudioVolumeDown" => KeyCode::AudioVolumeDown,
|
||||
"AudioVolumeMute" => KeyCode::AudioVolumeMute,
|
||||
"AudioVolumeUp" => KeyCode::AudioVolumeUp,
|
||||
"WakeUp" => KeyCode::WakeUp,
|
||||
"Hyper" => KeyCode::Hyper,
|
||||
"Turbo" => KeyCode::Turbo,
|
||||
"Abort" => KeyCode::Abort,
|
||||
"Resume" => KeyCode::Resume,
|
||||
"Suspend" => KeyCode::Suspend,
|
||||
"Again" => KeyCode::Again,
|
||||
"Copy" => KeyCode::Copy,
|
||||
"Cut" => KeyCode::Cut,
|
||||
"Find" => KeyCode::Find,
|
||||
"Open" => KeyCode::Open,
|
||||
"Paste" => KeyCode::Paste,
|
||||
"Props" => KeyCode::Props,
|
||||
"Select" => KeyCode::Select,
|
||||
"Undo" => KeyCode::Undo,
|
||||
"Hiragana" => KeyCode::Hiragana,
|
||||
"Katakana" => KeyCode::Katakana,
|
||||
"F1" => KeyCode::F1,
|
||||
"F2" => KeyCode::F2,
|
||||
"F3" => KeyCode::F3,
|
||||
"F4" => KeyCode::F4,
|
||||
"F5" => KeyCode::F5,
|
||||
"F6" => KeyCode::F6,
|
||||
"F7" => KeyCode::F7,
|
||||
"F8" => KeyCode::F8,
|
||||
"F9" => KeyCode::F9,
|
||||
"F10" => KeyCode::F10,
|
||||
"F11" => KeyCode::F11,
|
||||
"F12" => KeyCode::F12,
|
||||
"F13" => KeyCode::F13,
|
||||
"F14" => KeyCode::F14,
|
||||
"F15" => KeyCode::F15,
|
||||
"F16" => KeyCode::F16,
|
||||
"F17" => KeyCode::F17,
|
||||
"F18" => KeyCode::F18,
|
||||
"F19" => KeyCode::F19,
|
||||
"F20" => KeyCode::F20,
|
||||
"F21" => KeyCode::F21,
|
||||
"F22" => KeyCode::F22,
|
||||
"F23" => KeyCode::F23,
|
||||
"F24" => KeyCode::F24,
|
||||
"F25" => KeyCode::F25,
|
||||
"F26" => KeyCode::F26,
|
||||
"F27" => KeyCode::F27,
|
||||
"F28" => KeyCode::F28,
|
||||
"F29" => KeyCode::F29,
|
||||
"F30" => KeyCode::F30,
|
||||
"F31" => KeyCode::F31,
|
||||
"F32" => KeyCode::F32,
|
||||
"F33" => KeyCode::F33,
|
||||
"F34" => KeyCode::F34,
|
||||
"F35" => KeyCode::F35,
|
||||
_ => KeyCode::Unidentified(NativeKeyCode::Unidentified),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
mod device;
|
||||
mod error;
|
||||
mod event_loop;
|
||||
mod keyboard;
|
||||
mod monitor;
|
||||
mod window;
|
||||
|
||||
|
|
@ -34,6 +35,7 @@ pub(crate) use self::event_loop::{
|
|||
pub use self::monitor::{MonitorHandle, VideoMode};
|
||||
pub use self::window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId};
|
||||
|
||||
pub(crate) use self::keyboard::KeyEventExtra;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(self) use crate::platform_impl::Fullscreen;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ use super::event_handle::EventListenerHandle;
|
|||
use super::media_query_handle::MediaQueryListHandle;
|
||||
use crate::dpi::{LogicalPosition, PhysicalPosition, PhysicalSize};
|
||||
use crate::error::OsError as RootOE;
|
||||
use crate::event::{
|
||||
Force, ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode,
|
||||
};
|
||||
use crate::event::{Force, MouseButton, MouseScrollDelta};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState};
|
||||
use crate::platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use smol_str::SmolStr;
|
||||
use wasm_bindgen::{closure::Closure, JsCast};
|
||||
use web_sys::{
|
||||
AddEventListenerOptions, Event, FocusEvent, HtmlCanvasElement, KeyboardEvent,
|
||||
|
|
@ -29,7 +29,6 @@ pub struct Canvas {
|
|||
on_blur: Option<EventListenerHandle<dyn FnMut(FocusEvent)>>,
|
||||
on_keyboard_release: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
|
||||
on_keyboard_press: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
|
||||
on_received_character: Option<EventListenerHandle<dyn FnMut(KeyboardEvent)>>,
|
||||
on_mouse_wheel: Option<EventListenerHandle<dyn FnMut(WheelEvent)>>,
|
||||
on_fullscreen_change: Option<EventListenerHandle<dyn FnMut(Event)>>,
|
||||
on_dark_mode: Option<MediaQueryListHandle>,
|
||||
|
|
@ -89,7 +88,6 @@ impl Canvas {
|
|||
on_focus: None,
|
||||
on_keyboard_release: None,
|
||||
on_keyboard_press: None,
|
||||
on_received_character: None,
|
||||
on_mouse_wheel: None,
|
||||
on_fullscreen_change: None,
|
||||
on_dark_mode: None,
|
||||
|
|
@ -174,7 +172,7 @@ impl Canvas {
|
|||
|
||||
pub fn on_keyboard_release<F>(&mut self, mut handler: F, prevent_default: bool)
|
||||
where
|
||||
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||
F: 'static + FnMut(KeyCode, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
|
||||
{
|
||||
self.on_keyboard_release = Some(self.common.add_user_event(
|
||||
"keyup",
|
||||
|
|
@ -182,11 +180,15 @@ impl Canvas {
|
|||
if prevent_default {
|
||||
event.prevent_default();
|
||||
}
|
||||
|
||||
let key = event::key(&event);
|
||||
let modifiers = event::keyboard_modifiers(&event);
|
||||
handler(
|
||||
event::scan_code(&event),
|
||||
event::virtual_key_code(&event),
|
||||
event::keyboard_modifiers(&event),
|
||||
event::key_code(&event),
|
||||
key,
|
||||
event::key_text(&event),
|
||||
event::key_location(&event),
|
||||
event.repeat(),
|
||||
modifiers,
|
||||
);
|
||||
},
|
||||
));
|
||||
|
|
@ -194,59 +196,31 @@ impl Canvas {
|
|||
|
||||
pub fn on_keyboard_press<F>(&mut self, mut handler: F, prevent_default: bool)
|
||||
where
|
||||
F: 'static + FnMut(ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||
F: 'static + FnMut(KeyCode, Key, Option<SmolStr>, KeyLocation, bool, ModifiersState),
|
||||
{
|
||||
self.on_keyboard_press = Some(self.common.add_user_event(
|
||||
"keydown",
|
||||
move |event: KeyboardEvent| {
|
||||
// event.prevent_default() would suppress subsequent on_received_character() calls. That
|
||||
// suppression is correct for key sequences like Tab/Shift-Tab, Ctrl+R, PgUp/Down to
|
||||
// scroll, etc. We should not do it for key sequences that result in meaningful character
|
||||
// input though.
|
||||
if prevent_default {
|
||||
let event_key = &event.key();
|
||||
let is_key_string = event_key.len() == 1 || !event_key.is_ascii();
|
||||
let is_shortcut_modifiers =
|
||||
(event.ctrl_key() || event.alt_key()) && !event.get_modifier_state("AltGr");
|
||||
if !is_key_string || is_shortcut_modifiers {
|
||||
event.prevent_default();
|
||||
}
|
||||
}
|
||||
|
||||
handler(
|
||||
event::scan_code(&event),
|
||||
event::virtual_key_code(&event),
|
||||
event::keyboard_modifiers(&event),
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
pub fn on_received_character<F>(&mut self, mut handler: F, prevent_default: bool)
|
||||
where
|
||||
F: 'static + FnMut(char),
|
||||
{
|
||||
// TODO: Use `beforeinput`.
|
||||
//
|
||||
// The `keypress` event is deprecated, but there does not seem to be a
|
||||
// viable/compatible alternative as of now. `beforeinput` is still widely
|
||||
// unsupported.
|
||||
self.on_received_character = Some(self.common.add_user_event(
|
||||
"keypress",
|
||||
move |event: KeyboardEvent| {
|
||||
// Suppress further handling to stop keys like the space key from scrolling the page.
|
||||
if prevent_default {
|
||||
event.prevent_default();
|
||||
}
|
||||
|
||||
handler(event::codepoint(&event));
|
||||
let key = event::key(&event);
|
||||
let modifiers = event::keyboard_modifiers(&event);
|
||||
handler(
|
||||
event::key_code(&event),
|
||||
key,
|
||||
event::key_text(&event),
|
||||
event::key_location(&event),
|
||||
event.repeat(),
|
||||
modifiers,
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
pub fn on_cursor_leave<F>(&mut self, handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32),
|
||||
F: 'static + FnMut(i32, ModifiersState),
|
||||
{
|
||||
match &mut self.mouse_state {
|
||||
MouseState::HasPointerEvent(h) => h.on_cursor_leave(&self.common, handler),
|
||||
|
|
@ -256,7 +230,7 @@ impl Canvas {
|
|||
|
||||
pub fn on_cursor_enter<F>(&mut self, handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32),
|
||||
F: 'static + FnMut(i32, ModifiersState),
|
||||
{
|
||||
match &mut self.mouse_state {
|
||||
MouseState::HasPointerEvent(h) => h.on_cursor_enter(&self.common, handler),
|
||||
|
|
@ -326,7 +300,8 @@ impl Canvas {
|
|||
}
|
||||
|
||||
if let Some(delta) = event::mouse_scroll_delta(&event) {
|
||||
handler(0, delta, event::mouse_modifiers(&event));
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(0, delta, modifiers);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
@ -366,7 +341,6 @@ impl Canvas {
|
|||
self.on_blur = None;
|
||||
self.on_keyboard_release = None;
|
||||
self.on_keyboard_press = None;
|
||||
self.on_received_character = None;
|
||||
self.on_mouse_wheel = None;
|
||||
self.on_fullscreen_change = None;
|
||||
self.on_dark_mode = None;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
use super::event;
|
||||
use super::EventListenerHandle;
|
||||
use crate::dpi::PhysicalPosition;
|
||||
use crate::event::{ModifiersState, MouseButton};
|
||||
use crate::event::MouseButton;
|
||||
use crate::keyboard::ModifiersState;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use web_sys::{EventTarget, MouseEvent};
|
||||
|
||||
type MouseLeaveHandler = Rc<RefCell<Option<Box<dyn FnMut(i32)>>>>;
|
||||
type MouseLeaveHandler = Rc<RefCell<Option<Box<dyn FnMut(i32, ModifiersState)>>>>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(super) struct MouseHandler {
|
||||
|
|
@ -42,35 +43,43 @@ impl MouseHandler {
|
|||
}
|
||||
pub fn on_cursor_leave<F>(&mut self, canvas_common: &super::Common, handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32),
|
||||
F: 'static + FnMut(i32, ModifiersState),
|
||||
{
|
||||
*self.on_mouse_leave_handler.borrow_mut() = Some(Box::new(handler));
|
||||
let on_mouse_leave_handler = self.on_mouse_leave_handler.clone();
|
||||
let mouse_capture_state = self.mouse_capture_state.clone();
|
||||
self.on_mouse_leave = Some(canvas_common.add_event("mouseout", move |_: MouseEvent| {
|
||||
// If the mouse is being captured, it is always considered
|
||||
// to be "within" the the canvas, until the capture has been
|
||||
// released, therefore we don't send cursor leave events.
|
||||
if *mouse_capture_state.borrow() != MouseCaptureState::Captured {
|
||||
if let Some(handler) = on_mouse_leave_handler.borrow_mut().as_mut() {
|
||||
handler(0);
|
||||
self.on_mouse_leave = Some(canvas_common.add_event(
|
||||
"mouseout",
|
||||
move |event: MouseEvent| {
|
||||
// If the mouse is being captured, it is always considered
|
||||
// to be "within" the the canvas, until the capture has been
|
||||
// released, therefore we don't send cursor leave events.
|
||||
if *mouse_capture_state.borrow() != MouseCaptureState::Captured {
|
||||
if let Some(handler) = on_mouse_leave_handler.borrow_mut().as_mut() {
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(0, modifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
pub fn on_cursor_enter<F>(&mut self, canvas_common: &super::Common, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32),
|
||||
F: 'static + FnMut(i32, ModifiersState),
|
||||
{
|
||||
let mouse_capture_state = self.mouse_capture_state.clone();
|
||||
self.on_mouse_enter = Some(canvas_common.add_event("mouseover", move |_: MouseEvent| {
|
||||
// We don't send cursor leave events when the mouse is being
|
||||
// captured, therefore we do the same with cursor enter events.
|
||||
if *mouse_capture_state.borrow() != MouseCaptureState::Captured {
|
||||
handler(0);
|
||||
}
|
||||
}));
|
||||
self.on_mouse_enter = Some(canvas_common.add_event(
|
||||
"mouseover",
|
||||
move |event: MouseEvent| {
|
||||
// We don't send cursor leave events when the mouse is being
|
||||
// captured, therefore we do the same with cursor enter events.
|
||||
if *mouse_capture_state.borrow() != MouseCaptureState::Captured {
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(0, modifiers);
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
pub fn on_mouse_release<F>(&mut self, canvas_common: &super::Common, mut handler: F)
|
||||
|
|
@ -99,11 +108,8 @@ impl MouseHandler {
|
|||
MouseCaptureState::Captured => {}
|
||||
}
|
||||
event.stop_propagation();
|
||||
handler(
|
||||
0,
|
||||
event::mouse_button(&event),
|
||||
event::mouse_modifiers(&event),
|
||||
);
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(0, event::mouse_button(&event), modifiers);
|
||||
if event
|
||||
.target()
|
||||
.map_or(false, |target| target != EventTarget::from(canvas))
|
||||
|
|
@ -112,7 +118,8 @@ impl MouseHandler {
|
|||
// cursor is being captured, we instead send it after
|
||||
// the capture has been released.
|
||||
if let Some(handler) = on_mouse_leave_handler.borrow_mut().as_mut() {
|
||||
handler(0);
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(0, modifiers);
|
||||
}
|
||||
}
|
||||
if event.buttons() == 0 {
|
||||
|
|
@ -151,11 +158,12 @@ impl MouseHandler {
|
|||
}
|
||||
*mouse_capture_state = MouseCaptureState::Captured;
|
||||
event.stop_propagation();
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(
|
||||
0,
|
||||
event::mouse_position(&event).to_physical(super::super::scale_factor()),
|
||||
event::mouse_button(&event),
|
||||
event::mouse_modifiers(&event),
|
||||
modifiers,
|
||||
);
|
||||
},
|
||||
));
|
||||
|
|
@ -194,11 +202,12 @@ impl MouseHandler {
|
|||
event::mouse_position_by_client(&event, &canvas)
|
||||
};
|
||||
let mouse_delta = event::mouse_delta(&event);
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(
|
||||
0,
|
||||
mouse_pos.to_physical(super::super::scale_factor()),
|
||||
mouse_delta.to_physical(super::super::scale_factor()),
|
||||
event::mouse_modifiers(&event),
|
||||
modifiers,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use super::event;
|
||||
use super::EventListenerHandle;
|
||||
use crate::dpi::PhysicalPosition;
|
||||
use crate::event::Force;
|
||||
use crate::event::{ModifiersState, MouseButton};
|
||||
use crate::event::{Force, MouseButton};
|
||||
use crate::keyboard::ModifiersState;
|
||||
|
||||
use web_sys::PointerEvent;
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ impl PointerHandler {
|
|||
|
||||
pub fn on_cursor_leave<F>(&mut self, canvas_common: &super::Common, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32),
|
||||
F: 'static + FnMut(i32, ModifiersState),
|
||||
{
|
||||
self.on_cursor_leave = Some(canvas_common.add_event(
|
||||
"pointerout",
|
||||
|
|
@ -42,14 +42,15 @@ impl PointerHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
handler(event.pointer_id());
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(event.pointer_id(), modifiers);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
pub fn on_cursor_enter<F>(&mut self, canvas_common: &super::Common, mut handler: F)
|
||||
where
|
||||
F: 'static + FnMut(i32),
|
||||
F: 'static + FnMut(i32, ModifiersState),
|
||||
{
|
||||
self.on_cursor_enter = Some(canvas_common.add_event(
|
||||
"pointerover",
|
||||
|
|
@ -61,7 +62,8 @@ impl PointerHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
handler(event.pointer_id());
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
handler(event.pointer_id(), modifiers);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
@ -87,11 +89,8 @@ impl PointerHandler {
|
|||
Force::Normalized(event.pressure() as f64),
|
||||
);
|
||||
} else {
|
||||
mouse_handler(
|
||||
event.pointer_id(),
|
||||
event::mouse_button(&event),
|
||||
event::mouse_modifiers(&event),
|
||||
);
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
mouse_handler(event.pointer_id(), event::mouse_button(&event), modifiers);
|
||||
}
|
||||
},
|
||||
));
|
||||
|
|
@ -118,11 +117,12 @@ impl PointerHandler {
|
|||
Force::Normalized(event.pressure() as f64),
|
||||
);
|
||||
} else {
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
mouse_handler(
|
||||
event.pointer_id(),
|
||||
event::mouse_position(&event).to_physical(super::super::scale_factor()),
|
||||
event::mouse_button(&event),
|
||||
event::mouse_modifiers(&event),
|
||||
modifiers,
|
||||
);
|
||||
|
||||
// Error is swallowed here since the error would occur every time the mouse is
|
||||
|
|
@ -160,11 +160,12 @@ impl PointerHandler {
|
|||
Force::Normalized(event.pressure() as f64),
|
||||
);
|
||||
} else {
|
||||
let modifiers = event::mouse_modifiers(&event);
|
||||
mouse_handler(
|
||||
event.pointer_id(),
|
||||
event::mouse_position(&event).to_physical(super::super::scale_factor()),
|
||||
event::mouse_delta(&event).to_physical(super::super::scale_factor()),
|
||||
event::mouse_modifiers(&event),
|
||||
modifiers,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use crate::dpi::LogicalPosition;
|
||||
use crate::event::{ModifiersState, MouseButton, MouseScrollDelta, ScanCode, VirtualKeyCode};
|
||||
use crate::event::{MouseButton, MouseScrollDelta};
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, ModifiersState};
|
||||
|
||||
use smol_str::SmolStr;
|
||||
use std::convert::TryInto;
|
||||
use web_sys::{HtmlCanvasElement, KeyboardEvent, MouseEvent, PointerEvent, WheelEvent};
|
||||
|
||||
|
|
@ -13,15 +15,6 @@ pub fn mouse_button(event: &MouseEvent) -> MouseButton {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mouse_modifiers(event: &MouseEvent) -> ModifiersState {
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, event.shift_key());
|
||||
m.set(ModifiersState::CTRL, event.ctrl_key());
|
||||
m.set(ModifiersState::ALT, event.alt_key());
|
||||
m.set(ModifiersState::LOGO, event.meta_key());
|
||||
m
|
||||
}
|
||||
|
||||
pub fn mouse_position(event: &MouseEvent) -> LogicalPosition<f64> {
|
||||
LogicalPosition {
|
||||
x: event.offset_x() as f64,
|
||||
|
|
@ -61,190 +54,77 @@ pub fn mouse_scroll_delta(event: &WheelEvent) -> Option<MouseScrollDelta> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn scan_code(event: &KeyboardEvent) -> ScanCode {
|
||||
match event.key_code() {
|
||||
0 => event.char_code(),
|
||||
i => i,
|
||||
pub fn key_code(event: &KeyboardEvent) -> KeyCode {
|
||||
let code = event.code();
|
||||
KeyCode::from_key_code_attribute_value(&code)
|
||||
}
|
||||
|
||||
pub fn key(event: &KeyboardEvent) -> Key {
|
||||
Key::from_key_attribute_value(&event.key())
|
||||
}
|
||||
|
||||
pub fn key_text(event: &KeyboardEvent) -> Option<SmolStr> {
|
||||
let key = event.key();
|
||||
let key = Key::from_key_attribute_value(&key);
|
||||
match &key {
|
||||
Key::Character(text) => Some(text.clone()),
|
||||
Key::Tab => Some(SmolStr::new("\t")),
|
||||
Key::Enter => Some(SmolStr::new("\r")),
|
||||
Key::Space => Some(SmolStr::new(" ")),
|
||||
_ => None,
|
||||
}
|
||||
.map(SmolStr::new)
|
||||
}
|
||||
|
||||
pub fn key_location(event: &KeyboardEvent) -> KeyLocation {
|
||||
match event.location() {
|
||||
KeyboardEvent::DOM_KEY_LOCATION_LEFT => KeyLocation::Left,
|
||||
KeyboardEvent::DOM_KEY_LOCATION_RIGHT => KeyLocation::Right,
|
||||
KeyboardEvent::DOM_KEY_LOCATION_NUMPAD => KeyLocation::Numpad,
|
||||
KeyboardEvent::DOM_KEY_LOCATION_STANDARD => KeyLocation::Standard,
|
||||
location => {
|
||||
warn!("Unexpected key location: {location}");
|
||||
KeyLocation::Standard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn virtual_key_code(event: &KeyboardEvent) -> Option<VirtualKeyCode> {
|
||||
Some(match &event.code()[..] {
|
||||
"Digit1" => VirtualKeyCode::Key1,
|
||||
"Digit2" => VirtualKeyCode::Key2,
|
||||
"Digit3" => VirtualKeyCode::Key3,
|
||||
"Digit4" => VirtualKeyCode::Key4,
|
||||
"Digit5" => VirtualKeyCode::Key5,
|
||||
"Digit6" => VirtualKeyCode::Key6,
|
||||
"Digit7" => VirtualKeyCode::Key7,
|
||||
"Digit8" => VirtualKeyCode::Key8,
|
||||
"Digit9" => VirtualKeyCode::Key9,
|
||||
"Digit0" => VirtualKeyCode::Key0,
|
||||
"KeyA" => VirtualKeyCode::A,
|
||||
"KeyB" => VirtualKeyCode::B,
|
||||
"KeyC" => VirtualKeyCode::C,
|
||||
"KeyD" => VirtualKeyCode::D,
|
||||
"KeyE" => VirtualKeyCode::E,
|
||||
"KeyF" => VirtualKeyCode::F,
|
||||
"KeyG" => VirtualKeyCode::G,
|
||||
"KeyH" => VirtualKeyCode::H,
|
||||
"KeyI" => VirtualKeyCode::I,
|
||||
"KeyJ" => VirtualKeyCode::J,
|
||||
"KeyK" => VirtualKeyCode::K,
|
||||
"KeyL" => VirtualKeyCode::L,
|
||||
"KeyM" => VirtualKeyCode::M,
|
||||
"KeyN" => VirtualKeyCode::N,
|
||||
"KeyO" => VirtualKeyCode::O,
|
||||
"KeyP" => VirtualKeyCode::P,
|
||||
"KeyQ" => VirtualKeyCode::Q,
|
||||
"KeyR" => VirtualKeyCode::R,
|
||||
"KeyS" => VirtualKeyCode::S,
|
||||
"KeyT" => VirtualKeyCode::T,
|
||||
"KeyU" => VirtualKeyCode::U,
|
||||
"KeyV" => VirtualKeyCode::V,
|
||||
"KeyW" => VirtualKeyCode::W,
|
||||
"KeyX" => VirtualKeyCode::X,
|
||||
"KeyY" => VirtualKeyCode::Y,
|
||||
"KeyZ" => VirtualKeyCode::Z,
|
||||
"Escape" => VirtualKeyCode::Escape,
|
||||
"F1" => VirtualKeyCode::F1,
|
||||
"F2" => VirtualKeyCode::F2,
|
||||
"F3" => VirtualKeyCode::F3,
|
||||
"F4" => VirtualKeyCode::F4,
|
||||
"F5" => VirtualKeyCode::F5,
|
||||
"F6" => VirtualKeyCode::F6,
|
||||
"F7" => VirtualKeyCode::F7,
|
||||
"F8" => VirtualKeyCode::F8,
|
||||
"F9" => VirtualKeyCode::F9,
|
||||
"F10" => VirtualKeyCode::F10,
|
||||
"F11" => VirtualKeyCode::F11,
|
||||
"F12" => VirtualKeyCode::F12,
|
||||
"F13" => VirtualKeyCode::F13,
|
||||
"F14" => VirtualKeyCode::F14,
|
||||
"F15" => VirtualKeyCode::F15,
|
||||
"F16" => VirtualKeyCode::F16,
|
||||
"F17" => VirtualKeyCode::F17,
|
||||
"F18" => VirtualKeyCode::F18,
|
||||
"F19" => VirtualKeyCode::F19,
|
||||
"F20" => VirtualKeyCode::F20,
|
||||
"F21" => VirtualKeyCode::F21,
|
||||
"F22" => VirtualKeyCode::F22,
|
||||
"F23" => VirtualKeyCode::F23,
|
||||
"F24" => VirtualKeyCode::F24,
|
||||
"PrintScreen" => VirtualKeyCode::Snapshot,
|
||||
"ScrollLock" => VirtualKeyCode::Scroll,
|
||||
"Pause" => VirtualKeyCode::Pause,
|
||||
"Insert" => VirtualKeyCode::Insert,
|
||||
"Home" => VirtualKeyCode::Home,
|
||||
"Delete" => VirtualKeyCode::Delete,
|
||||
"End" => VirtualKeyCode::End,
|
||||
"PageDown" => VirtualKeyCode::PageDown,
|
||||
"PageUp" => VirtualKeyCode::PageUp,
|
||||
"ArrowLeft" => VirtualKeyCode::Left,
|
||||
"ArrowUp" => VirtualKeyCode::Up,
|
||||
"ArrowRight" => VirtualKeyCode::Right,
|
||||
"ArrowDown" => VirtualKeyCode::Down,
|
||||
"Backspace" => VirtualKeyCode::Back,
|
||||
"Enter" => VirtualKeyCode::Return,
|
||||
"Space" => VirtualKeyCode::Space,
|
||||
"Compose" => VirtualKeyCode::Compose,
|
||||
"Caret" => VirtualKeyCode::Caret,
|
||||
"NumLock" => VirtualKeyCode::Numlock,
|
||||
"Numpad0" => VirtualKeyCode::Numpad0,
|
||||
"Numpad1" => VirtualKeyCode::Numpad1,
|
||||
"Numpad2" => VirtualKeyCode::Numpad2,
|
||||
"Numpad3" => VirtualKeyCode::Numpad3,
|
||||
"Numpad4" => VirtualKeyCode::Numpad4,
|
||||
"Numpad5" => VirtualKeyCode::Numpad5,
|
||||
"Numpad6" => VirtualKeyCode::Numpad6,
|
||||
"Numpad7" => VirtualKeyCode::Numpad7,
|
||||
"Numpad8" => VirtualKeyCode::Numpad8,
|
||||
"Numpad9" => VirtualKeyCode::Numpad9,
|
||||
"AbntC1" => VirtualKeyCode::AbntC1,
|
||||
"AbntC2" => VirtualKeyCode::AbntC2,
|
||||
"NumpadAdd" => VirtualKeyCode::NumpadAdd,
|
||||
"Quote" => VirtualKeyCode::Apostrophe,
|
||||
"Apps" => VirtualKeyCode::Apps,
|
||||
"At" => VirtualKeyCode::At,
|
||||
"Ax" => VirtualKeyCode::Ax,
|
||||
"Backslash" => VirtualKeyCode::Backslash,
|
||||
"Calculator" => VirtualKeyCode::Calculator,
|
||||
"Capital" => VirtualKeyCode::Capital,
|
||||
"Semicolon" => VirtualKeyCode::Semicolon,
|
||||
"Comma" => VirtualKeyCode::Comma,
|
||||
"Convert" => VirtualKeyCode::Convert,
|
||||
"NumpadDecimal" => VirtualKeyCode::NumpadDecimal,
|
||||
"NumpadDivide" => VirtualKeyCode::NumpadDivide,
|
||||
"Equal" => VirtualKeyCode::Equals,
|
||||
"Backquote" => VirtualKeyCode::Grave,
|
||||
"Kana" => VirtualKeyCode::Kana,
|
||||
"Kanji" => VirtualKeyCode::Kanji,
|
||||
"AltLeft" => VirtualKeyCode::LAlt,
|
||||
"BracketLeft" => VirtualKeyCode::LBracket,
|
||||
"ControlLeft" => VirtualKeyCode::LControl,
|
||||
"ShiftLeft" => VirtualKeyCode::LShift,
|
||||
"MetaLeft" => VirtualKeyCode::LWin,
|
||||
"Mail" => VirtualKeyCode::Mail,
|
||||
"MediaSelect" => VirtualKeyCode::MediaSelect,
|
||||
"MediaStop" => VirtualKeyCode::MediaStop,
|
||||
"Minus" => VirtualKeyCode::Minus,
|
||||
"NumpadMultiply" => VirtualKeyCode::NumpadMultiply,
|
||||
"Mute" => VirtualKeyCode::Mute,
|
||||
"LaunchMyComputer" => VirtualKeyCode::MyComputer,
|
||||
"NavigateForward" => VirtualKeyCode::NavigateForward,
|
||||
"NavigateBackward" => VirtualKeyCode::NavigateBackward,
|
||||
"NextTrack" => VirtualKeyCode::NextTrack,
|
||||
"NoConvert" => VirtualKeyCode::NoConvert,
|
||||
"NumpadComma" => VirtualKeyCode::NumpadComma,
|
||||
"NumpadEnter" => VirtualKeyCode::NumpadEnter,
|
||||
"NumpadEquals" => VirtualKeyCode::NumpadEquals,
|
||||
"OEM102" => VirtualKeyCode::OEM102,
|
||||
"Period" => VirtualKeyCode::Period,
|
||||
"PlayPause" => VirtualKeyCode::PlayPause,
|
||||
"Power" => VirtualKeyCode::Power,
|
||||
"PrevTrack" => VirtualKeyCode::PrevTrack,
|
||||
"AltRight" => VirtualKeyCode::RAlt,
|
||||
"BracketRight" => VirtualKeyCode::RBracket,
|
||||
"ControlRight" => VirtualKeyCode::RControl,
|
||||
"ShiftRight" => VirtualKeyCode::RShift,
|
||||
"MetaRight" => VirtualKeyCode::RWin,
|
||||
"Slash" => VirtualKeyCode::Slash,
|
||||
"Sleep" => VirtualKeyCode::Sleep,
|
||||
"Stop" => VirtualKeyCode::Stop,
|
||||
"NumpadSubtract" => VirtualKeyCode::NumpadSubtract,
|
||||
"Sysrq" => VirtualKeyCode::Sysrq,
|
||||
"Tab" => VirtualKeyCode::Tab,
|
||||
"Underline" => VirtualKeyCode::Underline,
|
||||
"Unlabeled" => VirtualKeyCode::Unlabeled,
|
||||
"AudioVolumeDown" => VirtualKeyCode::VolumeDown,
|
||||
"AudioVolumeUp" => VirtualKeyCode::VolumeUp,
|
||||
"Wake" => VirtualKeyCode::Wake,
|
||||
"WebBack" => VirtualKeyCode::WebBack,
|
||||
"WebFavorites" => VirtualKeyCode::WebFavorites,
|
||||
"WebForward" => VirtualKeyCode::WebForward,
|
||||
"WebHome" => VirtualKeyCode::WebHome,
|
||||
"WebRefresh" => VirtualKeyCode::WebRefresh,
|
||||
"WebSearch" => VirtualKeyCode::WebSearch,
|
||||
"WebStop" => VirtualKeyCode::WebStop,
|
||||
"Yen" => VirtualKeyCode::Yen,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn keyboard_modifiers(event: &KeyboardEvent) -> ModifiersState {
|
||||
let mut m = ModifiersState::empty();
|
||||
m.set(ModifiersState::SHIFT, event.shift_key());
|
||||
m.set(ModifiersState::CTRL, event.ctrl_key());
|
||||
m.set(ModifiersState::ALT, event.alt_key());
|
||||
m.set(ModifiersState::LOGO, event.meta_key());
|
||||
m
|
||||
let mut state = ModifiersState::empty();
|
||||
|
||||
if event.shift_key() {
|
||||
state |= ModifiersState::SHIFT;
|
||||
}
|
||||
if event.ctrl_key() {
|
||||
state |= ModifiersState::CONTROL;
|
||||
}
|
||||
if event.alt_key() {
|
||||
state |= ModifiersState::ALT;
|
||||
}
|
||||
if event.meta_key() {
|
||||
state |= ModifiersState::SUPER;
|
||||
}
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
pub fn codepoint(event: &KeyboardEvent) -> char {
|
||||
// `event.key()` always returns a non-empty `String`. Therefore, this should
|
||||
// never panic.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
|
||||
event.key().chars().next().unwrap()
|
||||
pub fn mouse_modifiers(event: &MouseEvent) -> ModifiersState {
|
||||
let mut state = ModifiersState::empty();
|
||||
|
||||
if event.shift_key() {
|
||||
state |= ModifiersState::SHIFT;
|
||||
}
|
||||
if event.ctrl_key() {
|
||||
state |= ModifiersState::CONTROL;
|
||||
}
|
||||
if event.alt_key() {
|
||||
state |= ModifiersState::ALT;
|
||||
}
|
||||
if event.meta_key() {
|
||||
state |= ModifiersState::SUPER;
|
||||
}
|
||||
|
||||
state
|
||||
}
|
||||
|
||||
pub fn touch_position(event: &PointerEvent, _canvas: &HtmlCanvasElement) -> LogicalPosition<f64> {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use raw_window_handle::{RawDisplayHandle, RawWindowHandle, WebDisplayHandle, Web
|
|||
|
||||
use super::{backend, monitor::MonitorHandle, EventLoopWindowTarget, Fullscreen};
|
||||
|
||||
use std::cell::{Ref, RefCell};
|
||||
use std::cell::{Cell, Ref, RefCell};
|
||||
use std::collections::vec_deque::IntoIter as VecDequeIter;
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
|
|
@ -23,7 +23,7 @@ pub struct Window {
|
|||
register_redraw_request: Box<dyn Fn()>,
|
||||
resize_notify_fn: Box<dyn Fn(PhysicalSize<u32>)>,
|
||||
destroy_fn: Option<Box<dyn FnOnce()>>,
|
||||
has_focus: Rc<RefCell<bool>>,
|
||||
has_focus: Rc<Cell<bool>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
|
@ -43,7 +43,7 @@ impl Window {
|
|||
|
||||
let register_redraw_request = Box::new(move || runner.request_redraw(RootWI(id)));
|
||||
|
||||
let has_focus = Rc::new(RefCell::new(false));
|
||||
let has_focus = Rc::new(Cell::new(false));
|
||||
target.register(&canvas, id, prevent_default, has_focus.clone());
|
||||
|
||||
let runner = target.runner.clone();
|
||||
|
|
@ -388,12 +388,16 @@ impl Window {
|
|||
|
||||
#[inline]
|
||||
pub fn has_focus(&self) -> bool {
|
||||
*self.has_focus.borrow()
|
||||
self.has_focus.get()
|
||||
}
|
||||
|
||||
pub fn title(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
|
||||
pub fn reset_dead_keys(&self) {
|
||||
// Not supported
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
|
|
|
|||
|
|
@ -1,436 +0,0 @@
|
|||
use std::{
|
||||
char,
|
||||
sync::atomic::{AtomicBool, AtomicIsize, Ordering},
|
||||
};
|
||||
|
||||
use windows_sys::Win32::{
|
||||
Foundation::{LPARAM, WPARAM},
|
||||
UI::{
|
||||
Input::KeyboardAndMouse::{
|
||||
GetKeyState, GetKeyboardLayout, GetKeyboardState, MapVirtualKeyA, ToUnicodeEx,
|
||||
MAPVK_VK_TO_CHAR, MAPVK_VSC_TO_VK_EX, VIRTUAL_KEY, VK_0, VK_1, VK_2, VK_3, VK_4, VK_5,
|
||||
VK_6, VK_7, VK_8, VK_9, VK_A, VK_ADD, VK_APPS, VK_B, VK_BACK, VK_BROWSER_BACK,
|
||||
VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME, VK_BROWSER_REFRESH,
|
||||
VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_C, VK_CAPITAL, VK_CONTROL, VK_CONVERT, VK_D,
|
||||
VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_E, VK_END, VK_ESCAPE, VK_F, VK_F1,
|
||||
VK_F10, VK_F11, VK_F12, VK_F13, VK_F14, VK_F15, VK_F16, VK_F17, VK_F18, VK_F19, VK_F2,
|
||||
VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, VK_F3, VK_F4, VK_F5, VK_F6, VK_F7, VK_F8,
|
||||
VK_F9, VK_G, VK_H, VK_HOME, VK_I, VK_INSERT, VK_J, VK_K, VK_KANA, VK_KANJI, VK_L,
|
||||
VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LCONTROL, VK_LEFT, VK_LMENU, VK_LSHIFT,
|
||||
VK_LWIN, VK_M, VK_MEDIA_NEXT_TRACK, VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK,
|
||||
VK_MEDIA_STOP, VK_MENU, VK_MULTIPLY, VK_N, VK_NEXT, VK_NONCONVERT, VK_NUMLOCK,
|
||||
VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6,
|
||||
VK_NUMPAD7, VK_NUMPAD8, VK_NUMPAD9, VK_O, VK_OEM_1, VK_OEM_102, VK_OEM_2, VK_OEM_3,
|
||||
VK_OEM_4, VK_OEM_5, VK_OEM_6, VK_OEM_7, VK_OEM_COMMA, VK_OEM_MINUS, VK_OEM_PERIOD,
|
||||
VK_OEM_PLUS, VK_P, VK_PAUSE, VK_PRIOR, VK_Q, VK_R, VK_RCONTROL, VK_RETURN, VK_RIGHT,
|
||||
VK_RMENU, VK_RSHIFT, VK_RWIN, VK_S, VK_SCROLL, VK_SHIFT, VK_SLEEP, VK_SNAPSHOT,
|
||||
VK_SPACE, VK_SUBTRACT, VK_T, VK_TAB, VK_U, VK_UP, VK_V, VK_VOLUME_DOWN, VK_VOLUME_MUTE,
|
||||
VK_VOLUME_UP, VK_W, VK_X, VK_Y, VK_Z,
|
||||
},
|
||||
TextServices::HKL,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::event::{ModifiersState, ScanCode, VirtualKeyCode};
|
||||
|
||||
use super::util::has_flag;
|
||||
|
||||
fn key_pressed(vkey: VIRTUAL_KEY) -> bool {
|
||||
unsafe { has_flag(GetKeyState(vkey as i32), 1 << 15) }
|
||||
}
|
||||
|
||||
pub fn get_key_mods() -> ModifiersState {
|
||||
let filter_out_altgr = layout_uses_altgr() && key_pressed(VK_RMENU);
|
||||
|
||||
let mut mods = ModifiersState::empty();
|
||||
mods.set(ModifiersState::SHIFT, key_pressed(VK_SHIFT));
|
||||
mods.set(
|
||||
ModifiersState::CTRL,
|
||||
key_pressed(VK_CONTROL) && !filter_out_altgr,
|
||||
);
|
||||
mods.set(
|
||||
ModifiersState::ALT,
|
||||
key_pressed(VK_MENU) && !filter_out_altgr,
|
||||
);
|
||||
mods.set(
|
||||
ModifiersState::LOGO,
|
||||
key_pressed(VK_LWIN) || key_pressed(VK_RWIN),
|
||||
);
|
||||
mods
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
pub struct ModifiersStateSide: u32 {
|
||||
const LSHIFT = 0b010;
|
||||
const RSHIFT = 0b001;
|
||||
|
||||
const LCTRL = 0b010 << 3;
|
||||
const RCTRL = 0b001 << 3;
|
||||
|
||||
const LALT = 0b010 << 6;
|
||||
const RALT = 0b001 << 6;
|
||||
|
||||
const LLOGO = 0b010 << 9;
|
||||
const RLOGO = 0b001 << 9;
|
||||
}
|
||||
}
|
||||
|
||||
impl ModifiersStateSide {
|
||||
pub fn filter_out_altgr(&self) -> ModifiersStateSide {
|
||||
match layout_uses_altgr() && self.contains(Self::RALT) {
|
||||
false => *self,
|
||||
true => *self & !(Self::LCTRL | Self::RCTRL | Self::LALT | Self::RALT),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ModifiersStateSide> for ModifiersState {
|
||||
fn from(side: ModifiersStateSide) -> Self {
|
||||
let mut state = ModifiersState::default();
|
||||
state.set(
|
||||
Self::SHIFT,
|
||||
side.intersects(ModifiersStateSide::LSHIFT | ModifiersStateSide::RSHIFT),
|
||||
);
|
||||
state.set(
|
||||
Self::CTRL,
|
||||
side.intersects(ModifiersStateSide::LCTRL | ModifiersStateSide::RCTRL),
|
||||
);
|
||||
state.set(
|
||||
Self::ALT,
|
||||
side.intersects(ModifiersStateSide::LALT | ModifiersStateSide::RALT),
|
||||
);
|
||||
state.set(
|
||||
Self::LOGO,
|
||||
side.intersects(ModifiersStateSide::LLOGO | ModifiersStateSide::RLOGO),
|
||||
);
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pressed_keys() -> impl Iterator<Item = VIRTUAL_KEY> {
|
||||
let mut keyboard_state = vec![0u8; 256];
|
||||
unsafe { GetKeyboardState(keyboard_state.as_mut_ptr()) };
|
||||
keyboard_state
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter(|(_, p)| (*p & (1 << 7)) != 0) // whether or not a key is pressed is communicated via the high-order bit
|
||||
.map(|(i, _)| i as u16)
|
||||
}
|
||||
|
||||
unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option<char> {
|
||||
let mut unicode_bytes = [0u16; 5];
|
||||
let len = ToUnicodeEx(
|
||||
v_key,
|
||||
0,
|
||||
keyboard_state.as_ptr(),
|
||||
unicode_bytes.as_mut_ptr(),
|
||||
unicode_bytes.len() as _,
|
||||
0,
|
||||
hkl,
|
||||
);
|
||||
if len >= 1 {
|
||||
char::decode_utf16(unicode_bytes.iter().cloned())
|
||||
.next()
|
||||
.and_then(|c| c.ok())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Figures out if the keyboard layout has an AltGr key instead of an Alt key.
|
||||
///
|
||||
/// Unfortunately, the Windows API doesn't give a way for us to conveniently figure that out. So,
|
||||
/// we use a technique blatantly stolen from [the Firefox source code][source]: iterate over every
|
||||
/// possible virtual key and compare the `char` output when AltGr is pressed vs when it isn't. If
|
||||
/// pressing AltGr outputs characters that are different from the standard characters, the layout
|
||||
/// uses AltGr. Otherwise, it doesn't.
|
||||
///
|
||||
/// [source]: https://github.com/mozilla/gecko-dev/blob/265e6721798a455604328ed5262f430cfcc37c2f/widget/windows/KeyboardLayout.cpp#L4356-L4416
|
||||
fn layout_uses_altgr() -> bool {
|
||||
unsafe {
|
||||
static ACTIVE_LAYOUT: AtomicIsize = AtomicIsize::new(0);
|
||||
static USES_ALTGR: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
let hkl = GetKeyboardLayout(0);
|
||||
let old_hkl = ACTIVE_LAYOUT.swap(hkl, Ordering::SeqCst);
|
||||
|
||||
if hkl == old_hkl {
|
||||
return USES_ALTGR.load(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let mut keyboard_state_altgr = [0u8; 256];
|
||||
// AltGr is an alias for Ctrl+Alt for... some reason. Whatever it is, those are the keypresses
|
||||
// we have to emulate to do an AltGr test.
|
||||
keyboard_state_altgr[VK_MENU as usize] = 0x80;
|
||||
keyboard_state_altgr[VK_CONTROL as usize] = 0x80;
|
||||
|
||||
let keyboard_state_empty = [0u8; 256];
|
||||
|
||||
for v_key in 0..=255 {
|
||||
let key_noaltgr = get_char(&keyboard_state_empty, v_key, hkl);
|
||||
let key_altgr = get_char(&keyboard_state_altgr, v_key, hkl);
|
||||
if let (Some(noaltgr), Some(altgr)) = (key_noaltgr, key_altgr) {
|
||||
if noaltgr != altgr {
|
||||
USES_ALTGR.store(true, Ordering::SeqCst);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
USES_ALTGR.store(false, Ordering::SeqCst);
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vkey_to_winit_vkey(vkey: VIRTUAL_KEY) -> Option<VirtualKeyCode> {
|
||||
// VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
match vkey {
|
||||
//VK_LBUTTON => Some(VirtualKeyCode::Lbutton),
|
||||
//VK_RBUTTON => Some(VirtualKeyCode::Rbutton),
|
||||
//VK_CANCEL => Some(VirtualKeyCode::Cancel),
|
||||
//VK_MBUTTON => Some(VirtualKeyCode::Mbutton),
|
||||
//VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1),
|
||||
//VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2),
|
||||
VK_BACK => Some(VirtualKeyCode::Back),
|
||||
VK_TAB => Some(VirtualKeyCode::Tab),
|
||||
//VK_CLEAR => Some(VirtualKeyCode::Clear),
|
||||
VK_RETURN => Some(VirtualKeyCode::Return),
|
||||
VK_LSHIFT => Some(VirtualKeyCode::LShift),
|
||||
VK_RSHIFT => Some(VirtualKeyCode::RShift),
|
||||
VK_LCONTROL => Some(VirtualKeyCode::LControl),
|
||||
VK_RCONTROL => Some(VirtualKeyCode::RControl),
|
||||
VK_LMENU => Some(VirtualKeyCode::LAlt),
|
||||
VK_RMENU => Some(VirtualKeyCode::RAlt),
|
||||
VK_PAUSE => Some(VirtualKeyCode::Pause),
|
||||
VK_CAPITAL => Some(VirtualKeyCode::Capital),
|
||||
VK_KANA => Some(VirtualKeyCode::Kana),
|
||||
//VK_HANGUEL => Some(VirtualKeyCode::Hanguel),
|
||||
//VK_HANGUL => Some(VirtualKeyCode::Hangul),
|
||||
//VK_JUNJA => Some(VirtualKeyCode::Junja),
|
||||
//VK_FINAL => Some(VirtualKeyCode::Final),
|
||||
//VK_HANJA => Some(VirtualKeyCode::Hanja),
|
||||
VK_KANJI => Some(VirtualKeyCode::Kanji),
|
||||
VK_ESCAPE => Some(VirtualKeyCode::Escape),
|
||||
VK_CONVERT => Some(VirtualKeyCode::Convert),
|
||||
VK_NONCONVERT => Some(VirtualKeyCode::NoConvert),
|
||||
//VK_ACCEPT => Some(VirtualKeyCode::Accept),
|
||||
//VK_MODECHANGE => Some(VirtualKeyCode::Modechange),
|
||||
VK_SPACE => Some(VirtualKeyCode::Space),
|
||||
VK_PRIOR => Some(VirtualKeyCode::PageUp),
|
||||
VK_NEXT => Some(VirtualKeyCode::PageDown),
|
||||
VK_END => Some(VirtualKeyCode::End),
|
||||
VK_HOME => Some(VirtualKeyCode::Home),
|
||||
VK_LEFT => Some(VirtualKeyCode::Left),
|
||||
VK_UP => Some(VirtualKeyCode::Up),
|
||||
VK_RIGHT => Some(VirtualKeyCode::Right),
|
||||
VK_DOWN => Some(VirtualKeyCode::Down),
|
||||
//VK_SELECT => Some(VirtualKeyCode::Select),
|
||||
//VK_PRINT => Some(VirtualKeyCode::Print),
|
||||
//VK_EXECUTE => Some(VirtualKeyCode::Execute),
|
||||
VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot),
|
||||
VK_INSERT => Some(VirtualKeyCode::Insert),
|
||||
VK_DELETE => Some(VirtualKeyCode::Delete),
|
||||
//VK_HELP => Some(VirtualKeyCode::Help),
|
||||
VK_0 => Some(VirtualKeyCode::Key0),
|
||||
VK_1 => Some(VirtualKeyCode::Key1),
|
||||
VK_2 => Some(VirtualKeyCode::Key2),
|
||||
VK_3 => Some(VirtualKeyCode::Key3),
|
||||
VK_4 => Some(VirtualKeyCode::Key4),
|
||||
VK_5 => Some(VirtualKeyCode::Key5),
|
||||
VK_6 => Some(VirtualKeyCode::Key6),
|
||||
VK_7 => Some(VirtualKeyCode::Key7),
|
||||
VK_8 => Some(VirtualKeyCode::Key8),
|
||||
VK_9 => Some(VirtualKeyCode::Key9),
|
||||
VK_A => Some(VirtualKeyCode::A),
|
||||
VK_B => Some(VirtualKeyCode::B),
|
||||
VK_C => Some(VirtualKeyCode::C),
|
||||
VK_D => Some(VirtualKeyCode::D),
|
||||
VK_E => Some(VirtualKeyCode::E),
|
||||
VK_F => Some(VirtualKeyCode::F),
|
||||
VK_G => Some(VirtualKeyCode::G),
|
||||
VK_H => Some(VirtualKeyCode::H),
|
||||
VK_I => Some(VirtualKeyCode::I),
|
||||
VK_J => Some(VirtualKeyCode::J),
|
||||
VK_K => Some(VirtualKeyCode::K),
|
||||
VK_L => Some(VirtualKeyCode::L),
|
||||
VK_M => Some(VirtualKeyCode::M),
|
||||
VK_N => Some(VirtualKeyCode::N),
|
||||
VK_O => Some(VirtualKeyCode::O),
|
||||
VK_P => Some(VirtualKeyCode::P),
|
||||
VK_Q => Some(VirtualKeyCode::Q),
|
||||
VK_R => Some(VirtualKeyCode::R),
|
||||
VK_S => Some(VirtualKeyCode::S),
|
||||
VK_T => Some(VirtualKeyCode::T),
|
||||
VK_U => Some(VirtualKeyCode::U),
|
||||
VK_V => Some(VirtualKeyCode::V),
|
||||
VK_W => Some(VirtualKeyCode::W),
|
||||
VK_X => Some(VirtualKeyCode::X),
|
||||
VK_Y => Some(VirtualKeyCode::Y),
|
||||
VK_Z => Some(VirtualKeyCode::Z),
|
||||
VK_LWIN => Some(VirtualKeyCode::LWin),
|
||||
VK_RWIN => Some(VirtualKeyCode::RWin),
|
||||
VK_APPS => Some(VirtualKeyCode::Apps),
|
||||
VK_SLEEP => Some(VirtualKeyCode::Sleep),
|
||||
VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0),
|
||||
VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1),
|
||||
VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2),
|
||||
VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3),
|
||||
VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4),
|
||||
VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5),
|
||||
VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6),
|
||||
VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7),
|
||||
VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8),
|
||||
VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9),
|
||||
VK_MULTIPLY => Some(VirtualKeyCode::NumpadMultiply),
|
||||
VK_ADD => Some(VirtualKeyCode::NumpadAdd),
|
||||
//VK_SEPARATOR => Some(VirtualKeyCode::Separator),
|
||||
VK_SUBTRACT => Some(VirtualKeyCode::NumpadSubtract),
|
||||
VK_DECIMAL => Some(VirtualKeyCode::NumpadDecimal),
|
||||
VK_DIVIDE => Some(VirtualKeyCode::NumpadDivide),
|
||||
VK_F1 => Some(VirtualKeyCode::F1),
|
||||
VK_F2 => Some(VirtualKeyCode::F2),
|
||||
VK_F3 => Some(VirtualKeyCode::F3),
|
||||
VK_F4 => Some(VirtualKeyCode::F4),
|
||||
VK_F5 => Some(VirtualKeyCode::F5),
|
||||
VK_F6 => Some(VirtualKeyCode::F6),
|
||||
VK_F7 => Some(VirtualKeyCode::F7),
|
||||
VK_F8 => Some(VirtualKeyCode::F8),
|
||||
VK_F9 => Some(VirtualKeyCode::F9),
|
||||
VK_F10 => Some(VirtualKeyCode::F10),
|
||||
VK_F11 => Some(VirtualKeyCode::F11),
|
||||
VK_F12 => Some(VirtualKeyCode::F12),
|
||||
VK_F13 => Some(VirtualKeyCode::F13),
|
||||
VK_F14 => Some(VirtualKeyCode::F14),
|
||||
VK_F15 => Some(VirtualKeyCode::F15),
|
||||
VK_F16 => Some(VirtualKeyCode::F16),
|
||||
VK_F17 => Some(VirtualKeyCode::F17),
|
||||
VK_F18 => Some(VirtualKeyCode::F18),
|
||||
VK_F19 => Some(VirtualKeyCode::F19),
|
||||
VK_F20 => Some(VirtualKeyCode::F20),
|
||||
VK_F21 => Some(VirtualKeyCode::F21),
|
||||
VK_F22 => Some(VirtualKeyCode::F22),
|
||||
VK_F23 => Some(VirtualKeyCode::F23),
|
||||
VK_F24 => Some(VirtualKeyCode::F24),
|
||||
VK_NUMLOCK => Some(VirtualKeyCode::Numlock),
|
||||
VK_SCROLL => Some(VirtualKeyCode::Scroll),
|
||||
VK_BROWSER_BACK => Some(VirtualKeyCode::NavigateBackward),
|
||||
VK_BROWSER_FORWARD => Some(VirtualKeyCode::NavigateForward),
|
||||
VK_BROWSER_REFRESH => Some(VirtualKeyCode::WebRefresh),
|
||||
VK_BROWSER_STOP => Some(VirtualKeyCode::WebStop),
|
||||
VK_BROWSER_SEARCH => Some(VirtualKeyCode::WebSearch),
|
||||
VK_BROWSER_FAVORITES => Some(VirtualKeyCode::WebFavorites),
|
||||
VK_BROWSER_HOME => Some(VirtualKeyCode::WebHome),
|
||||
VK_VOLUME_MUTE => Some(VirtualKeyCode::Mute),
|
||||
VK_VOLUME_DOWN => Some(VirtualKeyCode::VolumeDown),
|
||||
VK_VOLUME_UP => Some(VirtualKeyCode::VolumeUp),
|
||||
VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::NextTrack),
|
||||
VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::PrevTrack),
|
||||
VK_MEDIA_STOP => Some(VirtualKeyCode::MediaStop),
|
||||
VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::PlayPause),
|
||||
VK_LAUNCH_MAIL => Some(VirtualKeyCode::Mail),
|
||||
VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::MediaSelect),
|
||||
/*VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1),
|
||||
VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),*/
|
||||
VK_OEM_PLUS => Some(VirtualKeyCode::Equals),
|
||||
VK_OEM_COMMA => Some(VirtualKeyCode::Comma),
|
||||
VK_OEM_MINUS => Some(VirtualKeyCode::Minus),
|
||||
VK_OEM_PERIOD => Some(VirtualKeyCode::Period),
|
||||
VK_OEM_1 => map_text_keys(vkey),
|
||||
VK_OEM_2 => map_text_keys(vkey),
|
||||
VK_OEM_3 => map_text_keys(vkey),
|
||||
VK_OEM_4 => map_text_keys(vkey),
|
||||
VK_OEM_5 => map_text_keys(vkey),
|
||||
VK_OEM_6 => map_text_keys(vkey),
|
||||
VK_OEM_7 => map_text_keys(vkey),
|
||||
/* VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */
|
||||
VK_OEM_102 => Some(VirtualKeyCode::OEM102),
|
||||
/*VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
|
||||
VK_PACKET => Some(VirtualKeyCode::Packet),
|
||||
VK_ATTN => Some(VirtualKeyCode::Attn),
|
||||
VK_CRSEL => Some(VirtualKeyCode::Crsel),
|
||||
VK_EXSEL => Some(VirtualKeyCode::Exsel),
|
||||
VK_EREOF => Some(VirtualKeyCode::Ereof),
|
||||
VK_PLAY => Some(VirtualKeyCode::Play),
|
||||
VK_ZOOM => Some(VirtualKeyCode::Zoom),
|
||||
VK_NONAME => Some(VirtualKeyCode::Noname),
|
||||
VK_PA1 => Some(VirtualKeyCode::Pa1),
|
||||
VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_extended_keys(
|
||||
vkey: VIRTUAL_KEY,
|
||||
mut scancode: u32,
|
||||
extended: bool,
|
||||
) -> Option<(VIRTUAL_KEY, u32)> {
|
||||
// Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
|
||||
scancode |= if extended { 0xE000 } else { 0x0000 };
|
||||
let vkey = match vkey {
|
||||
VK_SHIFT => (unsafe { MapVirtualKeyA(scancode, MAPVK_VSC_TO_VK_EX) } as u16),
|
||||
VK_CONTROL => {
|
||||
if extended {
|
||||
VK_RCONTROL
|
||||
} else {
|
||||
VK_LCONTROL
|
||||
}
|
||||
}
|
||||
VK_MENU => {
|
||||
if extended {
|
||||
VK_RMENU
|
||||
} else {
|
||||
VK_LMENU
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
match scancode {
|
||||
// When VK_PAUSE is pressed it emits a LeftControl + NumLock scancode event sequence, but reports VK_PAUSE
|
||||
// as the virtual key on both events, or VK_PAUSE on the first event or 0xFF when using raw input.
|
||||
// Don't emit anything for the LeftControl event in the pair...
|
||||
0xE01D if vkey == VK_PAUSE => return None,
|
||||
// ...and emit the Pause event for the second event in the pair.
|
||||
0x45 if vkey == VK_PAUSE || vkey == 0xFF => {
|
||||
scancode = 0xE059;
|
||||
VK_PAUSE
|
||||
}
|
||||
// VK_PAUSE has an incorrect vkey value when used with modifiers. VK_PAUSE also reports a different
|
||||
// scancode when used with modifiers than when used without
|
||||
0xE046 => {
|
||||
scancode = 0xE059;
|
||||
VK_PAUSE
|
||||
}
|
||||
// VK_SCROLL has an incorrect vkey value when used with modifiers.
|
||||
0x46 => VK_SCROLL,
|
||||
_ => vkey,
|
||||
}
|
||||
}
|
||||
};
|
||||
Some((vkey, scancode))
|
||||
}
|
||||
|
||||
pub fn process_key_params(
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
) -> Option<(ScanCode, Option<VirtualKeyCode>)> {
|
||||
let scancode = ((lparam >> 16) & 0xff) as u32;
|
||||
let extended = (lparam & 0x01000000) != 0;
|
||||
handle_extended_keys(wparam as u16, scancode, extended)
|
||||
.map(|(vkey, scancode)| (scancode, vkey_to_winit_vkey(vkey)))
|
||||
}
|
||||
|
||||
// This is needed as windows doesn't properly distinguish
|
||||
// some virtual key codes for different keyboard layouts
|
||||
fn map_text_keys(win_virtual_key: VIRTUAL_KEY) -> Option<VirtualKeyCode> {
|
||||
let char_key = unsafe { MapVirtualKeyA(win_virtual_key as u32, MAPVK_VK_TO_CHAR) } & 0x7FFF;
|
||||
match char::from_u32(char_key) {
|
||||
Some(';') => Some(VirtualKeyCode::Semicolon),
|
||||
Some('/') => Some(VirtualKeyCode::Slash),
|
||||
Some('`') => Some(VirtualKeyCode::Grave),
|
||||
Some('[') => Some(VirtualKeyCode::LBracket),
|
||||
Some(']') => Some(VirtualKeyCode::RBracket),
|
||||
Some('\'') => Some(VirtualKeyCode::Apostrophe),
|
||||
Some('\\') => Some(VirtualKeyCode::Backslash),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
1272
src/platform_impl/windows/keyboard.rs
Normal file
1272
src/platform_impl/windows/keyboard.rs
Normal file
File diff suppressed because it is too large
Load diff
997
src/platform_impl/windows/keyboard_layout.rs
Normal file
997
src/platform_impl/windows/keyboard_layout.rs
Normal file
|
|
@ -0,0 +1,997 @@
|
|||
use std::{
|
||||
collections::{hash_map::Entry, HashMap, HashSet},
|
||||
ffi::OsString,
|
||||
os::windows::ffi::OsStringExt,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use smol_str::SmolStr;
|
||||
use windows_sys::Win32::{
|
||||
System::SystemServices::{LANG_JAPANESE, LANG_KOREAN},
|
||||
UI::{
|
||||
Input::KeyboardAndMouse::{
|
||||
GetKeyState, GetKeyboardLayout, MapVirtualKeyExW, ToUnicodeEx, MAPVK_VK_TO_VSC_EX,
|
||||
VIRTUAL_KEY, VK_ACCEPT, VK_ADD, VK_APPS, VK_ATTN, VK_BACK, VK_BROWSER_BACK,
|
||||
VK_BROWSER_FAVORITES, VK_BROWSER_FORWARD, VK_BROWSER_HOME, VK_BROWSER_REFRESH,
|
||||
VK_BROWSER_SEARCH, VK_BROWSER_STOP, VK_CANCEL, VK_CAPITAL, VK_CLEAR, VK_CONTROL,
|
||||
VK_CONVERT, VK_CRSEL, VK_DECIMAL, VK_DELETE, VK_DIVIDE, VK_DOWN, VK_END, VK_EREOF,
|
||||
VK_ESCAPE, VK_EXECUTE, VK_EXSEL, VK_F1, VK_F10, VK_F11, VK_F12, VK_F13, VK_F14, VK_F15,
|
||||
VK_F16, VK_F17, VK_F18, VK_F19, VK_F2, VK_F20, VK_F21, VK_F22, VK_F23, VK_F24, VK_F3,
|
||||
VK_F4, VK_F5, VK_F6, VK_F7, VK_F8, VK_F9, VK_FINAL, VK_GAMEPAD_A, VK_GAMEPAD_B,
|
||||
VK_GAMEPAD_DPAD_DOWN, VK_GAMEPAD_DPAD_LEFT, VK_GAMEPAD_DPAD_RIGHT, VK_GAMEPAD_DPAD_UP,
|
||||
VK_GAMEPAD_LEFT_SHOULDER, VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON,
|
||||
VK_GAMEPAD_LEFT_THUMBSTICK_DOWN, VK_GAMEPAD_LEFT_THUMBSTICK_LEFT,
|
||||
VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT, VK_GAMEPAD_LEFT_THUMBSTICK_UP,
|
||||
VK_GAMEPAD_LEFT_TRIGGER, VK_GAMEPAD_MENU, VK_GAMEPAD_RIGHT_SHOULDER,
|
||||
VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON, VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN,
|
||||
VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT, VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT,
|
||||
VK_GAMEPAD_RIGHT_THUMBSTICK_UP, VK_GAMEPAD_RIGHT_TRIGGER, VK_GAMEPAD_VIEW,
|
||||
VK_GAMEPAD_X, VK_GAMEPAD_Y, VK_HANGUL, VK_HANJA, VK_HELP, VK_HOME, VK_ICO_00,
|
||||
VK_ICO_CLEAR, VK_ICO_HELP, VK_INSERT, VK_JUNJA, VK_KANA, VK_KANJI, VK_LAUNCH_APP1,
|
||||
VK_LAUNCH_APP2, VK_LAUNCH_MAIL, VK_LAUNCH_MEDIA_SELECT, VK_LBUTTON, VK_LCONTROL,
|
||||
VK_LEFT, VK_LMENU, VK_LSHIFT, VK_LWIN, VK_MBUTTON, VK_MEDIA_NEXT_TRACK,
|
||||
VK_MEDIA_PLAY_PAUSE, VK_MEDIA_PREV_TRACK, VK_MEDIA_STOP, VK_MENU, VK_MODECHANGE,
|
||||
VK_MULTIPLY, VK_NAVIGATION_ACCEPT, VK_NAVIGATION_CANCEL, VK_NAVIGATION_DOWN,
|
||||
VK_NAVIGATION_LEFT, VK_NAVIGATION_MENU, VK_NAVIGATION_RIGHT, VK_NAVIGATION_UP,
|
||||
VK_NAVIGATION_VIEW, VK_NEXT, VK_NONAME, VK_NONCONVERT, VK_NUMLOCK, VK_NUMPAD0,
|
||||
VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7,
|
||||
VK_NUMPAD8, VK_NUMPAD9, VK_OEM_1, VK_OEM_102, VK_OEM_2, VK_OEM_3, VK_OEM_4, VK_OEM_5,
|
||||
VK_OEM_6, VK_OEM_7, VK_OEM_8, VK_OEM_ATTN, VK_OEM_AUTO, VK_OEM_AX, VK_OEM_BACKTAB,
|
||||
VK_OEM_CLEAR, VK_OEM_COMMA, VK_OEM_COPY, VK_OEM_CUSEL, VK_OEM_ENLW, VK_OEM_FINISH,
|
||||
VK_OEM_FJ_LOYA, VK_OEM_FJ_MASSHOU, VK_OEM_FJ_ROYA, VK_OEM_FJ_TOUROKU, VK_OEM_JUMP,
|
||||
VK_OEM_MINUS, VK_OEM_NEC_EQUAL, VK_OEM_PA1, VK_OEM_PA2, VK_OEM_PA3, VK_OEM_PERIOD,
|
||||
VK_OEM_PLUS, VK_OEM_RESET, VK_OEM_WSCTRL, VK_PA1, VK_PACKET, VK_PAUSE, VK_PLAY,
|
||||
VK_PRINT, VK_PRIOR, VK_PROCESSKEY, VK_RBUTTON, VK_RCONTROL, VK_RETURN, VK_RIGHT,
|
||||
VK_RMENU, VK_RSHIFT, VK_RWIN, VK_SCROLL, VK_SELECT, VK_SEPARATOR, VK_SHIFT, VK_SLEEP,
|
||||
VK_SNAPSHOT, VK_SPACE, VK_SUBTRACT, VK_TAB, VK_UP, VK_VOLUME_DOWN, VK_VOLUME_MUTE,
|
||||
VK_VOLUME_UP, VK_XBUTTON1, VK_XBUTTON2, VK_ZOOM,
|
||||
},
|
||||
TextServices::HKL,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
keyboard::{Key, KeyCode, ModifiersState, NativeKey},
|
||||
platform::scancode::KeyCodeExtScancode,
|
||||
platform_impl::{loword, primarylangid},
|
||||
};
|
||||
|
||||
pub(crate) static LAYOUT_CACHE: Lazy<Mutex<LayoutCache>> =
|
||||
Lazy::new(|| Mutex::new(LayoutCache::default()));
|
||||
|
||||
fn key_pressed(vkey: VIRTUAL_KEY) -> bool {
|
||||
unsafe { (GetKeyState(vkey as i32) & (1 << 15)) == (1 << 15) }
|
||||
}
|
||||
|
||||
const NUMPAD_VKEYS: [VIRTUAL_KEY; 16] = [
|
||||
VK_NUMPAD0,
|
||||
VK_NUMPAD1,
|
||||
VK_NUMPAD2,
|
||||
VK_NUMPAD3,
|
||||
VK_NUMPAD4,
|
||||
VK_NUMPAD5,
|
||||
VK_NUMPAD6,
|
||||
VK_NUMPAD7,
|
||||
VK_NUMPAD8,
|
||||
VK_NUMPAD9,
|
||||
VK_MULTIPLY,
|
||||
VK_ADD,
|
||||
VK_SEPARATOR,
|
||||
VK_SUBTRACT,
|
||||
VK_DECIMAL,
|
||||
VK_DIVIDE,
|
||||
];
|
||||
|
||||
static NUMPAD_KEYCODES: Lazy<HashSet<KeyCode>> = Lazy::new(|| {
|
||||
let mut keycodes = HashSet::new();
|
||||
keycodes.insert(KeyCode::Numpad0);
|
||||
keycodes.insert(KeyCode::Numpad1);
|
||||
keycodes.insert(KeyCode::Numpad2);
|
||||
keycodes.insert(KeyCode::Numpad3);
|
||||
keycodes.insert(KeyCode::Numpad4);
|
||||
keycodes.insert(KeyCode::Numpad5);
|
||||
keycodes.insert(KeyCode::Numpad6);
|
||||
keycodes.insert(KeyCode::Numpad7);
|
||||
keycodes.insert(KeyCode::Numpad8);
|
||||
keycodes.insert(KeyCode::Numpad9);
|
||||
keycodes.insert(KeyCode::NumpadMultiply);
|
||||
keycodes.insert(KeyCode::NumpadAdd);
|
||||
keycodes.insert(KeyCode::NumpadComma);
|
||||
keycodes.insert(KeyCode::NumpadSubtract);
|
||||
keycodes.insert(KeyCode::NumpadDecimal);
|
||||
keycodes.insert(KeyCode::NumpadDivide);
|
||||
keycodes
|
||||
});
|
||||
|
||||
bitflags! {
|
||||
pub struct WindowsModifiers : u8 {
|
||||
const SHIFT = 1 << 0;
|
||||
const CONTROL = 1 << 1;
|
||||
const ALT = 1 << 2;
|
||||
const CAPS_LOCK = 1 << 3;
|
||||
const FLAGS_END = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowsModifiers {
|
||||
pub fn active_modifiers(key_state: &[u8; 256]) -> WindowsModifiers {
|
||||
let shift = key_state[VK_SHIFT as usize] & 0x80 != 0;
|
||||
let lshift = key_state[VK_LSHIFT as usize] & 0x80 != 0;
|
||||
let rshift = key_state[VK_RSHIFT as usize] & 0x80 != 0;
|
||||
|
||||
let control = key_state[VK_CONTROL as usize] & 0x80 != 0;
|
||||
let lcontrol = key_state[VK_LCONTROL as usize] & 0x80 != 0;
|
||||
let rcontrol = key_state[VK_RCONTROL as usize] & 0x80 != 0;
|
||||
|
||||
let alt = key_state[VK_MENU as usize] & 0x80 != 0;
|
||||
let lalt = key_state[VK_LMENU as usize] & 0x80 != 0;
|
||||
let ralt = key_state[VK_RMENU as usize] & 0x80 != 0;
|
||||
|
||||
let caps = key_state[VK_CAPITAL as usize] & 0x01 != 0;
|
||||
|
||||
let mut result = WindowsModifiers::empty();
|
||||
if shift || lshift || rshift {
|
||||
result.insert(WindowsModifiers::SHIFT);
|
||||
}
|
||||
if control || lcontrol || rcontrol {
|
||||
result.insert(WindowsModifiers::CONTROL);
|
||||
}
|
||||
if alt || lalt || ralt {
|
||||
result.insert(WindowsModifiers::ALT);
|
||||
}
|
||||
if caps {
|
||||
result.insert(WindowsModifiers::CAPS_LOCK);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn apply_to_kbd_state(self, key_state: &mut [u8; 256]) {
|
||||
if self.intersects(Self::SHIFT) {
|
||||
key_state[VK_SHIFT as usize] |= 0x80;
|
||||
} else {
|
||||
key_state[VK_SHIFT as usize] &= !0x80;
|
||||
key_state[VK_LSHIFT as usize] &= !0x80;
|
||||
key_state[VK_RSHIFT as usize] &= !0x80;
|
||||
}
|
||||
if self.intersects(Self::CONTROL) {
|
||||
key_state[VK_CONTROL as usize] |= 0x80;
|
||||
} else {
|
||||
key_state[VK_CONTROL as usize] &= !0x80;
|
||||
key_state[VK_LCONTROL as usize] &= !0x80;
|
||||
key_state[VK_RCONTROL as usize] &= !0x80;
|
||||
}
|
||||
if self.intersects(Self::ALT) {
|
||||
key_state[VK_MENU as usize] |= 0x80;
|
||||
} else {
|
||||
key_state[VK_MENU as usize] &= !0x80;
|
||||
key_state[VK_LMENU as usize] &= !0x80;
|
||||
key_state[VK_RMENU as usize] &= !0x80;
|
||||
}
|
||||
if self.intersects(Self::CAPS_LOCK) {
|
||||
key_state[VK_CAPITAL as usize] |= 0x01;
|
||||
} else {
|
||||
key_state[VK_CAPITAL as usize] &= !0x01;
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the control modifier if the alt modifier is not present.
|
||||
/// This is useful because on Windows: (Control + Alt) == AltGr
|
||||
/// but we don't want to interfere with the AltGr state.
|
||||
pub fn remove_only_ctrl(mut self) -> WindowsModifiers {
|
||||
if !self.contains(WindowsModifiers::ALT) {
|
||||
self.remove(WindowsModifiers::CONTROL);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Layout {
|
||||
pub hkl: u64,
|
||||
|
||||
/// Maps numpad keys from Windows virtual key to a `Key`.
|
||||
///
|
||||
/// This is useful because some numpad keys generate different charcaters based on the locale.
|
||||
/// For example `VK_DECIMAL` is sometimes "." and sometimes ",". Note: numpad-specific virtual
|
||||
/// keys are only produced by Windows when the NumLock is active.
|
||||
///
|
||||
/// Making this field separate from the `keys` field saves having to add NumLock as a modifier
|
||||
/// to `WindowsModifiers`, which would double the number of items in keys.
|
||||
pub numlock_on_keys: HashMap<VIRTUAL_KEY, Key>,
|
||||
/// Like `numlock_on_keys` but this will map to the key that would be produced if numlock was
|
||||
/// off. The keys of this map are identical to the keys of `numlock_on_keys`.
|
||||
pub numlock_off_keys: HashMap<VIRTUAL_KEY, Key>,
|
||||
|
||||
/// Maps a modifier state to group of key strings
|
||||
/// We're not using `ModifiersState` here because that object cannot express caps lock,
|
||||
/// but we need to handle caps lock too.
|
||||
///
|
||||
/// This map shouldn't need to exist.
|
||||
/// However currently this seems to be the only good way
|
||||
/// of getting the label for the pressed key. Note that calling `ToUnicode`
|
||||
/// just when the key is pressed/released would be enough if `ToUnicode` wouldn't
|
||||
/// change the keyboard state (it clears the dead key). There is a flag to prevent
|
||||
/// changing the state, but that flag requires Windows 10, version 1607 or newer)
|
||||
pub keys: HashMap<WindowsModifiers, HashMap<KeyCode, Key>>,
|
||||
pub has_alt_graph: bool,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub fn get_key(
|
||||
&self,
|
||||
mods: WindowsModifiers,
|
||||
num_lock_on: bool,
|
||||
vkey: VIRTUAL_KEY,
|
||||
keycode: &KeyCode,
|
||||
) -> Key {
|
||||
let native_code = NativeKey::Windows(vkey);
|
||||
|
||||
let unknown_alt = vkey == VK_MENU;
|
||||
if !unknown_alt {
|
||||
// Here we try using the virtual key directly but if the virtual key doesn't distinguish
|
||||
// between left and right alt, we can't report AltGr. Therefore, we only do this if the
|
||||
// key is not the "unknown alt" key.
|
||||
//
|
||||
// The reason for using the virtual key directly is that `MapVirtualKeyExW` (used when
|
||||
// building the keys map) sometimes maps virtual keys to odd scancodes that don't match
|
||||
// the scancode coming from the KEYDOWN message for the same key. For example: `VK_LEFT`
|
||||
// is mapped to `0x004B`, but the scancode for the left arrow is `0xE04B`.
|
||||
let key_from_vkey =
|
||||
vkey_to_non_char_key(vkey, native_code.clone(), self.hkl, self.has_alt_graph);
|
||||
|
||||
if !matches!(key_from_vkey, Key::Unidentified(_)) {
|
||||
return key_from_vkey;
|
||||
}
|
||||
}
|
||||
if num_lock_on {
|
||||
if let Some(key) = self.numlock_on_keys.get(&vkey) {
|
||||
return key.clone();
|
||||
}
|
||||
} else if let Some(key) = self.numlock_off_keys.get(&vkey) {
|
||||
return key.clone();
|
||||
}
|
||||
if let Some(keys) = self.keys.get(&mods) {
|
||||
if let Some(key) = keys.get(keycode) {
|
||||
return key.clone();
|
||||
}
|
||||
}
|
||||
Key::Unidentified(native_code)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct LayoutCache {
|
||||
/// Maps locale identifiers (HKL) to layouts
|
||||
pub layouts: HashMap<u64, Layout>,
|
||||
}
|
||||
|
||||
impl LayoutCache {
|
||||
/// Checks whether the current layout is already known and
|
||||
/// prepares the layout if it isn't known.
|
||||
/// The current layout is then returned.
|
||||
pub fn get_current_layout(&mut self) -> (u64, &Layout) {
|
||||
let locale_id = unsafe { GetKeyboardLayout(0) } as u64;
|
||||
match self.layouts.entry(locale_id) {
|
||||
Entry::Occupied(entry) => (locale_id, entry.into_mut()),
|
||||
Entry::Vacant(entry) => {
|
||||
let layout = Self::prepare_layout(locale_id);
|
||||
(locale_id, entry.insert(layout))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_agnostic_mods(&mut self) -> ModifiersState {
|
||||
let (_, layout) = self.get_current_layout();
|
||||
let filter_out_altgr = layout.has_alt_graph && key_pressed(VK_RMENU);
|
||||
let mut mods = ModifiersState::empty();
|
||||
mods.set(ModifiersState::SHIFT, key_pressed(VK_SHIFT));
|
||||
mods.set(
|
||||
ModifiersState::CONTROL,
|
||||
key_pressed(VK_CONTROL) && !filter_out_altgr,
|
||||
);
|
||||
mods.set(
|
||||
ModifiersState::ALT,
|
||||
key_pressed(VK_MENU) && !filter_out_altgr,
|
||||
);
|
||||
mods.set(
|
||||
ModifiersState::SUPER,
|
||||
key_pressed(VK_LWIN) || key_pressed(VK_RWIN),
|
||||
);
|
||||
mods
|
||||
}
|
||||
|
||||
fn prepare_layout(locale_id: u64) -> Layout {
|
||||
let mut layout = Layout {
|
||||
hkl: locale_id,
|
||||
numlock_on_keys: Default::default(),
|
||||
numlock_off_keys: Default::default(),
|
||||
keys: Default::default(),
|
||||
has_alt_graph: false,
|
||||
};
|
||||
|
||||
// We initialize the keyboard state with all zeros to
|
||||
// simulate a scenario when no modifier is active.
|
||||
let mut key_state = [0u8; 256];
|
||||
|
||||
// `MapVirtualKeyExW` maps (non-numpad-specific) virtual keys to scancodes as if numlock
|
||||
// was off. We rely on this behavior to find all virtual keys which are not numpad-specific
|
||||
// but map to the numpad.
|
||||
//
|
||||
// src_vkey: VK ==> scancode: u16 (on the numpad)
|
||||
//
|
||||
// Then we convert the source virtual key into a `Key` and the scancode into a virtual key
|
||||
// to get the reverse mapping.
|
||||
//
|
||||
// src_vkey: VK ==> scancode: u16 (on the numpad)
|
||||
// || ||
|
||||
// \/ \/
|
||||
// map_value: Key <- map_vkey: VK
|
||||
layout.numlock_off_keys.reserve(NUMPAD_KEYCODES.len());
|
||||
for vk in 0..256 {
|
||||
let scancode = unsafe { MapVirtualKeyExW(vk, MAPVK_VK_TO_VSC_EX, locale_id as HKL) };
|
||||
if scancode == 0 {
|
||||
continue;
|
||||
}
|
||||
let keycode = KeyCode::from_scancode(scancode);
|
||||
if !is_numpad_specific(vk as VIRTUAL_KEY) && NUMPAD_KEYCODES.contains(&keycode) {
|
||||
let native_code = NativeKey::Windows(vk as VIRTUAL_KEY);
|
||||
let map_vkey = keycode_to_vkey(keycode, locale_id);
|
||||
if map_vkey == 0 {
|
||||
continue;
|
||||
}
|
||||
let map_value =
|
||||
vkey_to_non_char_key(vk as VIRTUAL_KEY, native_code, locale_id, false);
|
||||
if matches!(map_value, Key::Unidentified(_)) {
|
||||
continue;
|
||||
}
|
||||
layout.numlock_off_keys.insert(map_vkey, map_value);
|
||||
}
|
||||
}
|
||||
|
||||
layout.numlock_on_keys.reserve(NUMPAD_VKEYS.len());
|
||||
for vk in NUMPAD_VKEYS.iter() {
|
||||
let vk = (*vk) as u32;
|
||||
let scancode = unsafe { MapVirtualKeyExW(vk, MAPVK_VK_TO_VSC_EX, locale_id as HKL) };
|
||||
let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_id);
|
||||
if let ToUnicodeResult::Str(s) = unicode {
|
||||
layout
|
||||
.numlock_on_keys
|
||||
.insert(vk as VIRTUAL_KEY, Key::Character(SmolStr::new(s)));
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through every combination of modifiers
|
||||
let mods_end = WindowsModifiers::FLAGS_END.bits;
|
||||
for mod_state in 0..mods_end {
|
||||
let mut keys_for_this_mod = HashMap::with_capacity(256);
|
||||
|
||||
let mod_state = unsafe { WindowsModifiers::from_bits_unchecked(mod_state) };
|
||||
mod_state.apply_to_kbd_state(&mut key_state);
|
||||
|
||||
// Virtual key values are in the domain [0, 255].
|
||||
// This is reinforced by the fact that the keyboard state array has 256
|
||||
// elements. This array is allowed to be indexed by virtual key values
|
||||
// giving the key state for the virtual key used for indexing.
|
||||
for vk in 0..256 {
|
||||
let scancode =
|
||||
unsafe { MapVirtualKeyExW(vk, MAPVK_VK_TO_VSC_EX, locale_id as HKL) };
|
||||
if scancode == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let native_code = NativeKey::Windows(vk as VIRTUAL_KEY);
|
||||
let key_code = KeyCode::from_scancode(scancode);
|
||||
// Let's try to get the key from just the scancode and vk
|
||||
// We don't necessarily know yet if AltGraph is present on this layout so we'll
|
||||
// assume it isn't. Then we'll do a second pass where we set the "AltRight" keys to
|
||||
// "AltGr" in case we find out that there's an AltGraph.
|
||||
let preliminary_key =
|
||||
vkey_to_non_char_key(vk as VIRTUAL_KEY, native_code, locale_id, false);
|
||||
match preliminary_key {
|
||||
Key::Unidentified(_) => (),
|
||||
_ => {
|
||||
keys_for_this_mod.insert(key_code, preliminary_key);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_id);
|
||||
let key = match unicode {
|
||||
ToUnicodeResult::Str(str) => Key::Character(SmolStr::new(str)),
|
||||
ToUnicodeResult::Dead(dead_char) => {
|
||||
//println!("{:?} - {:?} produced dead {:?}", key_code, mod_state, dead_char);
|
||||
Key::Dead(dead_char)
|
||||
}
|
||||
ToUnicodeResult::None => {
|
||||
let has_alt = mod_state.contains(WindowsModifiers::ALT);
|
||||
let has_ctrl = mod_state.contains(WindowsModifiers::CONTROL);
|
||||
// HACK: `ToUnicodeEx` seems to fail getting the string for the numpad
|
||||
// divide key, so we handle that explicitly here
|
||||
if !has_alt && !has_ctrl && key_code == KeyCode::NumpadDivide {
|
||||
Key::Character(SmolStr::new("/"))
|
||||
} else {
|
||||
// Just use the unidentified key, we got earlier
|
||||
preliminary_key
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Check for alt graph.
|
||||
// The logic is that if a key pressed with no modifier produces
|
||||
// a different `Character` from when it's pressed with CTRL+ALT then the layout
|
||||
// has AltGr.
|
||||
let ctrl_alt: WindowsModifiers = WindowsModifiers::CONTROL | WindowsModifiers::ALT;
|
||||
let is_in_ctrl_alt = mod_state == ctrl_alt;
|
||||
if !layout.has_alt_graph && is_in_ctrl_alt {
|
||||
// Unwrapping here because if we are in the ctrl+alt modifier state
|
||||
// then the alt modifier state must have come before.
|
||||
let simple_keys = layout.keys.get(&WindowsModifiers::empty()).unwrap();
|
||||
if let Some(Key::Character(key_no_altgr)) = simple_keys.get(&key_code) {
|
||||
if let Key::Character(key) = &key {
|
||||
layout.has_alt_graph = key != key_no_altgr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keys_for_this_mod.insert(key_code, key);
|
||||
}
|
||||
layout.keys.insert(mod_state, keys_for_this_mod);
|
||||
}
|
||||
|
||||
// Second pass: replace right alt keys with AltGr if the layout has alt graph
|
||||
if layout.has_alt_graph {
|
||||
for mod_state in 0..mods_end {
|
||||
let mod_state = unsafe { WindowsModifiers::from_bits_unchecked(mod_state) };
|
||||
if let Some(keys) = layout.keys.get_mut(&mod_state) {
|
||||
if let Some(key) = keys.get_mut(&KeyCode::AltRight) {
|
||||
*key = Key::AltGraph;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout
|
||||
}
|
||||
|
||||
fn to_unicode_string(
|
||||
key_state: &[u8; 256],
|
||||
vkey: u32,
|
||||
scancode: u32,
|
||||
locale_id: u64,
|
||||
) -> ToUnicodeResult {
|
||||
unsafe {
|
||||
let mut label_wide = [0u16; 8];
|
||||
let mut wide_len = ToUnicodeEx(
|
||||
vkey,
|
||||
scancode,
|
||||
(&key_state[0]) as *const _,
|
||||
(&mut label_wide[0]) as *mut _,
|
||||
label_wide.len() as i32,
|
||||
0,
|
||||
locale_id as HKL,
|
||||
);
|
||||
if wide_len < 0 {
|
||||
// If it's dead, we run `ToUnicode` again to consume the dead-key
|
||||
wide_len = ToUnicodeEx(
|
||||
vkey,
|
||||
scancode,
|
||||
(&key_state[0]) as *const _,
|
||||
(&mut label_wide[0]) as *mut _,
|
||||
label_wide.len() as i32,
|
||||
0,
|
||||
locale_id as HKL,
|
||||
);
|
||||
if wide_len > 0 {
|
||||
let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]);
|
||||
if let Ok(label_str) = os_string.into_string() {
|
||||
if let Some(ch) = label_str.chars().next() {
|
||||
return ToUnicodeResult::Dead(Some(ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ToUnicodeResult::Dead(None);
|
||||
}
|
||||
if wide_len > 0 {
|
||||
let os_string = OsString::from_wide(&label_wide[0..wide_len as usize]);
|
||||
if let Ok(label_str) = os_string.into_string() {
|
||||
return ToUnicodeResult::Str(label_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
ToUnicodeResult::None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
enum ToUnicodeResult {
|
||||
Str(String),
|
||||
Dead(Option<char>),
|
||||
None,
|
||||
}
|
||||
|
||||
fn is_numpad_specific(vk: VIRTUAL_KEY) -> bool {
|
||||
matches!(
|
||||
vk,
|
||||
VK_NUMPAD0
|
||||
| VK_NUMPAD1
|
||||
| VK_NUMPAD2
|
||||
| VK_NUMPAD3
|
||||
| VK_NUMPAD4
|
||||
| VK_NUMPAD5
|
||||
| VK_NUMPAD6
|
||||
| VK_NUMPAD7
|
||||
| VK_NUMPAD8
|
||||
| VK_NUMPAD9
|
||||
| VK_ADD
|
||||
| VK_SUBTRACT
|
||||
| VK_DIVIDE
|
||||
| VK_DECIMAL
|
||||
| VK_SEPARATOR
|
||||
)
|
||||
}
|
||||
|
||||
fn keycode_to_vkey(keycode: KeyCode, hkl: u64) -> VIRTUAL_KEY {
|
||||
let primary_lang_id = primarylangid(loword(hkl as u32));
|
||||
let is_korean = primary_lang_id as u32 == LANG_KOREAN;
|
||||
let is_japanese = primary_lang_id as u32 == LANG_JAPANESE;
|
||||
|
||||
match keycode {
|
||||
KeyCode::Backquote => 0,
|
||||
KeyCode::Backslash => 0,
|
||||
KeyCode::BracketLeft => 0,
|
||||
KeyCode::BracketRight => 0,
|
||||
KeyCode::Comma => 0,
|
||||
KeyCode::Digit0 => 0,
|
||||
KeyCode::Digit1 => 0,
|
||||
KeyCode::Digit2 => 0,
|
||||
KeyCode::Digit3 => 0,
|
||||
KeyCode::Digit4 => 0,
|
||||
KeyCode::Digit5 => 0,
|
||||
KeyCode::Digit6 => 0,
|
||||
KeyCode::Digit7 => 0,
|
||||
KeyCode::Digit8 => 0,
|
||||
KeyCode::Digit9 => 0,
|
||||
KeyCode::Equal => 0,
|
||||
KeyCode::IntlBackslash => 0,
|
||||
KeyCode::IntlRo => 0,
|
||||
KeyCode::IntlYen => 0,
|
||||
KeyCode::KeyA => 0,
|
||||
KeyCode::KeyB => 0,
|
||||
KeyCode::KeyC => 0,
|
||||
KeyCode::KeyD => 0,
|
||||
KeyCode::KeyE => 0,
|
||||
KeyCode::KeyF => 0,
|
||||
KeyCode::KeyG => 0,
|
||||
KeyCode::KeyH => 0,
|
||||
KeyCode::KeyI => 0,
|
||||
KeyCode::KeyJ => 0,
|
||||
KeyCode::KeyK => 0,
|
||||
KeyCode::KeyL => 0,
|
||||
KeyCode::KeyM => 0,
|
||||
KeyCode::KeyN => 0,
|
||||
KeyCode::KeyO => 0,
|
||||
KeyCode::KeyP => 0,
|
||||
KeyCode::KeyQ => 0,
|
||||
KeyCode::KeyR => 0,
|
||||
KeyCode::KeyS => 0,
|
||||
KeyCode::KeyT => 0,
|
||||
KeyCode::KeyU => 0,
|
||||
KeyCode::KeyV => 0,
|
||||
KeyCode::KeyW => 0,
|
||||
KeyCode::KeyX => 0,
|
||||
KeyCode::KeyY => 0,
|
||||
KeyCode::KeyZ => 0,
|
||||
KeyCode::Minus => 0,
|
||||
KeyCode::Period => 0,
|
||||
KeyCode::Quote => 0,
|
||||
KeyCode::Semicolon => 0,
|
||||
KeyCode::Slash => 0,
|
||||
KeyCode::AltLeft => VK_LMENU,
|
||||
KeyCode::AltRight => VK_RMENU,
|
||||
KeyCode::Backspace => VK_BACK,
|
||||
KeyCode::CapsLock => VK_CAPITAL,
|
||||
KeyCode::ContextMenu => VK_APPS,
|
||||
KeyCode::ControlLeft => VK_LCONTROL,
|
||||
KeyCode::ControlRight => VK_RCONTROL,
|
||||
KeyCode::Enter => VK_RETURN,
|
||||
KeyCode::SuperLeft => VK_LWIN,
|
||||
KeyCode::SuperRight => VK_RWIN,
|
||||
KeyCode::ShiftLeft => VK_RSHIFT,
|
||||
KeyCode::ShiftRight => VK_LSHIFT,
|
||||
KeyCode::Space => VK_SPACE,
|
||||
KeyCode::Tab => VK_TAB,
|
||||
KeyCode::Convert => VK_CONVERT,
|
||||
KeyCode::KanaMode => VK_KANA,
|
||||
KeyCode::Lang1 if is_korean => VK_HANGUL,
|
||||
KeyCode::Lang1 if is_japanese => VK_KANA,
|
||||
KeyCode::Lang2 if is_korean => VK_HANJA,
|
||||
KeyCode::Lang2 if is_japanese => 0,
|
||||
KeyCode::Lang3 if is_japanese => VK_OEM_FINISH,
|
||||
KeyCode::Lang4 if is_japanese => 0,
|
||||
KeyCode::Lang5 if is_japanese => 0,
|
||||
KeyCode::NonConvert => VK_NONCONVERT,
|
||||
KeyCode::Delete => VK_DELETE,
|
||||
KeyCode::End => VK_END,
|
||||
KeyCode::Help => VK_HELP,
|
||||
KeyCode::Home => VK_HOME,
|
||||
KeyCode::Insert => VK_INSERT,
|
||||
KeyCode::PageDown => VK_NEXT,
|
||||
KeyCode::PageUp => VK_PRIOR,
|
||||
KeyCode::ArrowDown => VK_DOWN,
|
||||
KeyCode::ArrowLeft => VK_LEFT,
|
||||
KeyCode::ArrowRight => VK_RIGHT,
|
||||
KeyCode::ArrowUp => VK_UP,
|
||||
KeyCode::NumLock => VK_NUMLOCK,
|
||||
KeyCode::Numpad0 => VK_NUMPAD0,
|
||||
KeyCode::Numpad1 => VK_NUMPAD1,
|
||||
KeyCode::Numpad2 => VK_NUMPAD2,
|
||||
KeyCode::Numpad3 => VK_NUMPAD3,
|
||||
KeyCode::Numpad4 => VK_NUMPAD4,
|
||||
KeyCode::Numpad5 => VK_NUMPAD5,
|
||||
KeyCode::Numpad6 => VK_NUMPAD6,
|
||||
KeyCode::Numpad7 => VK_NUMPAD7,
|
||||
KeyCode::Numpad8 => VK_NUMPAD8,
|
||||
KeyCode::Numpad9 => VK_NUMPAD9,
|
||||
KeyCode::NumpadAdd => VK_ADD,
|
||||
KeyCode::NumpadBackspace => VK_BACK,
|
||||
KeyCode::NumpadClear => VK_CLEAR,
|
||||
KeyCode::NumpadClearEntry => 0,
|
||||
KeyCode::NumpadComma => VK_SEPARATOR,
|
||||
KeyCode::NumpadDecimal => VK_DECIMAL,
|
||||
KeyCode::NumpadDivide => VK_DIVIDE,
|
||||
KeyCode::NumpadEnter => VK_RETURN,
|
||||
KeyCode::NumpadEqual => 0,
|
||||
KeyCode::NumpadHash => 0,
|
||||
KeyCode::NumpadMemoryAdd => 0,
|
||||
KeyCode::NumpadMemoryClear => 0,
|
||||
KeyCode::NumpadMemoryRecall => 0,
|
||||
KeyCode::NumpadMemoryStore => 0,
|
||||
KeyCode::NumpadMemorySubtract => 0,
|
||||
KeyCode::NumpadMultiply => VK_MULTIPLY,
|
||||
KeyCode::NumpadParenLeft => 0,
|
||||
KeyCode::NumpadParenRight => 0,
|
||||
KeyCode::NumpadStar => 0,
|
||||
KeyCode::NumpadSubtract => VK_SUBTRACT,
|
||||
KeyCode::Escape => VK_ESCAPE,
|
||||
KeyCode::Fn => 0,
|
||||
KeyCode::FnLock => 0,
|
||||
KeyCode::PrintScreen => VK_SNAPSHOT,
|
||||
KeyCode::ScrollLock => VK_SCROLL,
|
||||
KeyCode::Pause => VK_PAUSE,
|
||||
KeyCode::BrowserBack => VK_BROWSER_BACK,
|
||||
KeyCode::BrowserFavorites => VK_BROWSER_FAVORITES,
|
||||
KeyCode::BrowserForward => VK_BROWSER_FORWARD,
|
||||
KeyCode::BrowserHome => VK_BROWSER_HOME,
|
||||
KeyCode::BrowserRefresh => VK_BROWSER_REFRESH,
|
||||
KeyCode::BrowserSearch => VK_BROWSER_SEARCH,
|
||||
KeyCode::BrowserStop => VK_BROWSER_STOP,
|
||||
KeyCode::Eject => 0,
|
||||
KeyCode::LaunchApp1 => VK_LAUNCH_APP1,
|
||||
KeyCode::LaunchApp2 => VK_LAUNCH_APP2,
|
||||
KeyCode::LaunchMail => VK_LAUNCH_MAIL,
|
||||
KeyCode::MediaPlayPause => VK_MEDIA_PLAY_PAUSE,
|
||||
KeyCode::MediaSelect => VK_LAUNCH_MEDIA_SELECT,
|
||||
KeyCode::MediaStop => VK_MEDIA_STOP,
|
||||
KeyCode::MediaTrackNext => VK_MEDIA_NEXT_TRACK,
|
||||
KeyCode::MediaTrackPrevious => VK_MEDIA_PREV_TRACK,
|
||||
KeyCode::Power => 0,
|
||||
KeyCode::Sleep => 0,
|
||||
KeyCode::AudioVolumeDown => VK_VOLUME_DOWN,
|
||||
KeyCode::AudioVolumeMute => VK_VOLUME_MUTE,
|
||||
KeyCode::AudioVolumeUp => VK_VOLUME_UP,
|
||||
KeyCode::WakeUp => 0,
|
||||
KeyCode::Hyper => 0,
|
||||
KeyCode::Turbo => 0,
|
||||
KeyCode::Abort => 0,
|
||||
KeyCode::Resume => 0,
|
||||
KeyCode::Suspend => 0,
|
||||
KeyCode::Again => 0,
|
||||
KeyCode::Copy => 0,
|
||||
KeyCode::Cut => 0,
|
||||
KeyCode::Find => 0,
|
||||
KeyCode::Open => 0,
|
||||
KeyCode::Paste => 0,
|
||||
KeyCode::Props => 0,
|
||||
KeyCode::Select => VK_SELECT,
|
||||
KeyCode::Undo => 0,
|
||||
KeyCode::Hiragana => 0,
|
||||
KeyCode::Katakana => 0,
|
||||
KeyCode::F1 => VK_F1,
|
||||
KeyCode::F2 => VK_F2,
|
||||
KeyCode::F3 => VK_F3,
|
||||
KeyCode::F4 => VK_F4,
|
||||
KeyCode::F5 => VK_F5,
|
||||
KeyCode::F6 => VK_F6,
|
||||
KeyCode::F7 => VK_F7,
|
||||
KeyCode::F8 => VK_F8,
|
||||
KeyCode::F9 => VK_F9,
|
||||
KeyCode::F10 => VK_F10,
|
||||
KeyCode::F11 => VK_F11,
|
||||
KeyCode::F12 => VK_F12,
|
||||
KeyCode::F13 => VK_F13,
|
||||
KeyCode::F14 => VK_F14,
|
||||
KeyCode::F15 => VK_F15,
|
||||
KeyCode::F16 => VK_F16,
|
||||
KeyCode::F17 => VK_F17,
|
||||
KeyCode::F18 => VK_F18,
|
||||
KeyCode::F19 => VK_F19,
|
||||
KeyCode::F20 => VK_F20,
|
||||
KeyCode::F21 => VK_F21,
|
||||
KeyCode::F22 => VK_F22,
|
||||
KeyCode::F23 => VK_F23,
|
||||
KeyCode::F24 => VK_F24,
|
||||
KeyCode::F25 => 0,
|
||||
KeyCode::F26 => 0,
|
||||
KeyCode::F27 => 0,
|
||||
KeyCode::F28 => 0,
|
||||
KeyCode::F29 => 0,
|
||||
KeyCode::F30 => 0,
|
||||
KeyCode::F31 => 0,
|
||||
KeyCode::F32 => 0,
|
||||
KeyCode::F33 => 0,
|
||||
KeyCode::F34 => 0,
|
||||
KeyCode::F35 => 0,
|
||||
KeyCode::Unidentified(_) => 0,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// This converts virtual keys to `Key`s. Only virtual keys which can be unambiguously converted to
|
||||
/// a `Key`, with only the information passed in as arguments, are converted.
|
||||
///
|
||||
/// In other words: this function does not need to "prepare" the current layout in order to do
|
||||
/// the conversion, but as such it cannot convert certain keys, like language-specific character keys.
|
||||
///
|
||||
/// The result includes all non-character keys defined within `Key` plus characters from numpad keys.
|
||||
/// For example, backspace and tab are included.
|
||||
fn vkey_to_non_char_key(
|
||||
vkey: VIRTUAL_KEY,
|
||||
native_code: NativeKey,
|
||||
hkl: u64,
|
||||
has_alt_graph: bool,
|
||||
) -> Key {
|
||||
// List of the Web key names and their corresponding platform-native key names:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||
|
||||
let primary_lang_id = primarylangid(loword(hkl as u32));
|
||||
let is_korean = primary_lang_id as u32 == LANG_KOREAN;
|
||||
let is_japanese = primary_lang_id as u32 == LANG_JAPANESE;
|
||||
|
||||
match vkey {
|
||||
VK_LBUTTON => Key::Unidentified(NativeKey::Unidentified), // Mouse
|
||||
VK_RBUTTON => Key::Unidentified(NativeKey::Unidentified), // Mouse
|
||||
|
||||
// I don't think this can be represented with a Key
|
||||
VK_CANCEL => Key::Unidentified(native_code),
|
||||
|
||||
VK_MBUTTON => Key::Unidentified(NativeKey::Unidentified), // Mouse
|
||||
VK_XBUTTON1 => Key::Unidentified(NativeKey::Unidentified), // Mouse
|
||||
VK_XBUTTON2 => Key::Unidentified(NativeKey::Unidentified), // Mouse
|
||||
VK_BACK => Key::Backspace,
|
||||
VK_TAB => Key::Tab,
|
||||
VK_CLEAR => Key::Clear,
|
||||
VK_RETURN => Key::Enter,
|
||||
VK_SHIFT => Key::Shift,
|
||||
VK_CONTROL => Key::Control,
|
||||
VK_MENU => Key::Alt,
|
||||
VK_PAUSE => Key::Pause,
|
||||
VK_CAPITAL => Key::CapsLock,
|
||||
|
||||
//VK_HANGEUL => Key::HangulMode, // Deprecated in favour of VK_HANGUL
|
||||
|
||||
// VK_HANGUL and VK_KANA are defined as the same constant, therefore
|
||||
// we use appropriate conditions to differentate between them
|
||||
VK_HANGUL if is_korean => Key::HangulMode,
|
||||
VK_KANA if is_japanese => Key::KanaMode,
|
||||
|
||||
VK_JUNJA => Key::JunjaMode,
|
||||
VK_FINAL => Key::FinalMode,
|
||||
|
||||
// VK_HANJA and VK_KANJI are defined as the same constant, therefore
|
||||
// we use appropriate conditions to differentate between them
|
||||
VK_HANJA if is_korean => Key::HanjaMode,
|
||||
VK_KANJI if is_japanese => Key::KanjiMode,
|
||||
|
||||
VK_ESCAPE => Key::Escape,
|
||||
VK_CONVERT => Key::Convert,
|
||||
VK_NONCONVERT => Key::NonConvert,
|
||||
VK_ACCEPT => Key::Accept,
|
||||
VK_MODECHANGE => Key::ModeChange,
|
||||
VK_SPACE => Key::Space,
|
||||
VK_PRIOR => Key::PageUp,
|
||||
VK_NEXT => Key::PageDown,
|
||||
VK_END => Key::End,
|
||||
VK_HOME => Key::Home,
|
||||
VK_LEFT => Key::ArrowLeft,
|
||||
VK_UP => Key::ArrowUp,
|
||||
VK_RIGHT => Key::ArrowRight,
|
||||
VK_DOWN => Key::ArrowDown,
|
||||
VK_SELECT => Key::Select,
|
||||
VK_PRINT => Key::Print,
|
||||
VK_EXECUTE => Key::Execute,
|
||||
VK_SNAPSHOT => Key::PrintScreen,
|
||||
VK_INSERT => Key::Insert,
|
||||
VK_DELETE => Key::Delete,
|
||||
VK_HELP => Key::Help,
|
||||
VK_LWIN => Key::Super,
|
||||
VK_RWIN => Key::Super,
|
||||
VK_APPS => Key::ContextMenu,
|
||||
VK_SLEEP => Key::Standby,
|
||||
|
||||
// Numpad keys produce characters
|
||||
VK_NUMPAD0 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD1 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD2 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD3 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD4 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD5 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD6 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD7 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD8 => Key::Unidentified(native_code),
|
||||
VK_NUMPAD9 => Key::Unidentified(native_code),
|
||||
VK_MULTIPLY => Key::Unidentified(native_code),
|
||||
VK_ADD => Key::Unidentified(native_code),
|
||||
VK_SEPARATOR => Key::Unidentified(native_code),
|
||||
VK_SUBTRACT => Key::Unidentified(native_code),
|
||||
VK_DECIMAL => Key::Unidentified(native_code),
|
||||
VK_DIVIDE => Key::Unidentified(native_code),
|
||||
|
||||
VK_F1 => Key::F1,
|
||||
VK_F2 => Key::F2,
|
||||
VK_F3 => Key::F3,
|
||||
VK_F4 => Key::F4,
|
||||
VK_F5 => Key::F5,
|
||||
VK_F6 => Key::F6,
|
||||
VK_F7 => Key::F7,
|
||||
VK_F8 => Key::F8,
|
||||
VK_F9 => Key::F9,
|
||||
VK_F10 => Key::F10,
|
||||
VK_F11 => Key::F11,
|
||||
VK_F12 => Key::F12,
|
||||
VK_F13 => Key::F13,
|
||||
VK_F14 => Key::F14,
|
||||
VK_F15 => Key::F15,
|
||||
VK_F16 => Key::F16,
|
||||
VK_F17 => Key::F17,
|
||||
VK_F18 => Key::F18,
|
||||
VK_F19 => Key::F19,
|
||||
VK_F20 => Key::F20,
|
||||
VK_F21 => Key::F21,
|
||||
VK_F22 => Key::F22,
|
||||
VK_F23 => Key::F23,
|
||||
VK_F24 => Key::F24,
|
||||
VK_NAVIGATION_VIEW => Key::Unidentified(native_code),
|
||||
VK_NAVIGATION_MENU => Key::Unidentified(native_code),
|
||||
VK_NAVIGATION_UP => Key::Unidentified(native_code),
|
||||
VK_NAVIGATION_DOWN => Key::Unidentified(native_code),
|
||||
VK_NAVIGATION_LEFT => Key::Unidentified(native_code),
|
||||
VK_NAVIGATION_RIGHT => Key::Unidentified(native_code),
|
||||
VK_NAVIGATION_ACCEPT => Key::Unidentified(native_code),
|
||||
VK_NAVIGATION_CANCEL => Key::Unidentified(native_code),
|
||||
VK_NUMLOCK => Key::NumLock,
|
||||
VK_SCROLL => Key::ScrollLock,
|
||||
VK_OEM_NEC_EQUAL => Key::Unidentified(native_code),
|
||||
//VK_OEM_FJ_JISHO => Key::Unidentified(native_code), // Conflicts with `VK_OEM_NEC_EQUAL`
|
||||
VK_OEM_FJ_MASSHOU => Key::Unidentified(native_code),
|
||||
VK_OEM_FJ_TOUROKU => Key::Unidentified(native_code),
|
||||
VK_OEM_FJ_LOYA => Key::Unidentified(native_code),
|
||||
VK_OEM_FJ_ROYA => Key::Unidentified(native_code),
|
||||
VK_LSHIFT => Key::Shift,
|
||||
VK_RSHIFT => Key::Shift,
|
||||
VK_LCONTROL => Key::Control,
|
||||
VK_RCONTROL => Key::Control,
|
||||
VK_LMENU => Key::Alt,
|
||||
VK_RMENU => {
|
||||
if has_alt_graph {
|
||||
Key::AltGraph
|
||||
} else {
|
||||
Key::Alt
|
||||
}
|
||||
}
|
||||
VK_BROWSER_BACK => Key::BrowserBack,
|
||||
VK_BROWSER_FORWARD => Key::BrowserForward,
|
||||
VK_BROWSER_REFRESH => Key::BrowserRefresh,
|
||||
VK_BROWSER_STOP => Key::BrowserStop,
|
||||
VK_BROWSER_SEARCH => Key::BrowserSearch,
|
||||
VK_BROWSER_FAVORITES => Key::BrowserFavorites,
|
||||
VK_BROWSER_HOME => Key::BrowserHome,
|
||||
VK_VOLUME_MUTE => Key::AudioVolumeMute,
|
||||
VK_VOLUME_DOWN => Key::AudioVolumeDown,
|
||||
VK_VOLUME_UP => Key::AudioVolumeUp,
|
||||
VK_MEDIA_NEXT_TRACK => Key::MediaTrackNext,
|
||||
VK_MEDIA_PREV_TRACK => Key::MediaTrackPrevious,
|
||||
VK_MEDIA_STOP => Key::MediaStop,
|
||||
VK_MEDIA_PLAY_PAUSE => Key::MediaPlayPause,
|
||||
VK_LAUNCH_MAIL => Key::LaunchMail,
|
||||
VK_LAUNCH_MEDIA_SELECT => Key::LaunchMediaPlayer,
|
||||
VK_LAUNCH_APP1 => Key::LaunchApplication1,
|
||||
VK_LAUNCH_APP2 => Key::LaunchApplication2,
|
||||
|
||||
// This function only converts "non-printable"
|
||||
VK_OEM_1 => Key::Unidentified(native_code),
|
||||
VK_OEM_PLUS => Key::Unidentified(native_code),
|
||||
VK_OEM_COMMA => Key::Unidentified(native_code),
|
||||
VK_OEM_MINUS => Key::Unidentified(native_code),
|
||||
VK_OEM_PERIOD => Key::Unidentified(native_code),
|
||||
VK_OEM_2 => Key::Unidentified(native_code),
|
||||
VK_OEM_3 => Key::Unidentified(native_code),
|
||||
|
||||
VK_GAMEPAD_A => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_B => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_X => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_Y => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_RIGHT_SHOULDER => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_LEFT_SHOULDER => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_LEFT_TRIGGER => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_RIGHT_TRIGGER => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_DPAD_UP => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_DPAD_DOWN => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_DPAD_LEFT => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_DPAD_RIGHT => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_MENU => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_VIEW => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_LEFT_THUMBSTICK_BUTTON => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_RIGHT_THUMBSTICK_BUTTON => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_LEFT_THUMBSTICK_UP => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_LEFT_THUMBSTICK_DOWN => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_LEFT_THUMBSTICK_RIGHT => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_LEFT_THUMBSTICK_LEFT => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_RIGHT_THUMBSTICK_UP => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_RIGHT_THUMBSTICK_DOWN => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_RIGHT_THUMBSTICK_RIGHT => Key::Unidentified(native_code),
|
||||
VK_GAMEPAD_RIGHT_THUMBSTICK_LEFT => Key::Unidentified(native_code),
|
||||
|
||||
// This function only converts "non-printable"
|
||||
VK_OEM_4 => Key::Unidentified(native_code),
|
||||
VK_OEM_5 => Key::Unidentified(native_code),
|
||||
VK_OEM_6 => Key::Unidentified(native_code),
|
||||
VK_OEM_7 => Key::Unidentified(native_code),
|
||||
VK_OEM_8 => Key::Unidentified(native_code),
|
||||
VK_OEM_AX => Key::Unidentified(native_code),
|
||||
VK_OEM_102 => Key::Unidentified(native_code),
|
||||
|
||||
VK_ICO_HELP => Key::Unidentified(native_code),
|
||||
VK_ICO_00 => Key::Unidentified(native_code),
|
||||
|
||||
VK_PROCESSKEY => Key::Process,
|
||||
|
||||
VK_ICO_CLEAR => Key::Unidentified(native_code),
|
||||
VK_PACKET => Key::Unidentified(native_code),
|
||||
VK_OEM_RESET => Key::Unidentified(native_code),
|
||||
VK_OEM_JUMP => Key::Unidentified(native_code),
|
||||
VK_OEM_PA1 => Key::Unidentified(native_code),
|
||||
VK_OEM_PA2 => Key::Unidentified(native_code),
|
||||
VK_OEM_PA3 => Key::Unidentified(native_code),
|
||||
VK_OEM_WSCTRL => Key::Unidentified(native_code),
|
||||
VK_OEM_CUSEL => Key::Unidentified(native_code),
|
||||
|
||||
VK_OEM_ATTN => Key::Attn,
|
||||
VK_OEM_FINISH => {
|
||||
if is_japanese {
|
||||
Key::Katakana
|
||||
} else {
|
||||
// This matches IE and Firefox behaviour according to
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||
// At the time of writing, there is no `Key::Finish` variant as
|
||||
// Finish is not mentionned at https://w3c.github.io/uievents-key/
|
||||
// Also see: https://github.com/pyfisch/keyboard-types/issues/9
|
||||
Key::Unidentified(native_code)
|
||||
}
|
||||
}
|
||||
VK_OEM_COPY => Key::Copy,
|
||||
VK_OEM_AUTO => Key::Hankaku,
|
||||
VK_OEM_ENLW => Key::Zenkaku,
|
||||
VK_OEM_BACKTAB => Key::Romaji,
|
||||
VK_ATTN => Key::KanaMode,
|
||||
VK_CRSEL => Key::CrSel,
|
||||
VK_EXSEL => Key::ExSel,
|
||||
VK_EREOF => Key::EraseEof,
|
||||
VK_PLAY => Key::Play,
|
||||
VK_ZOOM => Key::ZoomToggle,
|
||||
VK_NONAME => Key::Unidentified(native_code),
|
||||
VK_PA1 => Key::Unidentified(native_code),
|
||||
VK_OEM_CLEAR => Key::Clear,
|
||||
_ => Key::Unidentified(native_code),
|
||||
}
|
||||
}
|
||||
67
src/platform_impl/windows/minimal_ime.rs
Normal file
67
src/platform_impl/windows/minimal_ime.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering::Relaxed},
|
||||
Mutex,
|
||||
};
|
||||
|
||||
use winapi::{
|
||||
shared::{
|
||||
minwindef::{LPARAM, WPARAM},
|
||||
windef::HWND,
|
||||
},
|
||||
um::winuser,
|
||||
};
|
||||
|
||||
use crate::platform_impl::platform::{event_loop::ProcResult, keyboard::next_kbd_msg};
|
||||
|
||||
pub struct MinimalIme {
|
||||
// True if we're currently receiving messages belonging to a finished IME session.
|
||||
getting_ime_text: AtomicBool,
|
||||
|
||||
utf16parts: Mutex<Vec<u16>>,
|
||||
}
|
||||
impl Default for MinimalIme {
|
||||
fn default() -> Self {
|
||||
MinimalIme {
|
||||
getting_ime_text: AtomicBool::new(false),
|
||||
utf16parts: Mutex::new(Vec::with_capacity(16)),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl MinimalIme {
|
||||
pub(crate) fn process_message(
|
||||
&self,
|
||||
hwnd: HWND,
|
||||
msg_kind: u32,
|
||||
wparam: WPARAM,
|
||||
_lparam: LPARAM,
|
||||
result: &mut ProcResult,
|
||||
) -> Option<String> {
|
||||
match msg_kind {
|
||||
winuser::WM_IME_ENDCOMPOSITION => {
|
||||
self.getting_ime_text.store(true, Relaxed);
|
||||
}
|
||||
winuser::WM_CHAR | winuser::WM_SYSCHAR => {
|
||||
if self.getting_ime_text.load(Relaxed) {
|
||||
*result = ProcResult::Value(0);
|
||||
self.utf16parts.lock().unwrap().push(wparam as u16);
|
||||
// It's important that we push the new character and release the lock
|
||||
// before getting the next message
|
||||
let next_msg = next_kbd_msg(hwnd);
|
||||
let more_char_coming = next_msg
|
||||
.map(|m| matches!(m.message, winuser::WM_CHAR | winuser::WM_SYSCHAR))
|
||||
.unwrap_or(false);
|
||||
if !more_char_coming {
|
||||
let mut utf16parts = self.utf16parts.lock().unwrap();
|
||||
let result = String::from_utf16(&utf16parts).ok();
|
||||
utf16parts.clear();
|
||||
self.getting_ime_text.store(false, Relaxed);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#![cfg(windows_platform)]
|
||||
|
||||
use smol_str::SmolStr;
|
||||
use windows_sys::Win32::{
|
||||
Foundation::{HANDLE, HWND},
|
||||
UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX},
|
||||
|
|
@ -19,6 +20,7 @@ pub(self) use crate::platform_impl::Fullscreen;
|
|||
|
||||
use crate::event::DeviceId as RootDeviceId;
|
||||
use crate::icon::Icon;
|
||||
use crate::keyboard::Key;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
|
|
@ -82,6 +84,12 @@ fn wrap_device_id(id: u32) -> RootDeviceId {
|
|||
|
||||
pub type OsError = std::io::Error;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct KeyEventExtra {
|
||||
pub text_with_all_modifers: Option<SmolStr>,
|
||||
pub key_without_modifiers: Key,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(HWND);
|
||||
unsafe impl Send for WindowId {}
|
||||
|
|
@ -127,7 +135,12 @@ const fn get_y_lparam(x: u32) -> i16 {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
const fn loword(x: u32) -> u16 {
|
||||
pub(crate) const fn primarylangid(lgid: u16) -> u16 {
|
||||
lgid & 0x3FF
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) const fn loword(x: u32) -> u16 {
|
||||
(x & 0xFFFF) as u16
|
||||
}
|
||||
|
||||
|
|
@ -162,10 +175,11 @@ mod dark_mode;
|
|||
mod definitions;
|
||||
mod dpi;
|
||||
mod drop_handler;
|
||||
mod event;
|
||||
mod event_loop;
|
||||
mod icon;
|
||||
mod ime;
|
||||
mod keyboard;
|
||||
mod keyboard_layout;
|
||||
mod monitor;
|
||||
mod raw_input;
|
||||
mod window;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ use raw_window_handle::{
|
|||
use std::{
|
||||
cell::Cell,
|
||||
ffi::c_void,
|
||||
io, mem, panic, ptr,
|
||||
io,
|
||||
mem::{self, MaybeUninit},
|
||||
panic, ptr,
|
||||
sync::{mpsc::channel, Arc, Mutex, MutexGuard},
|
||||
};
|
||||
|
||||
|
|
@ -32,9 +34,9 @@ use windows_sys::Win32::{
|
|||
UI::{
|
||||
Input::{
|
||||
KeyboardAndMouse::{
|
||||
EnableWindow, GetActiveWindow, MapVirtualKeyW, ReleaseCapture, SendInput, INPUT,
|
||||
INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP,
|
||||
MAPVK_VK_TO_VSC, VK_LMENU, VK_MENU,
|
||||
EnableWindow, GetActiveWindow, MapVirtualKeyW, ReleaseCapture, SendInput,
|
||||
ToUnicode, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYEVENTF_EXTENDEDKEY,
|
||||
KEYEVENTF_KEYUP, MAPVK_VK_TO_VSC, VIRTUAL_KEY, VK_LMENU, VK_MENU, VK_SPACE,
|
||||
},
|
||||
Touch::{RegisterTouchWindow, TWF_WANTPALM},
|
||||
},
|
||||
|
|
@ -65,6 +67,7 @@ use crate::{
|
|||
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID},
|
||||
icon::{self, IconType},
|
||||
ime::ImeContext,
|
||||
keyboard::KeyEventBuilder,
|
||||
monitor::{self, MonitorHandle},
|
||||
util,
|
||||
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
|
||||
|
|
@ -833,6 +836,26 @@ impl Window {
|
|||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn reset_dead_keys(&self) {
|
||||
// `ToUnicode` consumes the dead-key by default, so we are constructing a fake (but valid)
|
||||
// key input which we can call `ToUnicode` with.
|
||||
unsafe {
|
||||
let vk = VK_SPACE as VIRTUAL_KEY;
|
||||
let scancode = MapVirtualKeyW(vk as u32, MAPVK_VK_TO_VSC);
|
||||
let kbd_state = [0; 256];
|
||||
let mut char_buff = [MaybeUninit::uninit(); 8];
|
||||
ToUnicode(
|
||||
vk as u32,
|
||||
scancode,
|
||||
kbd_state.as_ptr(),
|
||||
char_buff[0].as_mut_ptr(),
|
||||
char_buff.len() as i32,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
|
|
@ -950,6 +973,7 @@ impl<'a, T: 'static> InitData<'a, T> {
|
|||
event_loop::WindowData {
|
||||
window_state: win.window_state.clone(),
|
||||
event_loop_runner: self.event_loop.runner_shared.clone(),
|
||||
key_event_builder: KeyEventBuilder::default(),
|
||||
_file_drop_handler: file_drop_handler,
|
||||
userdata_removed: Cell::new(false),
|
||||
recurse_depth: Cell::new(0),
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Size},
|
||||
event::ModifiersState,
|
||||
icon::Icon,
|
||||
keyboard::ModifiersState,
|
||||
platform_impl::platform::{event_loop, util, Fullscreen},
|
||||
window::{CursorIcon, Theme, WindowAttributes},
|
||||
};
|
||||
|
|
@ -42,7 +42,7 @@ pub(crate) struct WindowState {
|
|||
pub fullscreen: Option<Fullscreen>,
|
||||
pub current_theme: Theme,
|
||||
pub preferred_theme: Option<Theme>,
|
||||
pub high_surrogate: Option<u16>,
|
||||
|
||||
pub window_flags: WindowFlags,
|
||||
|
||||
pub ime_state: ImeState,
|
||||
|
|
@ -157,7 +157,6 @@ impl WindowState {
|
|||
fullscreen: None,
|
||||
current_theme,
|
||||
preferred_theme,
|
||||
high_surrogate: None,
|
||||
window_flags: WindowFlags::empty(),
|
||||
|
||||
ime_state: ImeState::Disabled,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue