diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 46e695fae2..38cf83f1d2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -75,7 +75,6 @@ jobs:
env:
RUST_BACKTRACE: 1
CARGO_INCREMENTAL: 0
- PKG_CONFIG_ALLOW_CROSS: 1
RUSTFLAGS: "-C debuginfo=0 --deny warnings"
OPTIONS: ${{ matrix.platform.options }}
FEATURES: ${{ format(',{0}', matrix.platform.features ) }}
@@ -98,12 +97,9 @@ jobs:
targets: ${{ matrix.platform.target }}
components: clippy
- - name: Install Linux dependencies
- if: (matrix.platform.os == 'ubuntu-latest')
- run: sudo apt-get update && sudo apt-get install pkg-config libxkbcommon-dev
- name: Install GCC Multilib
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
- run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install g++-multilib gcc-multilib libxkbcommon-dev:i386
+ run: sudo apt-get update && sudo apt-get install gcc-multilib
- name: Install cargo-apk
if: contains(matrix.platform.target, 'android')
run: cargo install cargo-apk
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e7c1facc4b..3e50ccab76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,34 @@ And please only add new entries to the top of this list, right below the `# Unre
# Unreleased
+- **Breaking:** Remove all deprecated `modifiers` fields.
+- **Breaking:** Overhaul keyboard input handling.
+ - Replace `KeyboardInput` with `KeyEvent` and `RawKeyEvent`.
+ - Change `WindowEvent::KeyboardInput` to contain a `KeyEvent`.
+ - Change `Event::Key` to contain a `RawKeyEvent`.
+ - Remove `Event::ReceivedCharacter`. In its place, you should use
+ `KeyEvent.text` in combination with `WindowEvent::Ime`.
+ - Replace `VirtualKeyCode` with the `Key` enum.
+ - Replace `ScanCode` with the `KeyCode` enum.
+ - Rename `ModifiersState::LOGO` to `SUPER` and `ModifiersState::CTRL` to `CONTROL`.
+ - Add `KeyCode` to refer to keys (roughly) by their physical location.
+ - Add `NativeKeyCode` to represent raw `KeyCode`s which Winit doesn't
+ understand.
+ - Add `Key` to represent the keys after they've been interpreted by the
+ active (software) keyboard layout.
+ - Add `NativeKey` to represent raw `Key`s which Winit doesn't understand.
+ - Add `KeyLocation` to tell apart `Key`s which usually "mean" the same thing,
+ but can appear simultanesouly in different spots on the same keyboard
+ layout.
+ - Add `Window::reset_dead_keys` to enable application-controlled cancellation
+ of dead key sequences.
+ - Add `KeyEventExtModifierSupplement` to expose additional (and less
+ portable) interpretations of a given key-press.
+ - Add `KeyCodeExtScancode`, which lets you convert between raw keycodes and
+ `KeyCode`.
+ - Remove `WindowExtMacOS::option_as_alt` and `WindowExtMacOS::set_option_as_alt`.
+ - `ModifiersChanged` now uses dedicated `Modifiers` struct.
+- On Orbital, fix `ModifiersChanged` not being sent.
- **Breaking:** `CursorIcon` is now used from the `cursor-icon` crate.
- **Breaking:** `CursorIcon::Hand` is now named `CursorIcon::Pointer`.
- **Breaking:** `CursorIcon::Arrow` was removed.
diff --git a/Cargo.toml b/Cargo.toml
index c5f9f4066d..5d9523bdd3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,15 +36,15 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
-x11 = ["x11-dl", "mio", "percent-encoding"]
-wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv"]
+x11 = ["x11-dl", "mio", "percent-encoding", "xkbcommon-dl/x11"]
+wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv", "memmap2"]
wayland-dlopen = ["wayland-backend/dlopen"]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
android-native-activity = ["android-activity/native-activity"]
android-game-activity = ["android-activity/game-activity"]
-serde = ["dep:serde", "cursor-icon/serde"]
+serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
[build-dependencies]
cfg_aliases = "0.1.1"
@@ -58,6 +58,7 @@ mint = { version = "0.5.6", optional = true }
once_cell = "1.12"
raw_window_handle = { package = "raw-window-handle", version = "0.5" }
serde = { version = "1", optional = true, features = ["serde_derive"] }
+smol_str = "0.2.0"
[dev-dependencies]
image = { version = "0.24.0", default-features = false, features = ["png"] }
@@ -67,6 +68,7 @@ simple_logger = { version = "2.1.0", default_features = false }
# Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995
android-activity = "0.4.0"
ndk = "0.7.0"
+ndk-sys = "0.4.0"
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
core-foundation = "0.9.3"
@@ -76,6 +78,9 @@ objc2 = ">=0.3.0-beta.3, <0.3.0-beta.4" # Allow `0.3.0-beta.3.patch-leaks`
core-graphics = "0.22.3"
dispatch = "0.2.0"
+[target.'cfg(target_os = "windows")'.dependencies]
+unicode-segmentation = "1.7.1"
+
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.45"
features = [
@@ -110,13 +115,15 @@ libc = "0.2.64"
mio = { version = "0.8", features = ["os-ext"], optional = true }
percent-encoding = { version = "2.0", optional = true }
fnv = { version = "1.0.3", optional = true }
-sctk = { package = "smithay-client-toolkit", version = "0.17.0", optional = true }
+sctk = { package = "smithay-client-toolkit", version = "0.17.0", default-features = false, features = ["calloop"], optional = true }
sctk-adwaita = { version = "0.6.0", default_features = false, optional = true }
wayland-client = { version = "0.30.0", optional = true }
wayland-backend = { version = "0.1.0", default_features = false, features = ["client_system"], optional = true }
wayland-protocols = { version = "0.30.0", features = [ "staging"], optional = true }
calloop = "0.10.5"
x11-dl = { version = "2.18.5", optional = true }
+xkbcommon-dl = { git = "https://github.com/maroider/xkbcommon-dl", rev = "900832888ad6f11011d1369befb344a9aa8a9610" }
+memmap2 = { version = "0.5.0", optional = true }
[target.'cfg(target_os = "redox")'.dependencies]
orbclient = { version = "0.3.42", default-features = false }
diff --git a/FEATURES.md b/FEATURES.md
index e2693e1964..04155b5dc8 100644
--- a/FEATURES.md
+++ b/FEATURES.md
@@ -222,9 +222,9 @@ Changes in the API that have been agreed upon but aren't implemented across all
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
-|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
-|Event Loop 2.0 ([#459]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
-|Keyboard Input ([#812]) |❌ |❌ |❌ |❌ |❌ |❌ |❓ |❓ |
+|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
+|Event Loop 2.0 ([#459]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |✔️ |
+|Keyboard Input 2.0 ([#753]) |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
### Completed API Reworks
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
@@ -243,5 +243,5 @@ Changes in the API that have been agreed upon but aren't implemented across all
[#720]: https://github.com/rust-windowing/winit/issues/720
[#721]: https://github.com/rust-windowing/winit/issues/721
[#750]: https://github.com/rust-windowing/winit/issues/750
+[#753]: https://github.com/rust-windowing/winit/issues/753
[#804]: https://github.com/rust-windowing/winit/issues/804
-[#812]: https://github.com/rust-windowing/winit/issues/812
diff --git a/docs/res/ATTRIBUTION.md b/docs/res/ATTRIBUTION.md
new file mode 100644
index 0000000000..25f51d683e
--- /dev/null
+++ b/docs/res/ATTRIBUTION.md
@@ -0,0 +1,11 @@
+# Image Attribution
+
+These images are used in the documentation of `winit`.
+
+## keyboard_*.svg
+
+These files are a modified version of "[ANSI US QWERTY (Windows)](https://commons.wikimedia.org/wiki/File:ANSI_US_QWERTY_(Windows).svg)"
+by [Tomiĉo] (https://commons.wikimedia.org/wiki/User:Tomi%C4%89o). It is
+originally released under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
+License. Minor modifications have been made by [John Nunley](https://github.com/notgull),
+which have been released under the same license as a derivative work.
diff --git a/docs/res/keyboard_left_shift_key.svg b/docs/res/keyboard_left_shift_key.svg
new file mode 100644
index 0000000000..bae6f9af1f
--- /dev/null
+++ b/docs/res/keyboard_left_shift_key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/res/keyboard_numpad_1_key.svg b/docs/res/keyboard_numpad_1_key.svg
new file mode 100644
index 0000000000..d5957581c8
--- /dev/null
+++ b/docs/res/keyboard_numpad_1_key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/res/keyboard_right_shift_key.svg b/docs/res/keyboard_right_shift_key.svg
new file mode 100644
index 0000000000..dc016f05ca
--- /dev/null
+++ b/docs/res/keyboard_right_shift_key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/res/keyboard_standard_1_key.svg b/docs/res/keyboard_standard_1_key.svg
new file mode 100644
index 0000000000..3520d5500a
--- /dev/null
+++ b/docs/res/keyboard_standard_1_key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/child_window.rs b/examples/child_window.rs
index b1b8f95e82..bc103341a1 100644
--- a/examples/child_window.rs
+++ b/examples/child_window.rs
@@ -5,7 +5,7 @@ fn main() {
use raw_window_handle::HasRawWindowHandle;
use winit::{
dpi::{LogicalPosition, LogicalSize, Position},
- event::{ElementState, Event, KeyboardInput, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
window::{Window, WindowBuilder, WindowId},
};
@@ -59,8 +59,8 @@ fn main() {
println!("cursor entered in the window {window_id:?}");
}
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
+ event:
+ KeyEvent {
state: ElementState::Pressed,
..
},
diff --git a/examples/control_flow.rs b/examples/control_flow.rs
index bade79d279..541f13ad8d 100644
--- a/examples/control_flow.rs
+++ b/examples/control_flow.rs
@@ -4,8 +4,9 @@ use std::{thread, time};
use simple_logger::SimpleLogger;
use winit::{
- event::{Event, KeyboardInput, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
+ keyboard::Key,
window::WindowBuilder,
};
@@ -40,7 +41,7 @@ fn main() {
let mut close_requested = false;
event_loop.run(move |event, _, control_flow| {
- use winit::event::{ElementState, StartCause, VirtualKeyCode};
+ use winit::event::StartCause;
println!("{event:?}");
match event {
Event::NewEvents(start_cause) => {
@@ -54,31 +55,33 @@ fn main() {
close_requested = true;
}
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- virtual_keycode: Some(virtual_code),
+ event:
+ KeyEvent {
+ logical_key: key,
state: ElementState::Pressed,
..
},
..
- } => match virtual_code {
- VirtualKeyCode::Key1 => {
+ } => match key.as_ref() {
+ // WARNING: Consider using `key_without_modifers()` if available on your platform.
+ // See the `key_binding` example
+ Key::Character("1") => {
mode = Mode::Wait;
println!("\nmode: {mode:?}\n");
}
- VirtualKeyCode::Key2 => {
+ Key::Character("2") => {
mode = Mode::WaitUntil;
println!("\nmode: {mode:?}\n");
}
- VirtualKeyCode::Key3 => {
+ Key::Character("3") => {
mode = Mode::Poll;
println!("\nmode: {mode:?}\n");
}
- VirtualKeyCode::R => {
+ Key::Character("r") => {
request_redraw = !request_redraw;
println!("\nrequest_redraw: {request_redraw}\n");
}
- VirtualKeyCode::Escape => {
+ Key::Escape => {
close_requested = true;
}
_ => (),
diff --git a/examples/cursor.rs b/examples/cursor.rs
index 93b69d77ff..ebfa816c5f 100644
--- a/examples/cursor.rs
+++ b/examples/cursor.rs
@@ -2,7 +2,7 @@
use simple_logger::SimpleLogger;
use winit::{
- event::{ElementState, Event, KeyboardInput, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
window::{CursorIcon, WindowBuilder},
};
@@ -23,8 +23,8 @@ fn main() {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
+ event:
+ KeyEvent {
state: ElementState::Pressed,
..
},
diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs
index 79253e106a..9759fa9852 100644
--- a/examples/cursor_grab.rs
+++ b/examples/cursor_grab.rs
@@ -2,8 +2,9 @@
use simple_logger::SimpleLogger;
use winit::{
- event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
+ event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
+ keyboard::{Key, ModifiersState},
window::{CursorGrabMode, WindowBuilder},
};
@@ -25,27 +26,29 @@ fn main() {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => control_flow.set_exit(),
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
+ event:
+ KeyEvent {
+ logical_key: key,
state: ElementState::Released,
- virtual_keycode: Some(key),
..
},
..
} => {
- use winit::event::VirtualKeyCode::*;
let result = match key {
- Escape => {
+ Key::Escape => {
control_flow.set_exit();
Ok(())
}
- G => window.set_cursor_grab(CursorGrabMode::Confined),
- L => window.set_cursor_grab(CursorGrabMode::Locked),
- A => window.set_cursor_grab(CursorGrabMode::None),
- H => {
- window.set_cursor_visible(modifiers.shift());
- Ok(())
- }
+ Key::Character(ch) => match ch.to_lowercase().as_str() {
+ "g" => window.set_cursor_grab(CursorGrabMode::Confined),
+ "l" => window.set_cursor_grab(CursorGrabMode::Locked),
+ "a" => window.set_cursor_grab(CursorGrabMode::None),
+ "h" => {
+ window.set_cursor_visible(modifiers.shift_key());
+ Ok(())
+ }
+ _ => Ok(()),
+ },
_ => Ok(()),
};
@@ -53,7 +56,7 @@ fn main() {
println!("error: {err}");
}
}
- WindowEvent::ModifiersChanged(m) => modifiers = m,
+ WindowEvent::ModifiersChanged(new) => modifiers = new.state(),
_ => (),
},
Event::DeviceEvent { event, .. } => match event {
diff --git a/examples/drag_window.rs b/examples/drag_window.rs
index 813e9b00c9..3bc691523c 100644
--- a/examples/drag_window.rs
+++ b/examples/drag_window.rs
@@ -2,10 +2,9 @@
use simple_logger::SimpleLogger;
use winit::{
- event::{
- ElementState, Event, KeyboardInput, MouseButton, StartCause, VirtualKeyCode, WindowEvent,
- },
+ event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
event_loop::EventLoop,
+ keyboard::Key,
window::{Window, WindowBuilder, WindowId},
};
@@ -45,14 +44,14 @@ fn main() {
name_windows(entered_id, switched, &window_1, &window_2)
}
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
+ event:
+ KeyEvent {
state: ElementState::Released,
- virtual_keycode: Some(VirtualKeyCode::X),
+ logical_key: Key::Character(c),
..
},
..
- } => {
+ } if c == "x" => {
switched = !switched;
name_windows(entered_id, switched, &window_1, &window_2);
println!("Switched!")
diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs
index a71d079f28..ab2d0582a0 100644
--- a/examples/fullscreen.rs
+++ b/examples/fullscreen.rs
@@ -1,8 +1,9 @@
#![allow(clippy::single_match)]
use simple_logger::SimpleLogger;
-use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
+use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::EventLoop;
+use winit::keyboard::Key;
use winit::window::{Fullscreen, WindowBuilder};
#[cfg(target_os = "macos")]
@@ -50,68 +51,74 @@ fn main() {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => control_flow.set_exit(),
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- virtual_keycode: Some(virtual_code),
+ event:
+ KeyEvent {
+ logical_key: key,
state: ElementState::Pressed,
..
},
..
- } => match virtual_code {
- VirtualKeyCode::Escape => control_flow.set_exit(),
- VirtualKeyCode::F | VirtualKeyCode::B if window.fullscreen().is_some() => {
- window.set_fullscreen(None);
- }
- VirtualKeyCode::F => {
- let fullscreen = Some(Fullscreen::Exclusive(mode.clone()));
- println!("Setting mode: {fullscreen:?}");
- window.set_fullscreen(fullscreen);
- }
- VirtualKeyCode::B => {
- let fullscreen = Some(Fullscreen::Borderless(Some(monitor.clone())));
- println!("Setting mode: {fullscreen:?}");
- window.set_fullscreen(fullscreen);
- }
- #[cfg(target_os = "macos")]
- VirtualKeyCode::C => {
- window.set_simple_fullscreen(!window.simple_fullscreen());
- }
- VirtualKeyCode::S => {
- monitor_index += 1;
- if let Some(mon) = elwt.available_monitors().nth(monitor_index) {
- monitor = mon;
- } else {
- monitor_index = 0;
- monitor = elwt.available_monitors().next().expect("no monitor found!");
+ } => match key {
+ Key::Escape => control_flow.set_exit(),
+ // WARNING: Consider using `key_without_modifers()` if available on your platform.
+ // See the `key_binding` example
+ Key::Character(ch) => match ch.to_lowercase().as_str() {
+ "f" | "b" if window.fullscreen().is_some() => {
+ window.set_fullscreen(None);
}
- println!("Monitor: {:?}", monitor.name());
+ "f" => {
+ let fullscreen = Some(Fullscreen::Exclusive(mode.clone()));
+ println!("Setting mode: {fullscreen:?}");
+ window.set_fullscreen(fullscreen);
+ }
+ "b" => {
+ let fullscreen = Some(Fullscreen::Borderless(Some(monitor.clone())));
+ println!("Setting mode: {fullscreen:?}");
+ window.set_fullscreen(fullscreen);
+ }
+ #[cfg(target_os = "macos")]
+ "c" => {
+ window.set_simple_fullscreen(!window.simple_fullscreen());
+ }
+ "s" => {
+ monitor_index += 1;
+ if let Some(mon) = elwt.available_monitors().nth(monitor_index) {
+ monitor = mon;
+ } else {
+ monitor_index = 0;
+ monitor =
+ elwt.available_monitors().next().expect("no monitor found!");
+ }
+ println!("Monitor: {:?}", monitor.name());
- mode_index = 0;
- mode = monitor.video_modes().next().expect("no mode found");
- println!("Mode: {mode}");
- }
- VirtualKeyCode::M => {
- mode_index += 1;
- if let Some(m) = monitor.video_modes().nth(mode_index) {
- mode = m;
- } else {
mode_index = 0;
mode = monitor.video_modes().next().expect("no mode found");
+ println!("Mode: {mode}");
+ }
+ "m" => {
+ mode_index += 1;
+ if let Some(m) = monitor.video_modes().nth(mode_index) {
+ mode = m;
+ } else {
+ mode_index = 0;
+ mode = monitor.video_modes().next().expect("no mode found");
+ }
+ println!("Mode: {mode}");
+ }
+ "d" => {
+ decorations = !decorations;
+ window.set_decorations(decorations);
+ }
+ "x" => {
+ let is_maximized = window.is_maximized();
+ window.set_maximized(!is_maximized);
+ }
+ "z" => {
+ minimized = !minimized;
+ window.set_minimized(minimized);
}
- println!("Mode: {mode}");
- }
- VirtualKeyCode::D => {
- decorations = !decorations;
- window.set_decorations(decorations);
- }
- VirtualKeyCode::X => {
- let is_maximized = window.is_maximized();
- window.set_maximized(!is_maximized);
- }
- VirtualKeyCode::Z => {
- minimized = !minimized;
- window.set_minimized(minimized);
- }
+ _ => (),
+ },
_ => (),
},
_ => (),
diff --git a/examples/handling_close.rs b/examples/handling_close.rs
index 1fe4ad3708..11d6fc5848 100644
--- a/examples/handling_close.rs
+++ b/examples/handling_close.rs
@@ -2,8 +2,9 @@
use simple_logger::SimpleLogger;
use winit::{
- event::{Event, KeyboardInput, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
+ keyboard::Key,
window::WindowBuilder,
};
@@ -19,10 +20,6 @@ fn main() {
let mut close_requested = false;
event_loop.run(move |event, _, control_flow| {
- use winit::event::{
- ElementState::Released,
- VirtualKeyCode::{N, Y},
- };
control_flow.set_wait();
match event {
@@ -46,16 +43,18 @@ fn main() {
// the Y key.
}
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- virtual_keycode: Some(virtual_code),
- state: Released,
+ event:
+ KeyEvent {
+ logical_key: key,
+ state: ElementState::Released,
..
},
..
} => {
- match virtual_code {
- Y => {
+ // WARNING: Consider using `key_without_modifers()` if available on your platform.
+ // See the `key_binding` example
+ match key.as_ref() {
+ Key::Character("y") => {
if close_requested {
// This is where you'll want to do any cleanup you need.
println!("Buh-bye!");
@@ -68,7 +67,7 @@ fn main() {
control_flow.set_exit();
}
}
- N => {
+ Key::Character("n") => {
if close_requested {
println!("Your window will continue to stay by your side.");
close_requested = false;
diff --git a/examples/ime.rs b/examples/ime.rs
index 59f43d4a33..d9833851db 100644
--- a/examples/ime.rs
+++ b/examples/ime.rs
@@ -4,8 +4,9 @@ use log::LevelFilter;
use simple_logger::SimpleLogger;
use winit::{
dpi::PhysicalPosition,
- event::{ElementState, Event, Ime, VirtualKeyCode, WindowEvent},
+ event::{ElementState, Event, Ime, WindowEvent},
event_loop::{ControlFlow, EventLoop},
+ keyboard::{Key, KeyCode},
window::{ImePurpose, WindowBuilder},
};
@@ -76,27 +77,17 @@ fn main() {
}
}
Event::WindowEvent {
- event: WindowEvent::ReceivedCharacter(ch),
+ event: WindowEvent::KeyboardInput { event, .. },
..
} => {
- println!("ch: {ch:?}");
- }
- Event::WindowEvent {
- event: WindowEvent::KeyboardInput { input, .. },
- ..
- } => {
- println!("key: {input:?}");
+ println!("key: {event:?}");
- if input.state == ElementState::Pressed
- && input.virtual_keycode == Some(VirtualKeyCode::F2)
- {
+ if event.state == ElementState::Pressed && event.physical_key == KeyCode::F2 {
ime_allowed = !ime_allowed;
window.set_ime_allowed(ime_allowed);
println!("\nIME allowed: {ime_allowed}\n");
}
- if input.state == ElementState::Pressed
- && input.virtual_keycode == Some(VirtualKeyCode::F3)
- {
+ if event.state == ElementState::Pressed && event.logical_key == Key::F3 {
ime_purpose = match ime_purpose {
ImePurpose::Normal => ImePurpose::Password,
ImePurpose::Password => ImePurpose::Terminal,
diff --git a/examples/key_binding.rs b/examples/key_binding.rs
new file mode 100644
index 0000000000..a768111592
--- /dev/null
+++ b/examples/key_binding.rs
@@ -0,0 +1,59 @@
+#![allow(clippy::single_match)]
+
+#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
+use winit::{
+ dpi::LogicalSize,
+ event::{ElementState, Event, WindowEvent},
+ event_loop::{ControlFlow, EventLoop},
+ keyboard::{Key, ModifiersState},
+ // WARNING: This is not available on all platforms (for example on the web).
+ platform::modifier_supplement::KeyEventExtModifierSupplement,
+ window::WindowBuilder,
+};
+
+#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
+fn main() {
+ println!("This example is not supported on this platform");
+}
+
+#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
+fn main() {
+ simple_logger::SimpleLogger::new().init().unwrap();
+ let event_loop = EventLoop::new();
+
+ let _window = WindowBuilder::new()
+ .with_inner_size(LogicalSize::new(400.0, 200.0))
+ .build(&event_loop)
+ .unwrap();
+
+ let mut modifiers = ModifiersState::default();
+
+ event_loop.run(move |event, _, control_flow| {
+ *control_flow = ControlFlow::Wait;
+
+ match event {
+ Event::WindowEvent { event, .. } => match event {
+ WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
+ WindowEvent::ModifiersChanged(new) => {
+ modifiers = new.state();
+ }
+ WindowEvent::KeyboardInput { event, .. } => {
+ if event.state == ElementState::Pressed && !event.repeat {
+ match event.key_without_modifiers().as_ref() {
+ Key::Character("1") => {
+ if modifiers.shift_key() {
+ println!("Shift + 1 | logical_key: {:?}", event.logical_key);
+ } else {
+ println!("1");
+ }
+ }
+ _ => (),
+ }
+ }
+ }
+ _ => (),
+ },
+ _ => (),
+ };
+ });
+}
diff --git a/examples/multithreaded.rs b/examples/multithreaded.rs
index 7e03329d83..d1bff70f85 100644
--- a/examples/multithreaded.rs
+++ b/examples/multithreaded.rs
@@ -7,8 +7,9 @@ fn main() {
use simple_logger::SimpleLogger;
use winit::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
- event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
+ keyboard::{Key, ModifiersState},
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
};
@@ -29,6 +30,7 @@ fn main() {
let (tx, rx) = mpsc::channel();
window_senders.insert(window.id(), tx);
+ let mut modifiers = ModifiersState::default();
thread::spawn(move || {
while let Ok(event) = rx.recv() {
match event {
@@ -51,107 +53,116 @@ fn main() {
);
}
}
- #[allow(deprecated)]
+ WindowEvent::ModifiersChanged(new) => {
+ modifiers = new.state();
+ }
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
+ event:
+ KeyEvent {
state: ElementState::Released,
- virtual_keycode: Some(key),
- modifiers,
+ logical_key: key,
..
},
..
} => {
+ use Key::{ArrowLeft, ArrowRight};
window.set_title(&format!("{key:?}"));
- let state = !modifiers.shift();
- use VirtualKeyCode::*;
+ let state = !modifiers.shift_key();
match key {
- Key1 => window.set_window_level(WindowLevel::AlwaysOnTop),
- Key2 => window.set_window_level(WindowLevel::AlwaysOnBottom),
- Key3 => window.set_window_level(WindowLevel::Normal),
- C => window.set_cursor_icon(match state {
- true => CursorIcon::Progress,
- false => CursorIcon::Default,
- }),
- D => window.set_decorations(!state),
// Cycle through video modes
- Right | Left => {
+ Key::ArrowRight | Key::ArrowLeft => {
video_mode_id = match key {
- Left => video_mode_id.saturating_sub(1),
- Right => (video_modes.len() - 1).min(video_mode_id + 1),
+ ArrowLeft => video_mode_id.saturating_sub(1),
+ ArrowRight => (video_modes.len() - 1).min(video_mode_id + 1),
_ => unreachable!(),
};
println!("Picking video mode: {}", video_modes[video_mode_id]);
}
- F => window.set_fullscreen(match (state, modifiers.alt()) {
- (true, false) => Some(Fullscreen::Borderless(None)),
- (true, true) => {
- Some(Fullscreen::Exclusive(video_modes[video_mode_id].clone()))
+ // WARNING: Consider using `key_without_modifers()` if available on your platform.
+ // See the `key_binding` example
+ Key::Character(ch) => match ch.to_lowercase().as_str() {
+ "1" => window.set_window_level(WindowLevel::AlwaysOnTop),
+ "2" => window.set_window_level(WindowLevel::AlwaysOnBottom),
+ "3" => window.set_window_level(WindowLevel::Normal),
+ "c" => window.set_cursor_icon(match state {
+ true => CursorIcon::Progress,
+ false => CursorIcon::Default,
+ }),
+ "d" => window.set_decorations(!state),
+ "f" => window.set_fullscreen(match (state, modifiers.alt_key()) {
+ (true, false) => Some(Fullscreen::Borderless(None)),
+ (true, true) => Some(Fullscreen::Exclusive(
+ video_modes[video_mode_id].clone(),
+ )),
+ (false, _) => None,
+ }),
+ "l" if state => {
+ if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
+ {
+ println!("error: {err}");
+ }
}
- (false, _) => None,
- }),
- L if state => {
- if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked) {
- println!("error: {err}");
+ "g" if state => {
+ if let Err(err) =
+ window.set_cursor_grab(CursorGrabMode::Confined)
+ {
+ println!("error: {err}");
+ }
}
- }
- G if state => {
- if let Err(err) = window.set_cursor_grab(CursorGrabMode::Confined) {
- println!("error: {err}");
+ "g" | "l" if !state => {
+ if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
+ println!("error: {err}");
+ }
}
- }
- G | L if !state => {
- if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
- println!("error: {err}");
+ "h" => window.set_cursor_visible(!state),
+ "i" => {
+ println!("Info:");
+ println!("-> outer_position : {:?}", window.outer_position());
+ println!("-> inner_position : {:?}", window.inner_position());
+ println!("-> outer_size : {:?}", window.outer_size());
+ println!("-> inner_size : {:?}", window.inner_size());
+ println!("-> fullscreen : {:?}", window.fullscreen());
}
- }
- H => window.set_cursor_visible(!state),
- I => {
- println!("Info:");
- println!("-> outer_position : {:?}", window.outer_position());
- println!("-> inner_position : {:?}", window.inner_position());
- println!("-> outer_size : {:?}", window.outer_size());
- println!("-> inner_size : {:?}", window.inner_size());
- println!("-> fullscreen : {:?}", window.fullscreen());
- }
- L => window.set_min_inner_size(match state {
- true => Some(WINDOW_SIZE),
- false => None,
- }),
- M => window.set_maximized(state),
- P => window.set_outer_position({
- let mut position = window.outer_position().unwrap();
- let sign = if state { 1 } else { -1 };
- position.x += 10 * sign;
- position.y += 10 * sign;
- position
- }),
- Q => window.request_redraw(),
- R => window.set_resizable(state),
- S => window.set_inner_size(match state {
- true => PhysicalSize::new(
- WINDOW_SIZE.width + 100,
- WINDOW_SIZE.height + 100,
- ),
- false => WINDOW_SIZE,
- }),
- W => {
- if let Size::Physical(size) = WINDOW_SIZE.into() {
- window
- .set_cursor_position(Position::Physical(
- PhysicalPosition::new(
- size.width as i32 / 2,
- size.height as i32 / 2,
- ),
- ))
- .unwrap()
+ "l" => window.set_min_inner_size(match state {
+ true => Some(WINDOW_SIZE),
+ false => None,
+ }),
+ "m" => window.set_maximized(state),
+ "p" => window.set_outer_position({
+ let mut position = window.outer_position().unwrap();
+ let sign = if state { 1 } else { -1 };
+ position.x += 10 * sign;
+ position.y += 10 * sign;
+ position
+ }),
+ "q" => window.request_redraw(),
+ "r" => window.set_resizable(state),
+ "s" => window.set_inner_size(match state {
+ true => PhysicalSize::new(
+ WINDOW_SIZE.width + 100,
+ WINDOW_SIZE.height + 100,
+ ),
+ false => WINDOW_SIZE,
+ }),
+ "w" => {
+ if let Size::Physical(size) = WINDOW_SIZE.into() {
+ window
+ .set_cursor_position(Position::Physical(
+ PhysicalPosition::new(
+ size.width as i32 / 2,
+ size.height as i32 / 2,
+ ),
+ ))
+ .unwrap()
+ }
}
- }
- Z => {
- window.set_visible(false);
- thread::sleep(Duration::from_secs(1));
- window.set_visible(true);
- }
+ "z" => {
+ window.set_visible(false);
+ thread::sleep(Duration::from_secs(1));
+ window.set_visible(true);
+ }
+ _ => (),
+ },
_ => (),
}
}
@@ -170,10 +181,10 @@ fn main() {
WindowEvent::CloseRequested
| WindowEvent::Destroyed
| WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
+ event:
+ KeyEvent {
state: ElementState::Released,
- virtual_keycode: Some(VirtualKeyCode::Escape),
+ logical_key: Key::Escape,
..
},
..
diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs
index 3409af5a65..0cd505b2fe 100644
--- a/examples/multiwindow.rs
+++ b/examples/multiwindow.rs
@@ -4,8 +4,9 @@ use std::collections::HashMap;
use simple_logger::SimpleLogger;
use winit::{
- event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
+ keyboard::Key,
window::Window,
};
@@ -39,15 +40,15 @@ fn main() {
}
}
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
+ event:
+ KeyEvent {
state: ElementState::Pressed,
- virtual_keycode: Some(VirtualKeyCode::N),
+ logical_key: Key::Character(c),
..
},
is_synthetic: false,
..
- } => {
+ } if matches!(c.as_ref(), "n" | "N") => {
let window = Window::new(event_loop).unwrap();
println!("Opened a new window: {:?}", window.id());
windows.insert(window.id(), window);
diff --git a/examples/resizable.rs b/examples/resizable.rs
index 8f16172fd7..cf3dacbec2 100644
--- a/examples/resizable.rs
+++ b/examples/resizable.rs
@@ -3,8 +3,9 @@
use simple_logger::SimpleLogger;
use winit::{
dpi::LogicalSize,
- event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
+ keyboard::KeyCode,
window::WindowBuilder,
};
@@ -30,9 +31,9 @@ fn main() {
Event::WindowEvent { event, .. } => match event {
WindowEvent::CloseRequested => control_flow.set_exit(),
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- virtual_keycode: Some(VirtualKeyCode::Space),
+ event:
+ KeyEvent {
+ physical_key: KeyCode::Space,
state: ElementState::Released,
..
},
diff --git a/examples/theme.rs b/examples/theme.rs
index ac8854e2e3..a521752a0a 100644
--- a/examples/theme.rs
+++ b/examples/theme.rs
@@ -2,8 +2,9 @@
use simple_logger::SimpleLogger;
use winit::{
- event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{ControlFlow, EventLoop},
+ keyboard::Key,
window::{Theme, WindowBuilder},
};
@@ -41,25 +42,25 @@ fn main() {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- virtual_keycode: Some(key),
+ event:
+ KeyEvent {
+ logical_key: key,
state: ElementState::Pressed,
..
},
..
},
..
- } => match key {
- VirtualKeyCode::A => {
+ } => match key.as_ref() {
+ Key::Character("A" | "a") => {
println!("Theme was: {:?}", window.theme());
window.set_theme(None);
}
- VirtualKeyCode::L => {
+ Key::Character("L" | "l") => {
println!("Theme was: {:?}", window.theme());
window.set_theme(Some(Theme::Light));
}
- VirtualKeyCode::D => {
+ Key::Character("D" | "d") => {
println!("Theme was: {:?}", window.theme());
window.set_theme(Some(Theme::Dark));
}
diff --git a/examples/window_buttons.rs b/examples/window_buttons.rs
index 5d41144dbd..b7245f5a4f 100644
--- a/examples/window_buttons.rs
+++ b/examples/window_buttons.rs
@@ -5,8 +5,9 @@
use simple_logger::SimpleLogger;
use winit::{
dpi::LogicalSize,
- event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{DeviceEventFilter, EventLoop},
+ keyboard::Key,
window::{WindowBuilder, WindowButtons},
};
@@ -34,25 +35,25 @@ fn main() {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- virtual_keycode: Some(key),
+ event:
+ KeyEvent {
+ logical_key: key,
state: ElementState::Pressed,
..
},
..
},
..
- } => match key {
- VirtualKeyCode::F => {
+ } => match key.as_ref() {
+ Key::Character("F" | "f") => {
let buttons = window.enabled_buttons();
window.set_enabled_buttons(buttons ^ WindowButtons::CLOSE);
}
- VirtualKeyCode::G => {
+ Key::Character("G" | "g") => {
let buttons = window.enabled_buttons();
window.set_enabled_buttons(buttons ^ WindowButtons::MAXIMIZE);
}
- VirtualKeyCode::H => {
+ Key::Character("H" | "h") => {
let buttons = window.enabled_buttons();
window.set_enabled_buttons(buttons ^ WindowButtons::MINIMIZE);
}
diff --git a/examples/window_debug.rs b/examples/window_debug.rs
index 01077a8b6e..1a16f513d8 100644
--- a/examples/window_debug.rs
+++ b/examples/window_debug.rs
@@ -5,8 +5,9 @@
use simple_logger::SimpleLogger;
use winit::{
dpi::{LogicalSize, PhysicalSize},
- event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+ event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
event_loop::{DeviceEventFilter, EventLoop},
+ keyboard::{Key, KeyCode},
window::{Fullscreen, WindowBuilder},
};
@@ -38,23 +39,25 @@ fn main() {
control_flow.set_wait();
match event {
+ // This used to use the virtual key, but the new API
+ // only provides the `physical_key` (`Code`).
Event::DeviceEvent {
event:
- DeviceEvent::Key(KeyboardInput {
- virtual_keycode: Some(key),
- state: ElementState::Pressed,
+ DeviceEvent::Key(RawKeyEvent {
+ physical_key,
+ state: ElementState::Released,
..
}),
..
- } => match key {
- VirtualKeyCode::M => {
+ } => match physical_key {
+ KeyCode::KeyM => {
if minimized {
minimized = !minimized;
window.set_minimized(minimized);
window.focus_window();
}
}
- VirtualKeyCode::V => {
+ KeyCode::KeyV => {
if !visible {
visible = !visible;
window.set_visible(visible);
@@ -65,17 +68,19 @@ fn main() {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- virtual_keycode: Some(key),
+ event:
+ KeyEvent {
+ logical_key: Key::Character(key_str),
state: ElementState::Pressed,
..
},
..
},
..
- } => match key {
- VirtualKeyCode::E => {
+ } => match key_str.as_ref() {
+ // WARNING: Consider using `key_without_modifers()` if available on your platform.
+ // See the `key_binding` example
+ "e" => {
fn area(size: PhysicalSize) -> u32 {
size.width * size.height
}
@@ -90,7 +95,7 @@ fn main() {
eprintln!("no video modes available");
}
}
- VirtualKeyCode::F => {
+ "f" => {
if window.fullscreen().is_some() {
window.set_fullscreen(None);
} else {
@@ -98,25 +103,25 @@ fn main() {
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
}
}
- VirtualKeyCode::P => {
+ "p" => {
if window.fullscreen().is_some() {
window.set_fullscreen(None);
} else {
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
}
}
- VirtualKeyCode::M => {
+ "m" => {
minimized = !minimized;
window.set_minimized(minimized);
}
- VirtualKeyCode::Q => {
+ "q" => {
control_flow.set_exit();
}
- VirtualKeyCode::V => {
+ "v" => {
visible = !visible;
window.set_visible(visible);
}
- VirtualKeyCode::X => {
+ "x" => {
let is_maximized = window.is_maximized();
window.set_maximized(!is_maximized);
}
diff --git a/examples/window_drag_resize.rs b/examples/window_drag_resize.rs
index 95a6737791..9bc9ba741d 100644
--- a/examples/window_drag_resize.rs
+++ b/examples/window_drag_resize.rs
@@ -2,10 +2,9 @@
use simple_logger::SimpleLogger;
use winit::{
- event::{
- ElementState, Event, KeyboardInput, MouseButton, StartCause, VirtualKeyCode, WindowEvent,
- },
+ event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
+ keyboard::Key,
window::{CursorIcon, ResizeDirection, WindowBuilder},
};
@@ -53,14 +52,14 @@ fn main() {
}
}
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
+ event:
+ KeyEvent {
state: ElementState::Released,
- virtual_keycode: Some(VirtualKeyCode::B),
+ logical_key: Key::Character(c),
..
},
..
- } => {
+ } if matches!(c.as_ref(), "B" | "b") => {
border = !border;
window.set_decorations(border);
}
diff --git a/examples/window_option_as_alt.rs b/examples/window_option_as_alt.rs
deleted file mode 100644
index b7d288d5bb..0000000000
--- a/examples/window_option_as_alt.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-#![allow(clippy::single_match)]
-
-#[cfg(target_os = "macos")]
-use winit::platform::macos::{OptionAsAlt, WindowExtMacOS};
-
-#[cfg(target_os = "macos")]
-use winit::{
- event::ElementState,
- event::{Event, MouseButton, WindowEvent},
- event_loop::EventLoop,
- window::WindowBuilder,
-};
-
-/// Prints the keyboard events characters received when option_is_alt is true versus false.
-/// A left mouse click will toggle option_is_alt.
-#[cfg(target_os = "macos")]
-fn main() {
- let event_loop = EventLoop::new();
-
- let window = WindowBuilder::new()
- .with_title("A fantastic window!")
- .with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
- .build(&event_loop)
- .unwrap();
-
- let mut option_as_alt = window.option_as_alt();
-
- event_loop.run(move |event, _, control_flow| {
- control_flow.set_wait();
-
- match event {
- Event::WindowEvent {
- event: WindowEvent::CloseRequested,
- window_id,
- } if window_id == window.id() => control_flow.set_exit(),
- Event::WindowEvent { event, .. } => match event {
- WindowEvent::MouseInput {
- state: ElementState::Pressed,
- button: MouseButton::Left,
- ..
- } => {
- option_as_alt = match option_as_alt {
- OptionAsAlt::None => OptionAsAlt::OnlyLeft,
- OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
- OptionAsAlt::OnlyRight => OptionAsAlt::Both,
- OptionAsAlt::Both => OptionAsAlt::None,
- };
-
- println!("Received Mouse click, toggling option_as_alt to: {option_as_alt:?}");
- window.set_option_as_alt(option_as_alt);
- }
- WindowEvent::ReceivedCharacter(c) => println!("ReceivedCharacter: {c:?}"),
- WindowEvent::KeyboardInput { .. } => println!("KeyboardInput: {event:?}"),
- _ => (),
- },
- Event::MainEventsCleared => {
- window.request_redraw();
- }
- _ => (),
- }
- });
-}
-
-#[cfg(not(target_os = "macos"))]
-fn main() {
- println!("This example is only supported on MacOS");
-}
diff --git a/examples/window_resize_increments.rs b/examples/window_resize_increments.rs
index ce06daf22f..11522a2500 100644
--- a/examples/window_resize_increments.rs
+++ b/examples/window_resize_increments.rs
@@ -2,8 +2,9 @@ use log::debug;
use simple_logger::SimpleLogger;
use winit::{
dpi::LogicalSize,
- event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
+ event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::EventLoop,
+ keyboard::Key,
window::WindowBuilder,
};
@@ -31,9 +32,9 @@ fn main() {
Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
- input:
- KeyboardInput {
- virtual_keycode: Some(VirtualKeyCode::Space),
+ event:
+ KeyEvent {
+ logical_key: Key::Space,
state: ElementState::Released,
..
},
diff --git a/src/event.rs b/src/event.rs
index 9612368345..cd7944c163 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -35,12 +35,14 @@
//! [`EventLoop::run(...)`]: crate::event_loop::EventLoop::run
//! [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
use instant::Instant;
+use smol_str::SmolStr;
use std::path::PathBuf;
#[cfg(doc)]
use crate::window::Window;
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
+ keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState},
platform_impl,
window::{Theme, WindowId},
};
@@ -256,6 +258,7 @@ impl Clone for Event<'static, T> {
}
impl<'a, T> Event<'a, T> {
+ #[allow(clippy::result_large_err)]
pub fn map_nonuser_event(self) -> Result, Event<'a, T>> {
use self::Event::*;
match self {
@@ -360,20 +363,21 @@ pub enum WindowEvent<'a> {
/// hovered.
HoveredFileCancelled,
- /// The window received a unicode character.
- ///
- /// See also the [`Ime`](Self::Ime) event for more complex character sequences.
- ReceivedCharacter(char),
-
/// The window gained or lost focus.
///
/// The parameter is true if the window has gained focus, and false if it has lost focus.
Focused(bool),
/// An event from the keyboard has been received.
+ ///
+ /// ## Platform-specific
+ /// - **Windows:** The shift key overrides NumLock. In other words, while shift is held down,
+ /// numpad keys act as if NumLock wasn't active. When this is used, the OS sends fake key
+ /// events which are not marked as `is_synthetic`.
KeyboardInput {
device_id: DeviceId,
- input: KeyboardInput,
+ event: KeyEvent,
+
/// If `true`, the event was generated synthetically by winit
/// in one of the following circumstances:
///
@@ -387,12 +391,7 @@ pub enum WindowEvent<'a> {
},
/// The keyboard modifiers have changed.
- ///
- /// ## Platform-specific
- ///
- /// - **Web:** This API is currently unimplemented on the web. This isn't by design - it's an
- /// issue, and it should get fixed - but it's the current state of the API.
- ModifiersChanged(ModifiersState),
+ ModifiersChanged(Modifiers),
/// An event from an input method.
///
@@ -411,8 +410,6 @@ pub enum WindowEvent<'a> {
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
position: PhysicalPosition,
- #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
- modifiers: ModifiersState,
},
/// The cursor has entered the window.
@@ -426,8 +423,6 @@ pub enum WindowEvent<'a> {
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
- #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
- modifiers: ModifiersState,
},
/// An mouse button press has been received.
@@ -435,8 +430,6 @@ pub enum WindowEvent<'a> {
device_id: DeviceId,
state: ElementState,
button: MouseButton,
- #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
- modifiers: ModifiersState,
},
/// Touchpad magnification event with two-finger pinch gesture.
@@ -560,28 +553,24 @@ impl Clone for WindowEvent<'static> {
DroppedFile(file) => DroppedFile(file.clone()),
HoveredFile(file) => HoveredFile(file.clone()),
HoveredFileCancelled => HoveredFileCancelled,
- ReceivedCharacter(c) => ReceivedCharacter(*c),
Focused(f) => Focused(*f),
KeyboardInput {
device_id,
- input,
+ event,
is_synthetic,
} => KeyboardInput {
device_id: *device_id,
- input: *input,
+ event: event.clone(),
is_synthetic: *is_synthetic,
},
Ime(preedit_state) => Ime(preedit_state.clone()),
ModifiersChanged(modifiers) => ModifiersChanged(*modifiers),
- #[allow(deprecated)]
CursorMoved {
device_id,
position,
- modifiers,
} => CursorMoved {
device_id: *device_id,
position: *position,
- modifiers: *modifiers,
},
CursorEntered { device_id } => CursorEntered {
device_id: *device_id,
@@ -589,29 +578,23 @@ impl Clone for WindowEvent<'static> {
CursorLeft { device_id } => CursorLeft {
device_id: *device_id,
},
- #[allow(deprecated)]
MouseWheel {
device_id,
delta,
phase,
- modifiers,
} => MouseWheel {
device_id: *device_id,
delta: *delta,
phase: *phase,
- modifiers: *modifiers,
},
- #[allow(deprecated)]
MouseInput {
device_id,
state,
button,
- modifiers,
} => MouseInput {
device_id: *device_id,
state: *state,
button: *button,
- modifiers: *modifiers,
},
TouchpadMagnify {
device_id,
@@ -673,54 +656,44 @@ impl<'a> WindowEvent<'a> {
DroppedFile(file) => Some(DroppedFile(file)),
HoveredFile(file) => Some(HoveredFile(file)),
HoveredFileCancelled => Some(HoveredFileCancelled),
- ReceivedCharacter(c) => Some(ReceivedCharacter(c)),
Focused(focused) => Some(Focused(focused)),
KeyboardInput {
device_id,
- input,
+ event,
is_synthetic,
} => Some(KeyboardInput {
device_id,
- input,
+ event,
is_synthetic,
}),
- ModifiersChanged(modifiers) => Some(ModifiersChanged(modifiers)),
+ ModifiersChanged(modifers) => Some(ModifiersChanged(modifers)),
Ime(event) => Some(Ime(event)),
- #[allow(deprecated)]
CursorMoved {
device_id,
position,
- modifiers,
} => Some(CursorMoved {
device_id,
position,
- modifiers,
}),
CursorEntered { device_id } => Some(CursorEntered { device_id }),
CursorLeft { device_id } => Some(CursorLeft { device_id }),
- #[allow(deprecated)]
MouseWheel {
device_id,
delta,
phase,
- modifiers,
} => Some(MouseWheel {
device_id,
delta,
phase,
- modifiers,
}),
- #[allow(deprecated)]
MouseInput {
device_id,
state,
button,
- modifiers,
} => Some(MouseInput {
device_id,
state,
button,
- modifiers,
}),
TouchpadMagnify {
device_id,
@@ -831,50 +804,224 @@ pub enum DeviceEvent {
state: ElementState,
},
- Key(KeyboardInput),
+ Key(RawKeyEvent),
Text {
codepoint: char,
},
}
-/// Describes a keyboard input event.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+/// Describes a keyboard input as a raw device event.
+///
+/// Note that holding down a key may produce repeated `RawKeyEvent`s. The
+/// operating system doesn't provide information whether such an event is a
+/// repeat or the initial keypress. An application may emulate this by, for
+/// example keeping a Map/Set of pressed keys and determining whether a keypress
+/// corresponds to an already pressed key.
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-pub struct KeyboardInput {
- /// Identifies the physical key pressed
+pub struct RawKeyEvent {
+ pub physical_key: keyboard::KeyCode,
+ pub state: ElementState,
+}
+
+/// Describes a keyboard input targeting a window.
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct KeyEvent {
+ /// Represents the position of a key independent of the currently active layout.
+ ///
+ /// It also uniquely identifies the physical key (i.e. it's mostly synonymous with a scancode).
+ /// The most prevalent use case for this is games. For example the default keys for the player
+ /// to move around might be the W, A, S, and D keys on a US layout. The position of these keys
+ /// is more important than their label, so they should map to Z, Q, S, and D on an "AZERTY"
+ /// layout. (This value is `KeyCode::KeyW` for the Z key on an AZERTY layout.)
+ ///
+ /// ## Caveats
+ ///
+ /// - Certain niche hardware will shuffle around physical key positions, e.g. a keyboard that
+ /// implements DVORAK in hardware (or firmware)
+ /// - Your application will likely have to handle keyboards which are missing keys that your
+ /// own keyboard has.
+ /// - Certain `KeyCode`s will move between a couple of different positions depending on what
+ /// layout the keyboard was manufactured to support.
+ ///
+ /// **Because of these caveats, it is important that you provide users with a way to configure
+ /// most (if not all) keybinds in your application.**
+ ///
+ /// ## `Fn` and `FnLock`
+ ///
+ /// `Fn` and `FnLock` key events are *exceedingly unlikely* to be emitted by Winit. These keys
+ /// are usually handled at the hardware or OS level, and aren't surfaced to applications. If
+ /// you somehow see this in the wild, we'd like to know :)
+ pub physical_key: keyboard::KeyCode,
+
+ // Allowing `broken_intra_doc_links` for `logical_key`, because
+ // `key_without_modifiers` is not available on all platforms
+ #[cfg_attr(
+ not(any(target_os = "macos", target_os = "windows", target_os = "linux")),
+ allow(rustdoc::broken_intra_doc_links)
+ )]
+ /// This value is affected by all modifiers except Ctrl.
+ ///
+ /// This has two use cases:
+ /// - Allows querying whether the current input is a Dead key.
+ /// - Allows handling key-bindings on platforms which don't
+ /// support [`key_without_modifiers`].
+ ///
+ /// If you use this field (or [`key_without_modifiers`] for that matter) for keyboard
+ /// shortcuts, **it is important that you provide users with a way to configure your
+ /// application's shortcuts so you don't render your application unusable for users with an
+ /// incompatible keyboard layout.**
+ ///
+ /// ## Platform-specific
+ /// - **Web:** Dead keys might be reported as the real key instead
+ /// of `Dead` depending on the browser/OS.
+ ///
+ /// [`key_without_modifiers`]: crate::platform::modifier_supplement::KeyEventExtModifierSupplement::key_without_modifiers
+ pub logical_key: keyboard::Key,
+
+ /// Contains the text produced by this keypress.
+ ///
+ /// In most cases this is identical to the content
+ /// of the `Character` variant of `logical_key`.
+ /// However, on Windows when a dead key was pressed earlier
+ /// but cannot be combined with the character from this
+ /// keypress, the produced text will consist of two characters:
+ /// the dead-key-character followed by the character resulting
+ /// from this keypress.
+ ///
+ /// An additional difference from `logical_key` is that
+ /// this field stores the text representation of any key
+ /// that has such a representation. For example when
+ /// `logical_key` is `Key::Enter`, this field is `Some("\r")`.
///
- /// This should not change if the user adjusts the host's keyboard map. Use when the physical location of the
- /// key is more important than the key's host GUI semantics, such as for movement controls in a first-person
- /// game.
- pub scancode: ScanCode,
+ /// This is `None` if the current keypress cannot
+ /// be interpreted as text.
+ ///
+ /// See also: `text_with_all_modifiers()`
+ pub text: Option,
+ /// Contains the location of this key on the keyboard.
+ ///
+ /// Certain keys on the keyboard may appear in more than once place. For example, the "Shift" key
+ /// appears on the left side of the QWERTY keyboard as well as the right side. However, both keys
+ /// have the same symbolic value. Another example of this phenomenon is the "1" key, which appears
+ /// both above the "Q" key and as the "Keypad 1" key.
+ ///
+ /// This field allows the user to differentiate between keys like this that have the same symbolic
+ /// value but different locations on the keyboard.
+ ///
+ /// See the [`KeyLocation`] type for more details.
+ ///
+ /// [`KeyLocation`]: crate::keyboard::KeyLocation
+ pub location: keyboard::KeyLocation,
+
+ /// Whether the key is being pressed or released.
+ ///
+ /// See the [`ElementState`] type for more details.
pub state: ElementState,
- /// Identifies the semantic meaning of the key
+ /// Whether or not this key is a key repeat event.
///
- /// Use when the semantics of the key are more important than the physical location of the key, such as when
- /// implementing appropriate behavior for "page up."
- pub virtual_keycode: Option,
+ /// On some systems, holding down a key for some period of time causes that key to be repeated
+ /// as though it were being pressed and released repeatedly. This field is `true` if and only if
+ /// this event is the result of one of those repeats.
+ pub repeat: bool,
- /// Modifier keys active at the time of this input.
+ /// Platform-specific key event information.
///
- /// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
- /// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
- #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"]
- pub modifiers: ModifiersState,
+ /// On Windows, Linux and macOS, this type contains the key without modifiers and the text with all
+ /// modifiers applied.
+ ///
+ /// On Android, iOS, Redox and Web, this type is a no-op.
+ pub(crate) platform_specific: platform_impl::KeyEventExtra,
+}
+
+/// Describes keyboard modifiers event.
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+pub struct Modifiers {
+ pub(crate) state: ModifiersState,
+
+ // NOTE: Currently pressed modifiers keys.
+ //
+ // The field providing a metadata, it shouldn't be used as a source of truth.
+ pub(crate) pressed_mods: ModifiersKeys,
+}
+
+impl Modifiers {
+ /// The state of the modifiers.
+ pub fn state(&self) -> ModifiersState {
+ self.state
+ }
+
+ /// The state of the left shift key.
+ pub fn lshift_state(&self) -> ModifiersKeyState {
+ self.mod_state(ModifiersKeys::LSHIFT)
+ }
+
+ /// The state of the right shift key.
+ pub fn rshift_state(&self) -> ModifiersKeyState {
+ self.mod_state(ModifiersKeys::RSHIFT)
+ }
+
+ /// The state of the left alt key.
+ pub fn lalt_state(&self) -> ModifiersKeyState {
+ self.mod_state(ModifiersKeys::LALT)
+ }
+
+ /// The state of the right alt key.
+ pub fn ralt_state(&self) -> ModifiersKeyState {
+ self.mod_state(ModifiersKeys::RALT)
+ }
+
+ /// The state of the left control key.
+ pub fn lcontrol_state(&self) -> ModifiersKeyState {
+ self.mod_state(ModifiersKeys::LCONTROL)
+ }
+
+ /// The state of the right control key.
+ pub fn rcontrol_state(&self) -> ModifiersKeyState {
+ self.mod_state(ModifiersKeys::RCONTROL)
+ }
+
+ /// The state of the left super key.
+ pub fn lsuper_state(&self) -> ModifiersKeyState {
+ self.mod_state(ModifiersKeys::LSUPER)
+ }
+
+ /// The state of the right super key.
+ pub fn rsuper_state(&self) -> ModifiersKeyState {
+ self.mod_state(ModifiersKeys::RSUPER)
+ }
+
+ fn mod_state(&self, modifier: ModifiersKeys) -> ModifiersKeyState {
+ if self.pressed_mods.contains(modifier) {
+ ModifiersKeyState::Pressed
+ } else {
+ ModifiersKeyState::Unknown
+ }
+ }
+}
+
+impl From for Modifiers {
+ fn from(value: ModifiersState) -> Self {
+ Self {
+ state: value,
+ pressed_mods: Default::default(),
+ }
+ }
}
/// Describes [input method](https://en.wikipedia.org/wiki/Input_method) events.
///
/// This is also called a "composition event".
///
-/// Most keypresses using a latin-like keyboard layout simply generate a [`WindowEvent::ReceivedCharacter`].
+/// Most keypresses using a latin-like keyboard layout simply generate a [`WindowEvent::KeyboardInput`].
/// However, one couldn't possibly have a key for every single unicode character that the user might want to type
/// - so the solution operating systems employ is to allow the user to type these using _a sequence of keypresses_ instead.
///
/// A prominent example of this is accents - many keyboard layouts allow you to first click the "accent key", and then
-/// the character you want to apply the accent to. This will generate the following event sequence:
+/// the character you want to apply the accent to. In this case, some platforms will generate the following event sequence:
/// ```ignore
/// // Press "`" key
/// Ime::Preedit("`", Some((0, 0)))
@@ -886,7 +1033,7 @@ pub struct KeyboardInput {
/// Additionally, certain input devices are configured to display a candidate box that allow the user to select the
/// desired character interactively. (To properly position this box, you must use [`Window::set_ime_position`].)
///
-/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keybaord the following event
+/// An example of a keyboard layout which uses candidate boxes is pinyin. On a latin keyboard the following event
/// sequence could be obtained:
/// ```ignore
/// // Press "A" key
@@ -1037,9 +1184,6 @@ impl Force {
}
}
-/// Hardware-dependent keyboard scan code.
-pub type ScanCode = u32;
-
/// Identifier for a specific analog axis on some device.
pub type AxisId = u32;
@@ -1090,303 +1234,3 @@ pub enum MouseScrollDelta {
/// and move the content right and down (to reveal more things left and up).
PixelDelta(PhysicalPosition),
}
-
-/// Symbolic name for a keyboard key.
-#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
-#[repr(u32)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-pub enum VirtualKeyCode {
- /// The '1' key over the letters.
- Key1,
- /// The '2' key over the letters.
- Key2,
- /// The '3' key over the letters.
- Key3,
- /// The '4' key over the letters.
- Key4,
- /// The '5' key over the letters.
- Key5,
- /// The '6' key over the letters.
- Key6,
- /// The '7' key over the letters.
- Key7,
- /// The '8' key over the letters.
- Key8,
- /// The '9' key over the letters.
- Key9,
- /// The '0' key over the 'O' and 'P' keys.
- Key0,
-
- A,
- B,
- C,
- D,
- E,
- F,
- G,
- H,
- I,
- J,
- K,
- L,
- M,
- N,
- O,
- P,
- Q,
- R,
- S,
- T,
- U,
- V,
- W,
- X,
- Y,
- Z,
-
- /// The Escape key, next to F1.
- Escape,
-
- F1,
- F2,
- F3,
- F4,
- F5,
- F6,
- F7,
- F8,
- F9,
- F10,
- F11,
- F12,
- F13,
- F14,
- F15,
- F16,
- F17,
- F18,
- F19,
- F20,
- F21,
- F22,
- F23,
- F24,
-
- /// Print Screen/SysRq.
- Snapshot,
- /// Scroll Lock.
- Scroll,
- /// Pause/Break key, next to Scroll lock.
- Pause,
-
- /// `Insert`, next to Backspace.
- Insert,
- Home,
- Delete,
- End,
- PageDown,
- PageUp,
-
- Left,
- Up,
- Right,
- Down,
-
- /// The Backspace key, right over Enter.
- // TODO: rename
- Back,
- /// The Enter key.
- Return,
- /// The space bar.
- Space,
-
- /// The "Compose" key on Linux.
- Compose,
-
- Caret,
-
- Numlock,
- Numpad0,
- Numpad1,
- Numpad2,
- Numpad3,
- Numpad4,
- Numpad5,
- Numpad6,
- Numpad7,
- Numpad8,
- Numpad9,
- NumpadAdd,
- NumpadDivide,
- NumpadDecimal,
- NumpadComma,
- NumpadEnter,
- NumpadEquals,
- NumpadMultiply,
- NumpadSubtract,
-
- AbntC1,
- AbntC2,
- Apostrophe,
- Apps,
- Asterisk,
- At,
- Ax,
- Backslash,
- Calculator,
- Capital,
- Colon,
- Comma,
- Convert,
- Equals,
- Grave,
- Kana,
- Kanji,
- LAlt,
- LBracket,
- LControl,
- LShift,
- LWin,
- Mail,
- MediaSelect,
- MediaStop,
- Minus,
- Mute,
- MyComputer,
- // also called "Next"
- NavigateForward,
- // also called "Prior"
- NavigateBackward,
- NextTrack,
- NoConvert,
- OEM102,
- Period,
- PlayPause,
- Plus,
- Power,
- PrevTrack,
- RAlt,
- RBracket,
- RControl,
- RShift,
- RWin,
- Semicolon,
- Slash,
- Sleep,
- Stop,
- Sysrq,
- Tab,
- Underline,
- Unlabeled,
- VolumeDown,
- VolumeUp,
- Wake,
- WebBack,
- WebFavorites,
- WebForward,
- WebHome,
- WebRefresh,
- WebSearch,
- WebStop,
- Yen,
- Copy,
- Paste,
- Cut,
-}
-
-impl ModifiersState {
- /// Returns `true` if the shift key is pressed.
- pub fn shift(&self) -> bool {
- self.intersects(Self::SHIFT)
- }
- /// Returns `true` if the control key is pressed.
- pub fn ctrl(&self) -> bool {
- self.intersects(Self::CTRL)
- }
- /// Returns `true` if the alt key is pressed.
- pub fn alt(&self) -> bool {
- self.intersects(Self::ALT)
- }
- /// Returns `true` if the logo key is pressed.
- pub fn logo(&self) -> bool {
- self.intersects(Self::LOGO)
- }
-}
-
-bitflags! {
- /// Represents the current state of the keyboard modifiers
- ///
- /// Each flag represents a modifier and is set if this modifier is active.
- #[derive(Default)]
- pub struct ModifiersState: u32 {
- // left and right modifiers are currently commented out, but we should be able to support
- // them in a future release
- /// The "shift" key.
- const SHIFT = 0b100;
- // const LSHIFT = 0b010;
- // const RSHIFT = 0b001;
- /// The "control" key.
- const CTRL = 0b100 << 3;
- // const LCTRL = 0b010 << 3;
- // const RCTRL = 0b001 << 3;
- /// The "alt" key.
- const ALT = 0b100 << 6;
- // const LALT = 0b010 << 6;
- // const RALT = 0b001 << 6;
- /// This is the "windows" key on PC and "command" key on Mac.
- const LOGO = 0b100 << 9;
- // const LLOGO = 0b010 << 9;
- // const RLOGO = 0b001 << 9;
- }
-}
-
-#[cfg(feature = "serde")]
-mod modifiers_serde {
- use super::ModifiersState;
- use serde::{Deserialize, Deserializer, Serialize, Serializer};
-
- #[derive(Default, Serialize, Deserialize)]
- #[serde(default)]
- #[serde(rename = "ModifiersState")]
- pub struct ModifiersStateSerialize {
- pub shift: bool,
- pub ctrl: bool,
- pub alt: bool,
- pub logo: bool,
- }
-
- impl Serialize for ModifiersState {
- fn serialize(&self, serializer: S) -> Result
- where
- S: Serializer,
- {
- let s = ModifiersStateSerialize {
- shift: self.shift(),
- ctrl: self.ctrl(),
- alt: self.alt(),
- logo: self.logo(),
- };
- s.serialize(serializer)
- }
- }
-
- impl<'de> Deserialize<'de> for ModifiersState {
- fn deserialize(deserializer: D) -> Result
- where
- D: Deserializer<'de>,
- {
- let ModifiersStateSerialize {
- shift,
- ctrl,
- alt,
- logo,
- } = ModifiersStateSerialize::deserialize(deserializer)?;
- let mut m = ModifiersState::empty();
- m.set(ModifiersState::SHIFT, shift);
- m.set(ModifiersState::CTRL, ctrl);
- m.set(ModifiersState::ALT, alt);
- m.set(ModifiersState::LOGO, logo);
- Ok(m)
- }
- }
-}
diff --git a/src/keyboard.rs b/src/keyboard.rs
new file mode 100644
index 0000000000..15f39702ef
--- /dev/null
+++ b/src/keyboard.rs
@@ -0,0 +1,1688 @@
+//! Types related to the keyboard.
+
+// This file contains a substantial portion of the UI Events Specification by the W3C. In
+// particular, the variant names within `Key` and `KeyCode` and their documentation are modified
+// versions of contents of the aforementioned specification.
+//
+// The original documents are:
+//
+// ### For `Key`
+// UI Events KeyboardEvent key Values
+// https://www.w3.org/TR/2017/CR-uievents-key-20170601/
+// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang).
+//
+// ### For `KeyCode`
+// UI Events KeyboardEvent code Values
+// https://www.w3.org/TR/2017/CR-uievents-code-20170601/
+// Copyright © 2017 W3C® (MIT, ERCIM, Keio, Beihang).
+//
+// These documents were used under the terms of the following license. This W3C license as well as
+// the W3C short notice apply to the `Key` and `KeyCode` enums and their variants and the
+// documentation attached to their variants.
+
+// --------- BEGGINING OF W3C LICENSE --------------------------------------------------------------
+//
+// License
+//
+// By obtaining and/or copying this work, you (the licensee) agree that you have read, understood,
+// and will comply with the following terms and conditions.
+//
+// Permission to copy, modify, and distribute this work, with or without modification, for any
+// purpose and without fee or royalty is hereby granted, provided that you include the following on
+// ALL copies of the work or portions thereof, including modifications:
+//
+// - The full text of this NOTICE in a location viewable to users of the redistributed or derivative
+// work.
+// - Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none
+// exist, the W3C Software and Document Short Notice should be included.
+// - Notice of any changes or modifications, through a copyright statement on the new code or
+// document such as "This software or document includes material copied from or derived from
+// [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)."
+//
+// Disclaimers
+//
+// THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR
+// ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD
+// PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+//
+// COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES
+// ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT.
+//
+// The name and trademarks of copyright holders may NOT be used in advertising or publicity
+// pertaining to the work without specific, written prior permission. Title to copyright in this
+// work will at all times remain with copyright holders.
+//
+// --------- END OF W3C LICENSE --------------------------------------------------------------------
+
+// --------- BEGGINING OF W3C SHORT NOTICE ---------------------------------------------------------
+//
+// winit: https://github.com/rust-windowing/winit
+//
+// Copyright © 2021 World Wide Web Consortium, (Massachusetts Institute of Technology, European
+// Research Consortium for Informatics and Mathematics, Keio University, Beihang). All Rights
+// Reserved. This work is distributed under the W3C® Software License [1] in the hope that it will
+// be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.
+//
+// [1] http://www.w3.org/Consortium/Legal/copyright-software
+//
+// --------- END OF W3C SHORT NOTICE ---------------------------------------------------------------
+
+use smol_str::SmolStr;
+
+/// Contains the platform-native physical key identifier
+///
+/// The exact values vary from platform to platform (which is part of why this is a per-platform
+/// enum), but the values are primarily tied to the key's physical location on the keyboard.
+///
+/// This enum is primarily used to store raw keycodes when Winit doesn't map a given native
+/// physical key identifier to a meaningful [`KeyCode`] variant. In the presence of identifiers we
+/// haven't mapped for you yet, this lets you use use [`KeyCode`] to:
+///
+/// - Correctly match key press and release events.
+/// - On non-web platforms, support assigning keybinds to virtually any key through a UI.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum NativeKeyCode {
+ Unidentified,
+ /// An Android "scancode".
+ Android(u32),
+ /// A macOS "scancode".
+ MacOS(u16),
+ /// A Windows "scancode".
+ Windows(u16),
+ /// An XKB "keycode".
+ Xkb(u32),
+}
+
+impl std::fmt::Debug for NativeKeyCode {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use NativeKeyCode::{Android, MacOS, Unidentified, Windows, Xkb};
+ let mut debug_tuple;
+ match self {
+ Unidentified => {
+ debug_tuple = f.debug_tuple("Unidentified");
+ }
+ Android(code) => {
+ debug_tuple = f.debug_tuple("Android");
+ debug_tuple.field(&format_args!("0x{code:04X}"));
+ }
+ MacOS(code) => {
+ debug_tuple = f.debug_tuple("MacOS");
+ debug_tuple.field(&format_args!("0x{code:04X}"));
+ }
+ Windows(code) => {
+ debug_tuple = f.debug_tuple("Windows");
+ debug_tuple.field(&format_args!("0x{code:04X}"));
+ }
+ Xkb(code) => {
+ debug_tuple = f.debug_tuple("Xkb");
+ debug_tuple.field(&format_args!("0x{code:04X}"));
+ }
+ }
+ debug_tuple.finish()
+ }
+}
+
+/// Contains the platform-native logical key identifier
+///
+/// Exactly what that means differs from platform to platform, but the values are to some degree
+/// tied to the currently active keyboard layout. The same key on the same keyboard may also report
+/// different values on different platforms, which is one of the reasons this is a per-platform
+/// enum.
+///
+/// This enum is primarily used to store raw keysym when Winit doesn't map a given native logical
+/// key identifier to a meaningful [`Key`] variant. This lets you use [`Key`], and let the user
+/// define keybinds which work in the presence of identifiers we haven't mapped for you yet.
+#[derive(Clone, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum NativeKey {
+ Unidentified,
+ /// An Android "keycode", which is similar to a "virtual-key code" on Windows.
+ Android(u32),
+ /// A macOS "scancode". There does not appear to be any direct analogue to either keysyms or
+ /// "virtual-key" codes in macOS, so we report the scancode instead.
+ MacOS(u16),
+ /// A Windows "virtual-key code".
+ Windows(u16),
+ /// An XKB "keysym".
+ Xkb(u32),
+ /// A "key value string".
+ Web(SmolStr),
+}
+
+impl std::fmt::Debug for NativeKey {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ use NativeKey::{Android, MacOS, Unidentified, Web, Windows, Xkb};
+ let mut debug_tuple;
+ match self {
+ Unidentified => {
+ debug_tuple = f.debug_tuple("Unidentified");
+ }
+ Android(code) => {
+ debug_tuple = f.debug_tuple("Android");
+ debug_tuple.field(&format_args!("0x{code:04X}"));
+ }
+ MacOS(code) => {
+ debug_tuple = f.debug_tuple("MacOS");
+ debug_tuple.field(&format_args!("0x{code:04X}"));
+ }
+ Windows(code) => {
+ debug_tuple = f.debug_tuple("Windows");
+ debug_tuple.field(&format_args!("0x{code:04X}"));
+ }
+ Xkb(code) => {
+ debug_tuple = f.debug_tuple("Xkb");
+ debug_tuple.field(&format_args!("0x{code:04X}"));
+ }
+ Web(code) => {
+ debug_tuple = f.debug_tuple("Web");
+ debug_tuple.field(code);
+ }
+ }
+ debug_tuple.finish()
+ }
+}
+
+/// Represents the location of a physical key.
+///
+/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.code`] with a few
+/// exceptions:
+/// - The keys that the specification calls "MetaLeft" and "MetaRight" are named "SuperLeft" and
+/// "SuperRight" here.
+/// - The key that the specification calls "Super" is reported as `Unidentified` here.
+/// - The `Unidentified` variant here, can still identify a key through it's `NativeKeyCode`.
+///
+/// [`KeyboardEvent.code`]: https://w3c.github.io/uievents-code/#code-value-tables
+#[non_exhaustive]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum KeyCode {
+ /// This variant is used when the key cannot be translated to any other variant.
+ ///
+ /// The native keycode is provided (if available) so you're able to more reliably match
+ /// key-press and key-release events by hashing the [`KeyCode`]. It is also possible to use
+ /// this for keybinds for non-standard keys, but such keybinds are tied to a given platform.
+ Unidentified(NativeKeyCode),
+ /// ` on a US keyboard. This is also called a backtick or grave.
+ /// This is the 半角/全角/漢字
+ /// (hankaku/zenkaku/kanji) key on Japanese keyboards
+ Backquote,
+ /// Used for both the US \\ (on the 101-key layout) and also for the key
+ /// located between the " and Enter keys on row C of the 102-,
+ /// 104- and 106-key layouts.
+ /// Labeled # on a UK (102) keyboard.
+ Backslash,
+ /// [ on a US keyboard.
+ BracketLeft,
+ /// ] on a US keyboard.
+ BracketRight,
+ /// , on a US keyboard.
+ Comma,
+ /// 0 on a US keyboard.
+ Digit0,
+ /// 1 on a US keyboard.
+ Digit1,
+ /// 2 on a US keyboard.
+ Digit2,
+ /// 3 on a US keyboard.
+ Digit3,
+ /// 4 on a US keyboard.
+ Digit4,
+ /// 5 on a US keyboard.
+ Digit5,
+ /// 6 on a US keyboard.
+ Digit6,
+ /// 7 on a US keyboard.
+ Digit7,
+ /// 8 on a US keyboard.
+ Digit8,
+ /// 9 on a US keyboard.
+ Digit9,
+ /// = on a US keyboard.
+ Equal,
+ /// Located between the left Shift and Z keys.
+ /// Labeled \\ on a UK keyboard.
+ IntlBackslash,
+ /// Located between the / and right Shift keys.
+ /// Labeled \\ (ro) on a Japanese keyboard.
+ IntlRo,
+ /// Located between the = and Backspace keys.
+ /// Labeled ¥ (yen) on a Japanese keyboard. \\ on a
+ /// Russian keyboard.
+ IntlYen,
+ /// a on a US keyboard.
+ /// Labeled q on an AZERTY (e.g., French) keyboard.
+ KeyA,
+ /// b on a US keyboard.
+ KeyB,
+ /// c on a US keyboard.
+ KeyC,
+ /// d on a US keyboard.
+ KeyD,
+ /// e on a US keyboard.
+ KeyE,
+ /// f on a US keyboard.
+ KeyF,
+ /// g on a US keyboard.
+ KeyG,
+ /// h on a US keyboard.
+ KeyH,
+ /// i on a US keyboard.
+ KeyI,
+ /// j on a US keyboard.
+ KeyJ,
+ /// k on a US keyboard.
+ KeyK,
+ /// l on a US keyboard.
+ KeyL,
+ /// m on a US keyboard.
+ KeyM,
+ /// n on a US keyboard.
+ KeyN,
+ /// o on a US keyboard.
+ KeyO,
+ /// p on a US keyboard.
+ KeyP,
+ /// q on a US keyboard.
+ /// Labeled a on an AZERTY (e.g., French) keyboard.
+ KeyQ,
+ /// r on a US keyboard.
+ KeyR,
+ /// s on a US keyboard.
+ KeyS,
+ /// t on a US keyboard.
+ KeyT,
+ /// u on a US keyboard.
+ KeyU,
+ /// v on a US keyboard.
+ KeyV,
+ /// w on a US keyboard.
+ /// Labeled z on an AZERTY (e.g., French) keyboard.
+ KeyW,
+ /// x on a US keyboard.
+ KeyX,
+ /// y on a US keyboard.
+ /// Labeled z on a QWERTZ (e.g., German) keyboard.
+ KeyY,
+ /// z on a US keyboard.
+ /// Labeled w on an AZERTY (e.g., French) keyboard, and y on a
+ /// QWERTZ (e.g., German) keyboard.
+ KeyZ,
+ /// - on a US keyboard.
+ Minus,
+ /// . on a US keyboard.
+ Period,
+ /// ' on a US keyboard.
+ Quote,
+ /// ; on a US keyboard.
+ Semicolon,
+ /// / on a US keyboard.
+ Slash,
+ /// Alt, Option, or ⌥.
+ AltLeft,
+ /// Alt, Option, or ⌥.
+ /// This is labeled AltGr on many keyboard layouts.
+ AltRight,
+ /// Backspace or ⌫.
+ /// Labeled Delete on Apple keyboards.
+ Backspace,
+ /// CapsLock or ⇪
+ CapsLock,
+ /// The application context menu key, which is typically found between the right
+ /// Super key and the right Control key.
+ ContextMenu,
+ /// Control or ⌃
+ ControlLeft,
+ /// Control or ⌃
+ ControlRight,
+ /// Enter or ↵. Labeled Return on Apple keyboards.
+ Enter,
+ /// The Windows, ⌘, Command, or other OS symbol key.
+ SuperLeft,
+ /// The Windows, ⌘, Command, or other OS symbol key.
+ SuperRight,
+ /// Shift or ⇧
+ ShiftLeft,
+ /// Shift or ⇧
+ ShiftRight,
+ /// (space)
+ Space,
+ /// Tab or ⇥
+ Tab,
+ /// Japanese: 変 (henkan)
+ Convert,
+ /// Japanese: カタカナ/ひらがな/ローマ字 (katakana/hiragana/romaji)
+ KanaMode,
+ /// Korean: HangulMode 한/영 (han/yeong)
+ ///
+ /// Japanese (Mac keyboard): か (kana)
+ Lang1,
+ /// Korean: Hanja 한 (hanja)
+ ///
+ /// Japanese (Mac keyboard): 英 (eisu)
+ Lang2,
+ /// Japanese (word-processing keyboard): Katakana
+ Lang3,
+ /// Japanese (word-processing keyboard): Hiragana
+ Lang4,
+ /// Japanese (word-processing keyboard): Zenkaku/Hankaku
+ Lang5,
+ /// Japanese: 無変換 (muhenkan)
+ NonConvert,
+ /// ⌦. The forward delete key.
+ /// Note that on Apple keyboards, the key labelled Delete on the main part of
+ /// the keyboard is encoded as [`Backspace`].
+ ///
+ /// [`Backspace`]: Self::Backspace
+ Delete,
+ /// Page Down, End, or ↘
+ End,
+ /// Help. Not present on standard PC keyboards.
+ Help,
+ /// Home or ↖
+ Home,
+ /// Insert or Ins. Not present on Apple keyboards.
+ Insert,
+ /// Page Down, PgDn, or ⇟
+ PageDown,
+ /// Page Up, PgUp, or ⇞
+ PageUp,
+ /// ↓
+ ArrowDown,
+ /// ←
+ ArrowLeft,
+ /// →
+ ArrowRight,
+ /// ↑
+ ArrowUp,
+ /// On the Mac, this is used for the numpad Clear key.
+ NumLock,
+ /// 0 Ins on a keyboard. 0 on a phone or remote control
+ Numpad0,
+ /// 1 End on a keyboard. 1 or 1 QZ on a phone or remote control
+ Numpad1,
+ /// 2 ↓ on a keyboard. 2 ABC on a phone or remote control
+ Numpad2,
+ /// 3 PgDn on a keyboard. 3 DEF on a phone or remote control
+ Numpad3,
+ /// 4 ← on a keyboard. 4 GHI on a phone or remote control
+ Numpad4,
+ /// 5 on a keyboard. 5 JKL on a phone or remote control
+ Numpad5,
+ /// 6 → on a keyboard. 6 MNO on a phone or remote control
+ Numpad6,
+ /// 7 Home on a keyboard. 7 PQRS or 7 PRS on a phone
+ /// or remote control
+ Numpad7,
+ /// 8 ↑ on a keyboard. 8 TUV on a phone or remote control
+ Numpad8,
+ /// 9 PgUp on a keyboard. 9 WXYZ or 9 WXY on a phone
+ /// or remote control
+ Numpad9,
+ /// +
+ NumpadAdd,
+ /// Found on the Microsoft Natural Keyboard.
+ NumpadBackspace,
+ /// C or A (All Clear). Also for use with numpads that have a
+ /// Clear key that is separate from the NumLock key. On the Mac, the
+ /// numpad Clear key is encoded as [`NumLock`].
+ ///
+ /// [`NumLock`]: Self::NumLock
+ NumpadClear,
+ /// C (Clear Entry)
+ NumpadClearEntry,
+ /// , (thousands separator). For locales where the thousands separator
+ /// is a "." (e.g., Brazil), this key may generate a ..
+ NumpadComma,
+ /// . Del. For locales where the decimal separator is "," (e.g.,
+ /// Brazil), this key may generate a ,.
+ NumpadDecimal,
+ /// /
+ NumpadDivide,
+ NumpadEnter,
+ /// =
+ NumpadEqual,
+ /// # on a phone or remote control device. This key is typically found
+ /// below the 9 key and to the right of the 0 key.
+ NumpadHash,
+ /// M Add current entry to the value stored in memory.
+ NumpadMemoryAdd,
+ /// M Clear the value stored in memory.
+ NumpadMemoryClear,
+ /// M Replace the current entry with the value stored in memory.
+ NumpadMemoryRecall,
+ /// M Replace the value stored in memory with the current entry.
+ NumpadMemoryStore,
+ /// M Subtract current entry from the value stored in memory.
+ NumpadMemorySubtract,
+ /// * on a keyboard. For use with numpads that provide mathematical
+ /// operations (+, - * and /).
+ ///
+ /// Use `NumpadStar` for the * key on phones and remote controls.
+ NumpadMultiply,
+ /// ( Found on the Microsoft Natural Keyboard.
+ NumpadParenLeft,
+ /// ) Found on the Microsoft Natural Keyboard.
+ NumpadParenRight,
+ /// * on a phone or remote control device.
+ ///
+ /// This key is typically found below the 7 key and to the left of
+ /// the 0 key.
+ ///
+ /// Use "NumpadMultiply" for the * key on
+ /// numeric keypads.
+ NumpadStar,
+ /// -
+ NumpadSubtract,
+ /// Esc or ⎋
+ Escape,
+ /// Fn This is typically a hardware key that does not generate a separate code.
+ Fn,
+ /// FLock or FnLock. Function Lock key. Found on the Microsoft
+ /// Natural Keyboard.
+ FnLock,
+ /// PrtScr SysRq or Print Screen
+ PrintScreen,
+ /// Scroll Lock
+ ScrollLock,
+ /// Pause Break
+ Pause,
+ /// Some laptops place this key to the left of the ↑ key.
+ ///
+ /// This also the "back" button (triangle) on Android.
+ BrowserBack,
+ BrowserFavorites,
+ /// Some laptops place this key to the right of the ↑ key.
+ BrowserForward,
+ /// The "home" button on Android.
+ BrowserHome,
+ BrowserRefresh,
+ BrowserSearch,
+ BrowserStop,
+ /// Eject or ⏏. This key is placed in the function section on some Apple
+ /// keyboards.
+ Eject,
+ /// Sometimes labelled My Computer on the keyboard
+ LaunchApp1,
+ /// Sometimes labelled Calculator on the keyboard
+ LaunchApp2,
+ LaunchMail,
+ MediaPlayPause,
+ MediaSelect,
+ MediaStop,
+ MediaTrackNext,
+ MediaTrackPrevious,
+ /// This key is placed in the function section on some Apple keyboards, replacing the
+ /// Eject key.
+ Power,
+ Sleep,
+ AudioVolumeDown,
+ AudioVolumeMute,
+ AudioVolumeUp,
+ WakeUp,
+ // Legacy modifier key. Also called "Super" in certain places.
+ Meta,
+ // Legacy modifier key.
+ Hyper,
+ Turbo,
+ Abort,
+ Resume,
+ Suspend,
+ /// Found on Sun’s USB keyboard.
+ Again,
+ /// Found on Sun’s USB keyboard.
+ Copy,
+ /// Found on Sun’s USB keyboard.
+ Cut,
+ /// Found on Sun’s USB keyboard.
+ Find,
+ /// Found on Sun’s USB keyboard.
+ Open,
+ /// Found on Sun’s USB keyboard.
+ Paste,
+ /// Found on Sun’s USB keyboard.
+ Props,
+ /// Found on Sun’s USB keyboard.
+ Select,
+ /// Found on Sun’s USB keyboard.
+ Undo,
+ /// Use for dedicated ひらがな key found on some Japanese word processing keyboards.
+ Hiragana,
+ /// Use for dedicated カタカナ key found on some Japanese word processing keyboards.
+ Katakana,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F1,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F2,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F3,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F4,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F5,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F6,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F7,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F8,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F9,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F10,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F11,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F12,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F13,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F14,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F15,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F16,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F17,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F18,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F19,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F20,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F21,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F22,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F23,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F24,
+ /// General-purpose function key.
+ F25,
+ /// General-purpose function key.
+ F26,
+ /// General-purpose function key.
+ F27,
+ /// General-purpose function key.
+ F28,
+ /// General-purpose function key.
+ F29,
+ /// General-purpose function key.
+ F30,
+ /// General-purpose function key.
+ F31,
+ /// General-purpose function key.
+ F32,
+ /// General-purpose function key.
+ F33,
+ /// General-purpose function key.
+ F34,
+ /// General-purpose function key.
+ F35,
+}
+
+/// Key represents the meaning of a keypress.
+///
+/// This mostly conforms to the UI Events Specification's [`KeyboardEvent.key`] with a few
+/// exceptions:
+/// - The `Super` variant here, is named `Meta` in the aforementioned specification. (There's
+/// another key which the specification calls `Super`. That does not exist here.)
+/// - The `Space` variant here, can be identified by the character it generates in the
+/// specificaiton.
+/// - The `Unidentified` variant here, can still identifiy a key through it's `NativeKeyCode`.
+/// - The `Dead` variant here, can specify the character which is inserted when pressing the
+/// dead-key twice.
+///
+/// [`KeyboardEvent.key`]: https://w3c.github.io/uievents-key/
+#[non_exhaustive]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum Key {
+ /// A key string that corresponds to the character typed by the user, taking into account the
+ /// user’s current locale setting, and any system-level keyboard mapping overrides that are in
+ /// effect.
+ Character(Str),
+
+ /// This variant is used when the key cannot be translated to any other variant.
+ ///
+ /// The native key is provided (if available) in order to allow the user to specify keybindings
+ /// for keys which are not defined by this API, mainly through some sort of UI.
+ Unidentified(NativeKey),
+
+ /// Contains the text representation of the dead-key when available.
+ ///
+ /// ## Platform-specific
+ /// - **Web:** Always contains `None`
+ Dead(Option),
+
+ /// The `Alt` (Alternative) key.
+ ///
+ /// This key enables the alternate modifier function for interpreting concurrent or subsequent
+ /// keyboard input. This key value is also used for the Apple Option key.
+ Alt,
+ /// The Alternate Graphics (AltGr or AltGraph) key.
+ ///
+ /// This key is used enable the ISO Level 3 shift modifier (the standard `Shift` key is the
+ /// level 2 modifier).
+ AltGraph,
+ /// The `Caps Lock` (Capital) key.
+ ///
+ /// Toggle capital character lock function for interpreting subsequent keyboard input event.
+ CapsLock,
+ /// The `Control` or `Ctrl` key.
+ ///
+ /// Used to enable control modifier function for interpreting concurrent or subsequent keyboard
+ /// input.
+ Control,
+ /// The Function switch `Fn` key. Activating this key simultaneously with another key changes
+ /// that key’s value to an alternate character or function. This key is often handled directly
+ /// in the keyboard hardware and does not usually generate key events.
+ Fn,
+ /// The Function-Lock (`FnLock` or `F-Lock`) key. Activating this key switches the mode of the
+ /// keyboard to changes some keys' values to an alternate character or function. This key is
+ /// often handled directly in the keyboard hardware and does not usually generate key events.
+ FnLock,
+ /// The `NumLock` or Number Lock key. Used to toggle numpad mode function for interpreting
+ /// subsequent keyboard input.
+ NumLock,
+ /// Toggle between scrolling and cursor movement modes.
+ ScrollLock,
+ /// Used to enable shift modifier function for interpreting concurrent or subsequent keyboard
+ /// input.
+ Shift,
+ /// The Symbol modifier key (used on some virtual keyboards).
+ Symbol,
+ SymbolLock,
+ // Legacy modifier key. Also called "Super" in certain places.
+ Meta,
+ // Legacy modifier key.
+ Hyper,
+ /// Used to enable "super" modifier function for interpreting concurrent or subsequent keyboard
+ /// input. This key value is used for the "Windows Logo" key and the Apple `Command` or `⌘` key.
+ ///
+ /// Note: In some contexts (e.g. the Web) this is referred to as the "Meta" key.
+ Super,
+ /// The `Enter` or `↵` key. Used to activate current selection or accept current input. This key
+ /// value is also used for the `Return` (Macintosh numpad) key. This key value is also used for
+ /// the Android `KEYCODE_DPAD_CENTER`.
+ Enter,
+ /// The Horizontal Tabulation `Tab` key.
+ Tab,
+ /// Used in text to insert a space between words. Usually located below the character keys.
+ Space,
+ /// Navigate or traverse downward. (`KEYCODE_DPAD_DOWN`)
+ ArrowDown,
+ /// Navigate or traverse leftward. (`KEYCODE_DPAD_LEFT`)
+ ArrowLeft,
+ /// Navigate or traverse rightward. (`KEYCODE_DPAD_RIGHT`)
+ ArrowRight,
+ /// Navigate or traverse upward. (`KEYCODE_DPAD_UP`)
+ ArrowUp,
+ /// The End key, used with keyboard entry to go to the end of content (`KEYCODE_MOVE_END`).
+ End,
+ /// The Home key, used with keyboard entry, to go to start of content (`KEYCODE_MOVE_HOME`).
+ /// For the mobile phone `Home` key (which goes to the phone’s main screen), use [`GoHome`].
+ ///
+ /// [`GoHome`]: Self::GoHome
+ Home,
+ /// Scroll down or display next page of content.
+ PageDown,
+ /// Scroll up or display previous page of content.
+ PageUp,
+ /// Used to remove the character to the left of the cursor. This key value is also used for
+ /// the key labeled `Delete` on MacOS keyboards.
+ Backspace,
+ /// Remove the currently selected input.
+ Clear,
+ /// Copy the current selection. (`APPCOMMAND_COPY`)
+ Copy,
+ /// The Cursor Select key.
+ CrSel,
+ /// Cut the current selection. (`APPCOMMAND_CUT`)
+ Cut,
+ /// Used to delete the character to the right of the cursor. This key value is also used for the
+ /// key labeled `Delete` on MacOS keyboards when `Fn` is active.
+ Delete,
+ /// The Erase to End of Field key. This key deletes all characters from the current cursor
+ /// position to the end of the current field.
+ EraseEof,
+ /// The Extend Selection (Exsel) key.
+ ExSel,
+ /// Toggle between text modes for insertion or overtyping.
+ /// (`KEYCODE_INSERT`)
+ Insert,
+ /// The Paste key. (`APPCOMMAND_PASTE`)
+ Paste,
+ /// Redo the last action. (`APPCOMMAND_REDO`)
+ Redo,
+ /// Undo the last action. (`APPCOMMAND_UNDO`)
+ Undo,
+ /// The Accept (Commit, OK) key. Accept current option or input method sequence conversion.
+ Accept,
+ /// Redo or repeat an action.
+ Again,
+ /// The Attention (Attn) key.
+ Attn,
+ Cancel,
+ /// Show the application’s context menu.
+ /// This key is commonly found between the right `Super` key and the right `Control` key.
+ ContextMenu,
+ /// The `Esc` key. This key was originally used to initiate an escape sequence, but is
+ /// now more generally used to exit or "escape" the current context, such as closing a dialog
+ /// or exiting full screen mode.
+ Escape,
+ Execute,
+ /// Open the Find dialog. (`APPCOMMAND_FIND`)
+ Find,
+ /// Open a help dialog or toggle display of help information. (`APPCOMMAND_HELP`,
+ /// `KEYCODE_HELP`)
+ Help,
+ /// Pause the current state or application (as appropriate).
+ ///
+ /// Note: Do not use this value for the `Pause` button on media controllers. Use `"MediaPause"`
+ /// instead.
+ Pause,
+ /// Play or resume the current state or application (as appropriate).
+ ///
+ /// Note: Do not use this value for the `Play` button on media controllers. Use `"MediaPlay"`
+ /// instead.
+ Play,
+ /// The properties (Props) key.
+ Props,
+ Select,
+ /// The ZoomIn key. (`KEYCODE_ZOOM_IN`)
+ ZoomIn,
+ /// The ZoomOut key. (`KEYCODE_ZOOM_OUT`)
+ ZoomOut,
+ /// The Brightness Down key. Typically controls the display brightness.
+ /// (`KEYCODE_BRIGHTNESS_DOWN`)
+ BrightnessDown,
+ /// The Brightness Up key. Typically controls the display brightness. (`KEYCODE_BRIGHTNESS_UP`)
+ BrightnessUp,
+ /// Toggle removable media to eject (open) and insert (close) state. (`KEYCODE_MEDIA_EJECT`)
+ Eject,
+ LogOff,
+ /// Toggle power state. (`KEYCODE_POWER`)
+ /// Note: Note: Some devices might not expose this key to the operating environment.
+ Power,
+ /// The `PowerOff` key. Sometime called `PowerDown`.
+ PowerOff,
+ /// Initiate print-screen function.
+ PrintScreen,
+ /// The Hibernate key. This key saves the current state of the computer to disk so that it can
+ /// be restored. The computer will then shutdown.
+ Hibernate,
+ /// The Standby key. This key turns off the display and places the computer into a low-power
+ /// mode without completely shutting down. It is sometimes labelled `Suspend` or `Sleep` key.
+ /// (`KEYCODE_SLEEP`)
+ Standby,
+ /// The WakeUp key. (`KEYCODE_WAKEUP`)
+ WakeUp,
+ /// Initate the multi-candidate mode.
+ AllCandidates,
+ Alphanumeric,
+ /// Initiate the Code Input mode to allow characters to be entered by
+ /// their code points.
+ CodeInput,
+ /// The Compose key, also known as "Multi_key" on the X Window System. This key acts in a
+ /// manner similar to a dead key, triggering a mode where subsequent key presses are combined to
+ /// produce a different character.
+ Compose,
+ /// Convert the current input method sequence.
+ Convert,
+ /// The Final Mode `Final` key used on some Asian keyboards, to enable the final mode for IMEs.
+ FinalMode,
+ /// Switch to the first character group. (ISO/IEC 9995)
+ GroupFirst,
+ /// Switch to the last character group. (ISO/IEC 9995)
+ GroupLast,
+ /// Switch to the next character group. (ISO/IEC 9995)
+ GroupNext,
+ /// Switch to the previous character group. (ISO/IEC 9995)
+ GroupPrevious,
+ /// Toggle between or cycle through input modes of IMEs.
+ ModeChange,
+ NextCandidate,
+ /// Accept current input method sequence without
+ /// conversion in IMEs.
+ NonConvert,
+ PreviousCandidate,
+ Process,
+ SingleCandidate,
+ /// Toggle between Hangul and English modes.
+ HangulMode,
+ HanjaMode,
+ JunjaMode,
+ /// The Eisu key. This key may close the IME, but its purpose is defined by the current IME.
+ /// (`KEYCODE_EISU`)
+ Eisu,
+ /// The (Half-Width) Characters key.
+ Hankaku,
+ /// The Hiragana (Japanese Kana characters) key.
+ Hiragana,
+ /// The Hiragana/Katakana toggle key. (`KEYCODE_KATAKANA_HIRAGANA`)
+ HiraganaKatakana,
+ /// The Kana Mode (Kana Lock) key. This key is used to enter hiragana mode (typically from
+ /// romaji mode).
+ KanaMode,
+ /// The Kanji (Japanese name for ideographic characters of Chinese origin) Mode key. This key is
+ /// typically used to switch to a hiragana keyboard for the purpose of converting input into
+ /// kanji. (`KEYCODE_KANA`)
+ KanjiMode,
+ /// The Katakana (Japanese Kana characters) key.
+ Katakana,
+ /// The Roman characters function key.
+ Romaji,
+ /// The Zenkaku (Full-Width) Characters key.
+ Zenkaku,
+ /// The Zenkaku/Hankaku (full-width/half-width) toggle key. (`KEYCODE_ZENKAKU_HANKAKU`)
+ ZenkakuHankaku,
+ /// General purpose virtual function key, as index 1.
+ Soft1,
+ /// General purpose virtual function key, as index 2.
+ Soft2,
+ /// General purpose virtual function key, as index 3.
+ Soft3,
+ /// General purpose virtual function key, as index 4.
+ Soft4,
+ /// Select next (numerically or logically) lower channel. (`APPCOMMAND_MEDIA_CHANNEL_DOWN`,
+ /// `KEYCODE_CHANNEL_DOWN`)
+ ChannelDown,
+ /// Select next (numerically or logically) higher channel. (`APPCOMMAND_MEDIA_CHANNEL_UP`,
+ /// `KEYCODE_CHANNEL_UP`)
+ ChannelUp,
+ /// Close the current document or message (Note: This doesn’t close the application).
+ /// (`APPCOMMAND_CLOSE`)
+ Close,
+ /// Open an editor to forward the current message. (`APPCOMMAND_FORWARD_MAIL`)
+ MailForward,
+ /// Open an editor to reply to the current message. (`APPCOMMAND_REPLY_TO_MAIL`)
+ MailReply,
+ /// Send the current message. (`APPCOMMAND_SEND_MAIL`)
+ MailSend,
+ /// Close the current media, for example to close a CD or DVD tray. (`KEYCODE_MEDIA_CLOSE`)
+ MediaClose,
+ /// Initiate or continue forward playback at faster than normal speed, or increase speed if
+ /// already fast forwarding. (`APPCOMMAND_MEDIA_FAST_FORWARD`, `KEYCODE_MEDIA_FAST_FORWARD`)
+ MediaFastForward,
+ /// Pause the currently playing media. (`APPCOMMAND_MEDIA_PAUSE`, `KEYCODE_MEDIA_PAUSE`)
+ ///
+ /// Note: Media controller devices should use this value rather than `"Pause"` for their pause
+ /// keys.
+ MediaPause,
+ /// Initiate or continue media playback at normal speed, if not currently playing at normal
+ /// speed. (`APPCOMMAND_MEDIA_PLAY`, `KEYCODE_MEDIA_PLAY`)
+ MediaPlay,
+ /// Toggle media between play and pause states. (`APPCOMMAND_MEDIA_PLAY_PAUSE`,
+ /// `KEYCODE_MEDIA_PLAY_PAUSE`)
+ MediaPlayPause,
+ /// Initiate or resume recording of currently selected media. (`APPCOMMAND_MEDIA_RECORD`,
+ /// `KEYCODE_MEDIA_RECORD`)
+ MediaRecord,
+ /// Initiate or continue reverse playback at faster than normal speed, or increase speed if
+ /// already rewinding. (`APPCOMMAND_MEDIA_REWIND`, `KEYCODE_MEDIA_REWIND`)
+ MediaRewind,
+ /// Stop media playing, pausing, forwarding, rewinding, or recording, if not already stopped.
+ /// (`APPCOMMAND_MEDIA_STOP`, `KEYCODE_MEDIA_STOP`)
+ MediaStop,
+ /// Seek to next media or program track. (`APPCOMMAND_MEDIA_NEXTTRACK`, `KEYCODE_MEDIA_NEXT`)
+ MediaTrackNext,
+ /// Seek to previous media or program track. (`APPCOMMAND_MEDIA_PREVIOUSTRACK`,
+ /// `KEYCODE_MEDIA_PREVIOUS`)
+ MediaTrackPrevious,
+ /// Open a new document or message. (`APPCOMMAND_NEW`)
+ New,
+ /// Open an existing document or message. (`APPCOMMAND_OPEN`)
+ Open,
+ /// Print the current document or message. (`APPCOMMAND_PRINT`)
+ Print,
+ /// Save the current document or message. (`APPCOMMAND_SAVE`)
+ Save,
+ /// Spellcheck the current document or selection. (`APPCOMMAND_SPELL_CHECK`)
+ SpellCheck,
+ /// The `11` key found on media numpads that
+ /// have buttons from `1` ... `12`.
+ Key11,
+ /// The `12` key found on media numpads that
+ /// have buttons from `1` ... `12`.
+ Key12,
+ /// Adjust audio balance leftward. (`VK_AUDIO_BALANCE_LEFT`)
+ AudioBalanceLeft,
+ /// Adjust audio balance rightward. (`VK_AUDIO_BALANCE_RIGHT`)
+ AudioBalanceRight,
+ /// Decrease audio bass boost or cycle down through bass boost states. (`APPCOMMAND_BASS_DOWN`,
+ /// `VK_BASS_BOOST_DOWN`)
+ AudioBassBoostDown,
+ /// Toggle bass boost on/off. (`APPCOMMAND_BASS_BOOST`)
+ AudioBassBoostToggle,
+ /// Increase audio bass boost or cycle up through bass boost states. (`APPCOMMAND_BASS_UP`,
+ /// `VK_BASS_BOOST_UP`)
+ AudioBassBoostUp,
+ /// Adjust audio fader towards front. (`VK_FADER_FRONT`)
+ AudioFaderFront,
+ /// Adjust audio fader towards rear. (`VK_FADER_REAR`)
+ AudioFaderRear,
+ /// Advance surround audio mode to next available mode. (`VK_SURROUND_MODE_NEXT`)
+ AudioSurroundModeNext,
+ /// Decrease treble. (`APPCOMMAND_TREBLE_DOWN`)
+ AudioTrebleDown,
+ /// Increase treble. (`APPCOMMAND_TREBLE_UP`)
+ AudioTrebleUp,
+ /// Decrease audio volume. (`APPCOMMAND_VOLUME_DOWN`, `KEYCODE_VOLUME_DOWN`)
+ AudioVolumeDown,
+ /// Increase audio volume. (`APPCOMMAND_VOLUME_UP`, `KEYCODE_VOLUME_UP`)
+ AudioVolumeUp,
+ /// Toggle between muted state and prior volume level. (`APPCOMMAND_VOLUME_MUTE`,
+ /// `KEYCODE_VOLUME_MUTE`)
+ AudioVolumeMute,
+ /// Toggle the microphone on/off. (`APPCOMMAND_MIC_ON_OFF_TOGGLE`)
+ MicrophoneToggle,
+ /// Decrease microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_DOWN`)
+ MicrophoneVolumeDown,
+ /// Increase microphone volume. (`APPCOMMAND_MICROPHONE_VOLUME_UP`)
+ MicrophoneVolumeUp,
+ /// Mute the microphone. (`APPCOMMAND_MICROPHONE_VOLUME_MUTE`, `KEYCODE_MUTE`)
+ MicrophoneVolumeMute,
+ /// Show correction list when a word is incorrectly identified. (`APPCOMMAND_CORRECTION_LIST`)
+ SpeechCorrectionList,
+ /// Toggle between dictation mode and command/control mode.
+ /// (`APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE`)
+ SpeechInputToggle,
+ /// The first generic "LaunchApplication" key. This is commonly associated with launching "My
+ /// Computer", and may have a computer symbol on the key. (`APPCOMMAND_LAUNCH_APP1`)
+ LaunchApplication1,
+ /// The second generic "LaunchApplication" key. This is commonly associated with launching
+ /// "Calculator", and may have a calculator symbol on the key. (`APPCOMMAND_LAUNCH_APP2`,
+ /// `KEYCODE_CALCULATOR`)
+ LaunchApplication2,
+ /// The "Calendar" key. (`KEYCODE_CALENDAR`)
+ LaunchCalendar,
+ /// The "Contacts" key. (`KEYCODE_CONTACTS`)
+ LaunchContacts,
+ /// The "Mail" key. (`APPCOMMAND_LAUNCH_MAIL`)
+ LaunchMail,
+ /// The "Media Player" key. (`APPCOMMAND_LAUNCH_MEDIA_SELECT`)
+ LaunchMediaPlayer,
+ LaunchMusicPlayer,
+ LaunchPhone,
+ LaunchScreenSaver,
+ LaunchSpreadsheet,
+ LaunchWebBrowser,
+ LaunchWebCam,
+ LaunchWordProcessor,
+ /// Navigate to previous content or page in current history. (`APPCOMMAND_BROWSER_BACKWARD`)
+ BrowserBack,
+ /// Open the list of browser favorites. (`APPCOMMAND_BROWSER_FAVORITES`)
+ BrowserFavorites,
+ /// Navigate to next content or page in current history. (`APPCOMMAND_BROWSER_FORWARD`)
+ BrowserForward,
+ /// Go to the user’s preferred home page. (`APPCOMMAND_BROWSER_HOME`)
+ BrowserHome,
+ /// Refresh the current page or content. (`APPCOMMAND_BROWSER_REFRESH`)
+ BrowserRefresh,
+ /// Call up the user’s preferred search page. (`APPCOMMAND_BROWSER_SEARCH`)
+ BrowserSearch,
+ /// Stop loading the current page or content. (`APPCOMMAND_BROWSER_STOP`)
+ BrowserStop,
+ /// The Application switch key, which provides a list of recent apps to switch between.
+ /// (`KEYCODE_APP_SWITCH`)
+ AppSwitch,
+ /// The Call key. (`KEYCODE_CALL`)
+ Call,
+ /// The Camera key. (`KEYCODE_CAMERA`)
+ Camera,
+ /// The Camera focus key. (`KEYCODE_FOCUS`)
+ CameraFocus,
+ /// The End Call key. (`KEYCODE_ENDCALL`)
+ EndCall,
+ /// The Back key. (`KEYCODE_BACK`)
+ GoBack,
+ /// The Home key, which goes to the phone’s main screen. (`KEYCODE_HOME`)
+ GoHome,
+ /// The Headset Hook key. (`KEYCODE_HEADSETHOOK`)
+ HeadsetHook,
+ LastNumberRedial,
+ /// The Notification key. (`KEYCODE_NOTIFICATION`)
+ Notification,
+ /// Toggle between manner mode state: silent, vibrate, ring, ... (`KEYCODE_MANNER_MODE`)
+ MannerMode,
+ VoiceDial,
+ /// Switch to viewing TV. (`KEYCODE_TV`)
+ TV,
+ /// TV 3D Mode. (`KEYCODE_3D_MODE`)
+ TV3DMode,
+ /// Toggle between antenna and cable input. (`KEYCODE_TV_ANTENNA_CABLE`)
+ TVAntennaCable,
+ /// Audio description. (`KEYCODE_TV_AUDIO_DESCRIPTION`)
+ TVAudioDescription,
+ /// Audio description mixing volume down. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN`)
+ TVAudioDescriptionMixDown,
+ /// Audio description mixing volume up. (`KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP`)
+ TVAudioDescriptionMixUp,
+ /// Contents menu. (`KEYCODE_TV_CONTENTS_MENU`)
+ TVContentsMenu,
+ /// Contents menu. (`KEYCODE_TV_DATA_SERVICE`)
+ TVDataService,
+ /// Switch the input mode on an external TV. (`KEYCODE_TV_INPUT`)
+ TVInput,
+ /// Switch to component input #1. (`KEYCODE_TV_INPUT_COMPONENT_1`)
+ TVInputComponent1,
+ /// Switch to component input #2. (`KEYCODE_TV_INPUT_COMPONENT_2`)
+ TVInputComponent2,
+ /// Switch to composite input #1. (`KEYCODE_TV_INPUT_COMPOSITE_1`)
+ TVInputComposite1,
+ /// Switch to composite input #2. (`KEYCODE_TV_INPUT_COMPOSITE_2`)
+ TVInputComposite2,
+ /// Switch to HDMI input #1. (`KEYCODE_TV_INPUT_HDMI_1`)
+ TVInputHDMI1,
+ /// Switch to HDMI input #2. (`KEYCODE_TV_INPUT_HDMI_2`)
+ TVInputHDMI2,
+ /// Switch to HDMI input #3. (`KEYCODE_TV_INPUT_HDMI_3`)
+ TVInputHDMI3,
+ /// Switch to HDMI input #4. (`KEYCODE_TV_INPUT_HDMI_4`)
+ TVInputHDMI4,
+ /// Switch to VGA input #1. (`KEYCODE_TV_INPUT_VGA_1`)
+ TVInputVGA1,
+ /// Media context menu. (`KEYCODE_TV_MEDIA_CONTEXT_MENU`)
+ TVMediaContext,
+ /// Toggle network. (`KEYCODE_TV_NETWORK`)
+ TVNetwork,
+ /// Number entry. (`KEYCODE_TV_NUMBER_ENTRY`)
+ TVNumberEntry,
+ /// Toggle the power on an external TV. (`KEYCODE_TV_POWER`)
+ TVPower,
+ /// Radio. (`KEYCODE_TV_RADIO_SERVICE`)
+ TVRadioService,
+ /// Satellite. (`KEYCODE_TV_SATELLITE`)
+ TVSatellite,
+ /// Broadcast Satellite. (`KEYCODE_TV_SATELLITE_BS`)
+ TVSatelliteBS,
+ /// Communication Satellite. (`KEYCODE_TV_SATELLITE_CS`)
+ TVSatelliteCS,
+ /// Toggle between available satellites. (`KEYCODE_TV_SATELLITE_SERVICE`)
+ TVSatelliteToggle,
+ /// Analog Terrestrial. (`KEYCODE_TV_TERRESTRIAL_ANALOG`)
+ TVTerrestrialAnalog,
+ /// Digital Terrestrial. (`KEYCODE_TV_TERRESTRIAL_DIGITAL`)
+ TVTerrestrialDigital,
+ /// Timer programming. (`KEYCODE_TV_TIMER_PROGRAMMING`)
+ TVTimer,
+ /// Switch the input mode on an external AVR (audio/video receiver). (`KEYCODE_AVR_INPUT`)
+ AVRInput,
+ /// Toggle the power on an external AVR (audio/video receiver). (`KEYCODE_AVR_POWER`)
+ AVRPower,
+ /// General purpose color-coded media function key, as index 0 (red). (`VK_COLORED_KEY_0`,
+ /// `KEYCODE_PROG_RED`)
+ ColorF0Red,
+ /// General purpose color-coded media function key, as index 1 (green). (`VK_COLORED_KEY_1`,
+ /// `KEYCODE_PROG_GREEN`)
+ ColorF1Green,
+ /// General purpose color-coded media function key, as index 2 (yellow). (`VK_COLORED_KEY_2`,
+ /// `KEYCODE_PROG_YELLOW`)
+ ColorF2Yellow,
+ /// General purpose color-coded media function key, as index 3 (blue). (`VK_COLORED_KEY_3`,
+ /// `KEYCODE_PROG_BLUE`)
+ ColorF3Blue,
+ /// General purpose color-coded media function key, as index 4 (grey). (`VK_COLORED_KEY_4`)
+ ColorF4Grey,
+ /// General purpose color-coded media function key, as index 5 (brown). (`VK_COLORED_KEY_5`)
+ ColorF5Brown,
+ /// Toggle the display of Closed Captions. (`VK_CC`, `KEYCODE_CAPTIONS`)
+ ClosedCaptionToggle,
+ /// Adjust brightness of device, by toggling between or cycling through states. (`VK_DIMMER`)
+ Dimmer,
+ /// Swap video sources. (`VK_DISPLAY_SWAP`)
+ DisplaySwap,
+ /// Select Digital Video Rrecorder. (`KEYCODE_DVR`)
+ DVR,
+ /// Exit the current application. (`VK_EXIT`)
+ Exit,
+ /// Clear program or content stored as favorite 0. (`VK_CLEAR_FAVORITE_0`)
+ FavoriteClear0,
+ /// Clear program or content stored as favorite 1. (`VK_CLEAR_FAVORITE_1`)
+ FavoriteClear1,
+ /// Clear program or content stored as favorite 2. (`VK_CLEAR_FAVORITE_2`)
+ FavoriteClear2,
+ /// Clear program or content stored as favorite 3. (`VK_CLEAR_FAVORITE_3`)
+ FavoriteClear3,
+ /// Select (recall) program or content stored as favorite 0. (`VK_RECALL_FAVORITE_0`)
+ FavoriteRecall0,
+ /// Select (recall) program or content stored as favorite 1. (`VK_RECALL_FAVORITE_1`)
+ FavoriteRecall1,
+ /// Select (recall) program or content stored as favorite 2. (`VK_RECALL_FAVORITE_2`)
+ FavoriteRecall2,
+ /// Select (recall) program or content stored as favorite 3. (`VK_RECALL_FAVORITE_3`)
+ FavoriteRecall3,
+ /// Store current program or content as favorite 0. (`VK_STORE_FAVORITE_0`)
+ FavoriteStore0,
+ /// Store current program or content as favorite 1. (`VK_STORE_FAVORITE_1`)
+ FavoriteStore1,
+ /// Store current program or content as favorite 2. (`VK_STORE_FAVORITE_2`)
+ FavoriteStore2,
+ /// Store current program or content as favorite 3. (`VK_STORE_FAVORITE_3`)
+ FavoriteStore3,
+ /// Toggle display of program or content guide. (`VK_GUIDE`, `KEYCODE_GUIDE`)
+ Guide,
+ /// If guide is active and displayed, then display next day’s content. (`VK_NEXT_DAY`)
+ GuideNextDay,
+ /// If guide is active and displayed, then display previous day’s content. (`VK_PREV_DAY`)
+ GuidePreviousDay,
+ /// Toggle display of information about currently selected context or media. (`VK_INFO`,
+ /// `KEYCODE_INFO`)
+ Info,
+ /// Toggle instant replay. (`VK_INSTANT_REPLAY`)
+ InstantReplay,
+ /// Launch linked content, if available and appropriate. (`VK_LINK`)
+ Link,
+ /// List the current program. (`VK_LIST`)
+ ListProgram,
+ /// Toggle display listing of currently available live content or programs. (`VK_LIVE`)
+ LiveContent,
+ /// Lock or unlock current content or program. (`VK_LOCK`)
+ Lock,
+ /// Show a list of media applications: audio/video players and image viewers. (`VK_APPS`)
+ ///
+ /// Note: Do not confuse this key value with the Windows' `VK_APPS` / `VK_CONTEXT_MENU` key,
+ /// which is encoded as `"ContextMenu"`.
+ MediaApps,
+ /// Audio track key. (`KEYCODE_MEDIA_AUDIO_TRACK`)
+ MediaAudioTrack,
+ /// Select previously selected channel or media. (`VK_LAST`, `KEYCODE_LAST_CHANNEL`)
+ MediaLast,
+ /// Skip backward to next content or program. (`KEYCODE_MEDIA_SKIP_BACKWARD`)
+ MediaSkipBackward,
+ /// Skip forward to next content or program. (`VK_SKIP`, `KEYCODE_MEDIA_SKIP_FORWARD`)
+ MediaSkipForward,
+ /// Step backward to next content or program. (`KEYCODE_MEDIA_STEP_BACKWARD`)
+ MediaStepBackward,
+ /// Step forward to next content or program. (`KEYCODE_MEDIA_STEP_FORWARD`)
+ MediaStepForward,
+ /// Media top menu. (`KEYCODE_MEDIA_TOP_MENU`)
+ MediaTopMenu,
+ /// Navigate in. (`KEYCODE_NAVIGATE_IN`)
+ NavigateIn,
+ /// Navigate to next key. (`KEYCODE_NAVIGATE_NEXT`)
+ NavigateNext,
+ /// Navigate out. (`KEYCODE_NAVIGATE_OUT`)
+ NavigateOut,
+ /// Navigate to previous key. (`KEYCODE_NAVIGATE_PREVIOUS`)
+ NavigatePrevious,
+ /// Cycle to next favorite channel (in favorites list). (`VK_NEXT_FAVORITE_CHANNEL`)
+ NextFavoriteChannel,
+ /// Cycle to next user profile (if there are multiple user profiles). (`VK_USER`)
+ NextUserProfile,
+ /// Access on-demand content or programs. (`VK_ON_DEMAND`)
+ OnDemand,
+ /// Pairing key to pair devices. (`KEYCODE_PAIRING`)
+ Pairing,
+ /// Move picture-in-picture window down. (`VK_PINP_DOWN`)
+ PinPDown,
+ /// Move picture-in-picture window. (`VK_PINP_MOVE`)
+ PinPMove,
+ /// Toggle display of picture-in-picture window. (`VK_PINP_TOGGLE`)
+ PinPToggle,
+ /// Move picture-in-picture window up. (`VK_PINP_UP`)
+ PinPUp,
+ /// Decrease media playback speed. (`VK_PLAY_SPEED_DOWN`)
+ PlaySpeedDown,
+ /// Reset playback to normal speed. (`VK_PLAY_SPEED_RESET`)
+ PlaySpeedReset,
+ /// Increase media playback speed. (`VK_PLAY_SPEED_UP`)
+ PlaySpeedUp,
+ /// Toggle random media or content shuffle mode. (`VK_RANDOM_TOGGLE`)
+ RandomToggle,
+ /// Not a physical key, but this key code is sent when the remote control battery is low.
+ /// (`VK_RC_LOW_BATTERY`)
+ RcLowBattery,
+ /// Toggle or cycle between media recording speeds. (`VK_RECORD_SPEED_NEXT`)
+ RecordSpeedNext,
+ /// Toggle RF (radio frequency) input bypass mode (pass RF input directly to the RF output).
+ /// (`VK_RF_BYPASS`)
+ RfBypass,
+ /// Toggle scan channels mode. (`VK_SCAN_CHANNELS_TOGGLE`)
+ ScanChannelsToggle,
+ /// Advance display screen mode to next available mode. (`VK_SCREEN_MODE_NEXT`)
+ ScreenModeNext,
+ /// Toggle display of device settings screen. (`VK_SETTINGS`, `KEYCODE_SETTINGS`)
+ Settings,
+ /// Toggle split screen mode. (`VK_SPLIT_SCREEN_TOGGLE`)
+ SplitScreenToggle,
+ /// Switch the input mode on an external STB (set top box). (`KEYCODE_STB_INPUT`)
+ STBInput,
+ /// Toggle the power on an external STB (set top box). (`KEYCODE_STB_POWER`)
+ STBPower,
+ /// Toggle display of subtitles, if available. (`VK_SUBTITLE`)
+ Subtitle,
+ /// Toggle display of teletext, if available (`VK_TELETEXT`, `KEYCODE_TV_TELETEXT`).
+ Teletext,
+ /// Advance video mode to next available mode. (`VK_VIDEO_MODE_NEXT`)
+ VideoModeNext,
+ /// Cause device to identify itself in some manner, e.g., audibly or visibly. (`VK_WINK`)
+ Wink,
+ /// Toggle between full-screen and scaled content, or alter magnification level. (`VK_ZOOM`,
+ /// `KEYCODE_TV_ZOOM_MODE`)
+ ZoomToggle,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F1,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F2,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F3,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F4,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F5,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F6,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F7,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F8,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F9,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F10,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F11,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F12,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F13,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F14,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F15,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F16,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F17,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F18,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F19,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F20,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F21,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F22,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F23,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F24,
+ /// General-purpose function key.
+ F25,
+ /// General-purpose function key.
+ F26,
+ /// General-purpose function key.
+ F27,
+ /// General-purpose function key.
+ F28,
+ /// General-purpose function key.
+ F29,
+ /// General-purpose function key.
+ F30,
+ /// General-purpose function key.
+ F31,
+ /// General-purpose function key.
+ F32,
+ /// General-purpose function key.
+ F33,
+ /// General-purpose function key.
+ F34,
+ /// General-purpose function key.
+ F35,
+}
+
+macro_rules! map_match {
+ (
+ $to_match:expr,
+ // Custom match arms
+ { $( $from:pat => $to:expr ),* },
+ // The enum's name
+ $prefix:path,
+ // Trivial match arms for unit variants
+ { $( $t:tt ),* }) => {
+ match $to_match {
+ $( $from => $to, )*
+ $( Key::$t => Key::$t, )*
+ }
+ };
+}
+
+impl Key {
+ /// Convert `Key::Character(SmolStr)` to `Key::Character(&str)` so you can more easily match on
+ /// `Key`. All other variants remain unchanged.
+ pub fn as_ref(&self) -> Key<&str> {
+ map_match!(
+ self,
+ {
+ Key::Character(ch) => Key::Character(ch.as_str()),
+ Key::Dead(d) => Key::Dead(*d),
+ Key::Unidentified(u) => Key::Unidentified(u.clone())
+ },
+ Key,
+ {
+ Alt, AltGraph, CapsLock, Control, Fn, FnLock, NumLock, ScrollLock, Shift, Symbol,
+ SymbolLock, Meta, Hyper, Super, Enter, Tab, Space, ArrowDown, ArrowLeft,
+ ArrowRight, ArrowUp, End, Home, PageDown, PageUp, Backspace, Clear, Copy, CrSel,
+ Cut, Delete, EraseEof, ExSel, Insert, Paste, Redo, Undo, Accept, Again, Attn,
+ Cancel, ContextMenu, Escape, Execute, Find, Help, Pause, Play, Props, Select,
+ ZoomIn, ZoomOut, BrightnessDown, BrightnessUp, Eject, LogOff, Power, PowerOff,
+ PrintScreen, Hibernate, Standby, WakeUp, AllCandidates, Alphanumeric, CodeInput,
+ Compose, Convert, FinalMode, GroupFirst, GroupLast, GroupNext, GroupPrevious,
+ ModeChange, NextCandidate, NonConvert, PreviousCandidate, Process, SingleCandidate,
+ HangulMode, HanjaMode, JunjaMode, Eisu, Hankaku, Hiragana, HiraganaKatakana,
+ KanaMode, KanjiMode, Katakana, Romaji, Zenkaku, ZenkakuHankaku, Soft1, Soft2,
+ Soft3, Soft4, ChannelDown, ChannelUp, Close, MailForward, MailReply, MailSend,
+ MediaClose, MediaFastForward, MediaPause, MediaPlay, MediaPlayPause, MediaRecord,
+ MediaRewind, MediaStop, MediaTrackNext, MediaTrackPrevious, New, Open, Print, Save,
+ SpellCheck, Key11, Key12, AudioBalanceLeft, AudioBalanceRight, AudioBassBoostDown,
+ AudioBassBoostToggle, AudioBassBoostUp, AudioFaderFront, AudioFaderRear,
+ AudioSurroundModeNext, AudioTrebleDown, AudioTrebleUp, AudioVolumeDown,
+ AudioVolumeUp, AudioVolumeMute, MicrophoneToggle, MicrophoneVolumeDown,
+ MicrophoneVolumeUp, MicrophoneVolumeMute, SpeechCorrectionList, SpeechInputToggle,
+ LaunchApplication1, LaunchApplication2, LaunchCalendar, LaunchContacts, LaunchMail,
+ LaunchMediaPlayer, LaunchMusicPlayer, LaunchPhone, LaunchScreenSaver,
+ LaunchSpreadsheet, LaunchWebBrowser, LaunchWebCam, LaunchWordProcessor,
+ BrowserBack, BrowserFavorites, BrowserForward, BrowserHome, BrowserRefresh,
+ BrowserSearch, BrowserStop, AppSwitch, Call, Camera, CameraFocus, EndCall, GoBack,
+ GoHome, HeadsetHook, LastNumberRedial, Notification, MannerMode, VoiceDial, TV,
+ TV3DMode, TVAntennaCable, TVAudioDescription, TVAudioDescriptionMixDown,
+ TVAudioDescriptionMixUp, TVContentsMenu, TVDataService, TVInput, TVInputComponent1,
+ TVInputComponent2, TVInputComposite1, TVInputComposite2, TVInputHDMI1,
+ TVInputHDMI2, TVInputHDMI3, TVInputHDMI4, TVInputVGA1, TVMediaContext, TVNetwork,
+ TVNumberEntry, TVPower, TVRadioService, TVSatellite, TVSatelliteBS, TVSatelliteCS,
+ TVSatelliteToggle, TVTerrestrialAnalog, TVTerrestrialDigital, TVTimer, AVRInput,
+ AVRPower, ColorF0Red, ColorF1Green, ColorF2Yellow, ColorF3Blue, ColorF4Grey,
+ ColorF5Brown, ClosedCaptionToggle, Dimmer, DisplaySwap, DVR, Exit, FavoriteClear0,
+ FavoriteClear1, FavoriteClear2, FavoriteClear3, FavoriteRecall0, FavoriteRecall1,
+ FavoriteRecall2, FavoriteRecall3, FavoriteStore0, FavoriteStore1, FavoriteStore2,
+ FavoriteStore3, Guide, GuideNextDay, GuidePreviousDay, Info, InstantReplay, Link,
+ ListProgram, LiveContent, Lock, MediaApps, MediaAudioTrack, MediaLast,
+ MediaSkipBackward, MediaSkipForward, MediaStepBackward, MediaStepForward,
+ MediaTopMenu, NavigateIn, NavigateNext, NavigateOut, NavigatePrevious,
+ NextFavoriteChannel, NextUserProfile, OnDemand, Pairing, PinPDown, PinPMove,
+ PinPToggle, PinPUp, PlaySpeedDown, PlaySpeedReset, PlaySpeedUp, RandomToggle,
+ RcLowBattery, RecordSpeedNext, RfBypass, ScanChannelsToggle, ScreenModeNext,
+ Settings, SplitScreenToggle, STBInput, STBPower, Subtitle, Teletext, VideoModeNext,
+ Wink, ZoomToggle, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, F14, F15,
+ F16, F17, F18, F19, F20, F21, F22, F23, F24, F25, F26, F27, F28, F29, F30, F31,
+ F32, F33, F34, F35
+ }
+ )
+ }
+}
+
+impl Key {
+ /// Convert a key to its approximate textual equivalent.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use winit::keyboard::Key;
+ ///
+ /// assert_eq!(Key::Character("a".into()).to_text(), Some("a"));
+ /// assert_eq!(Key::Enter.to_text(), Some("\r"));
+ /// assert_eq!(Key::F20.to_text(), None);
+ /// ```
+ pub fn to_text(&self) -> Option<&str> {
+ match self {
+ Key::Character(ch) => Some(ch.as_str()),
+ Key::Enter => Some("\r"),
+ Key::Backspace => Some("\x08"),
+ Key::Tab => Some("\t"),
+ Key::Space => Some(" "),
+ Key::Escape => Some("\x1b"),
+ _ => None,
+ }
+ }
+}
+
+/// The location of the key on the keyboard.
+///
+/// Certain physical keys on the keyboard can have the same value, but are in different locations.
+/// For instance, the Shift key can be on the left or right side of the keyboard, or the number
+/// keys can be above the letters or on the numpad. This enum allows the user to differentiate
+/// them.
+///
+/// See the documentation for the [`location`] field on the [`KeyEvent`] struct for more information.
+///
+/// [`location`]: ../event/struct.KeyEvent.html#structfield.location
+/// [`KeyEvent`]: crate::event::KeyEvent
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+pub enum KeyLocation {
+ /// The key is in its "normal" location on the keyboard.
+ ///
+ /// For instance, the "1" key above the "Q" key on a QWERTY keyboard will use this location. This
+ /// invariant is also returned when the location of the key cannot be identified.
+ ///
+ /// ![Standard 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_standard_1_key.svg)
+ ///
+ ///
+ /// For image attribution, see the
+ ///
+ /// ATTRIBUTION.md
+ ///
+ /// file.
+ ///
+ Standard,
+
+ /// The key is on the left side of the keyboard.
+ ///
+ /// For instance, the left Shift key below the Caps Lock key on a QWERTY keyboard will use this
+ /// location.
+ ///
+ /// ![Left Shift key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_left_shift_key.svg)
+ ///
+ ///
+ /// For image attribution, see the
+ ///
+ /// ATTRIBUTION.md
+ ///
+ /// file.
+ ///
+ Left,
+
+ /// The key is on the right side of the keyboard.
+ ///
+ /// For instance, the right Shift key below the Enter key on a QWERTY keyboard will use this
+ /// location.
+ ///
+ /// ![Right Shift key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_right_shift_key.svg)
+ ///
+ ///
+ /// For image attribution, see the
+ ///
+ /// ATTRIBUTION.md
+ ///
+ /// file.
+ ///
+ Right,
+
+ /// The key is on the numpad.
+ ///
+ /// For instance, the "1" key on the numpad will use this location.
+ ///
+ /// ![Numpad 1 key](https://raw.githubusercontent.com/rust-windowing/winit/master/docs/res/keyboard_numpad_1_key.svg)
+ ///
+ ///
+ /// For image attribution, see the
+ ///
+ /// ATTRIBUTION.md
+ ///
+ /// file.
+ ///
+ Numpad,
+}
+
+bitflags! {
+ /// Represents the current state of the keyboard modifiers
+ ///
+ /// Each flag represents a modifier and is set if this modifier is active.
+ #[derive(Default)]
+ pub struct ModifiersState: u32 {
+ /// The "shift" key.
+ const SHIFT = 0b100;
+ /// The "control" key.
+ const CONTROL = 0b100 << 3;
+ /// The "alt" key.
+ const ALT = 0b100 << 6;
+ /// This is the "windows" key on PC and "command" key on Mac.
+ const SUPER = 0b100 << 9;
+ }
+}
+
+impl ModifiersState {
+ /// Returns `true` if the shift key is pressed.
+ pub fn shift_key(&self) -> bool {
+ self.intersects(Self::SHIFT)
+ }
+ /// Returns `true` if the control key is pressed.
+ pub fn control_key(&self) -> bool {
+ self.intersects(Self::CONTROL)
+ }
+ /// Returns `true` if the alt key is pressed.
+ pub fn alt_key(&self) -> bool {
+ self.intersects(Self::ALT)
+ }
+ /// Returns `true` if the super key is pressed.
+ pub fn super_key(&self) -> bool {
+ self.intersects(Self::SUPER)
+ }
+}
+
+/// The state of the particular modifiers key.
+#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ModifiersKeyState {
+ /// The particular key is pressed.
+ Pressed,
+ /// The state of the key is unknown.
+ #[default]
+ Unknown,
+}
+
+// NOTE: the exact modifier key is not used to represent modifiers state in the
+// first place due to a fact that modifiers state could be changed without any
+// key being pressed and on some platforms like Wayland/X11 which key resulted
+// in modifiers change is hidden, also, not that it really matters.
+//
+// The reason this API is even exposed is mostly to provide a way for users
+// to treat modifiers differently based on their position, which is required
+// on macOS due to their AltGr/Option situation.
+bitflags! {
+ #[derive(Default)]
+ pub(crate) struct ModifiersKeys: u8 {
+ const LSHIFT = 0b0000_0001;
+ const RSHIFT = 0b0000_0010;
+ const LCONTROL = 0b0000_0100;
+ const RCONTROL = 0b0000_1000;
+ const LALT = 0b0001_0000;
+ const RALT = 0b0010_0000;
+ const LSUPER = 0b0100_0000;
+ const RSUPER = 0b1000_0000;
+ }
+}
+
+#[cfg(feature = "serde")]
+mod modifiers_serde {
+ use super::ModifiersState;
+ use serde::{Deserialize, Deserializer, Serialize, Serializer};
+
+ #[derive(Default, Serialize, Deserialize)]
+ #[serde(default)]
+ #[serde(rename = "ModifiersState")]
+ pub struct ModifiersStateSerialize {
+ pub shift_key: bool,
+ pub control_key: bool,
+ pub alt_key: bool,
+ pub super_key: bool,
+ }
+
+ impl Serialize for ModifiersState {
+ fn serialize(&self, serializer: S) -> Result
+ where
+ S: Serializer,
+ {
+ let s = ModifiersStateSerialize {
+ shift_key: self.shift_key(),
+ control_key: self.control_key(),
+ alt_key: self.alt_key(),
+ super_key: self.super_key(),
+ };
+ s.serialize(serializer)
+ }
+ }
+
+ impl<'de> Deserialize<'de> for ModifiersState {
+ fn deserialize(deserializer: D) -> Result
+ where
+ D: Deserializer<'de>,
+ {
+ let ModifiersStateSerialize {
+ shift_key,
+ control_key,
+ alt_key,
+ super_key,
+ } = ModifiersStateSerialize::deserialize(deserializer)?;
+ let mut m = ModifiersState::empty();
+ m.set(ModifiersState::SHIFT, shift_key);
+ m.set(ModifiersState::CONTROL, control_key);
+ m.set(ModifiersState::ALT, alt_key);
+ m.set(ModifiersState::SUPER, super_key);
+ Ok(m)
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index f6276a2f5c..9d2a93b82f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -154,6 +154,7 @@ pub mod error;
pub mod event;
pub mod event_loop;
mod icon;
+pub mod keyboard;
pub mod monitor;
mod platform_impl;
pub mod window;
diff --git a/src/platform/macos.rs b/src/platform/macos.rs
index 0b6ebf11ed..e4da83bd1c 100644
--- a/src/platform/macos.rs
+++ b/src/platform/macos.rs
@@ -56,17 +56,6 @@ pub trait WindowExtMacOS {
/// Put the window in a state which indicates a file save is required.
fn set_document_edited(&self, edited: bool);
-
- /// Set option as alt behavior as described in [`OptionAsAlt`].
- ///
- /// This will ignore diacritical marks and accent characters from
- /// being processed as received characters. Instead, the input
- /// device's raw character will be placed in event queues with the
- /// Alt modifier set.
- fn set_option_as_alt(&self, option_as_alt: OptionAsAlt);
-
- /// Getter for the [`WindowExtMacOS::set_option_as_alt`].
- fn option_as_alt(&self) -> OptionAsAlt;
}
impl WindowExtMacOS for Window {
@@ -109,16 +98,6 @@ impl WindowExtMacOS for Window {
fn set_document_edited(&self, edited: bool) {
self.window.set_document_edited(edited)
}
-
- #[inline]
- fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
- self.window.set_option_as_alt(option_as_alt)
- }
-
- #[inline]
- fn option_as_alt(&self) -> OptionAsAlt {
- self.window.option_as_alt()
- }
}
/// Corresponds to `NSApplicationActivationPolicy`.
@@ -161,11 +140,6 @@ pub trait WindowBuilderExtMacOS {
fn with_has_shadow(self, has_shadow: bool) -> WindowBuilder;
/// Window accepts click-through mouse events.
fn with_accepts_first_mouse(self, accepts_first_mouse: bool) -> WindowBuilder;
-
- /// Set whether the `OptionAsAlt` key is interpreted as the `Alt` modifier.
- ///
- /// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
- fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> WindowBuilder;
}
impl WindowBuilderExtMacOS for WindowBuilder {
@@ -225,12 +199,6 @@ impl WindowBuilderExtMacOS for WindowBuilder {
self.platform_specific.accepts_first_mouse = accepts_first_mouse;
self
}
-
- #[inline]
- fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> WindowBuilder {
- self.platform_specific.option_as_alt = option_as_alt;
- self
- }
}
pub trait EventLoopBuilderExtMacOS {
@@ -341,23 +309,3 @@ impl EventLoopWindowTargetExtMacOS for EventLoopWindowTarget {
self.p.hide_other_applications()
}
}
-
-/// Option as alt behavior.
-///
-/// The default is `None`.
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-pub enum OptionAsAlt {
- /// The left `Option` key is treated as `Alt`.
- OnlyLeft,
-
- /// The right `Option` key is treated as `Alt`.
- OnlyRight,
-
- /// Both `Option` keys are treated as `Alt`.
- Both,
-
- /// No special handling is applied for `Option` key.
- #[default]
- None,
-}
diff --git a/src/platform/mod.rs b/src/platform/mod.rs
index 91c5077739..1985b7d7d6 100644
--- a/src/platform/mod.rs
+++ b/src/platform/mod.rs
@@ -32,6 +32,7 @@ pub mod windows;
#[cfg(x11_platform)]
pub mod x11;
+pub mod modifier_supplement;
#[cfg(any(
windows_platform,
macos_platform,
@@ -41,3 +42,4 @@ pub mod x11;
orbital_platform
))]
pub mod run_return;
+pub mod scancode;
diff --git a/src/platform/modifier_supplement.rs b/src/platform/modifier_supplement.rs
new file mode 100644
index 0000000000..2b0302bf78
--- /dev/null
+++ b/src/platform/modifier_supplement.rs
@@ -0,0 +1,24 @@
+#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
+
+use crate::keyboard::Key;
+
+/// Additional methods for the `KeyEvent` which cannot be implemented on all
+/// platforms.
+pub trait KeyEventExtModifierSupplement {
+ /// Identical to `KeyEvent::text` but this is affected by Ctrl.
+ ///
+ /// For example, pressing Ctrl+a produces `Some("\x01")`.
+ fn text_with_all_modifiers(&self) -> Option<&str>;
+
+ /// This value ignores all modifiers including,
+ /// but not limited to Shift, Caps Lock,
+ /// and Ctrl. In most cases this means that the
+ /// unicode character in the resulting string is lowercase.
+ ///
+ /// This is useful for key-bindings / shortcut key combinations.
+ ///
+ /// In case `logical_key` reports `Dead`, this will still report the
+ /// key as `Character` according to the current keyboard layout. This value
+ /// cannot be `Dead`.
+ fn key_without_modifiers(&self) -> Key;
+}
diff --git a/src/platform/scancode.rs b/src/platform/scancode.rs
new file mode 100644
index 0000000000..25109a04af
--- /dev/null
+++ b/src/platform/scancode.rs
@@ -0,0 +1,28 @@
+#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
+
+// TODO: Maybe merge this with `modifier_supplement` if the two are indeed supported on the same
+// set of platforms
+
+use crate::keyboard::KeyCode;
+
+/// Additional methods for the [`KeyCode`] type that allow the user to access the platform-specific
+/// scancode.
+///
+/// [`KeyCode`]: crate::keyboard::KeyCode
+pub trait KeyCodeExtScancode {
+ /// The raw value of the platform-specific physical key identifier.
+ ///
+ /// Returns `Some(key_id)` if the conversion was succesful; returns `None` otherwise.
+ ///
+ /// ## Platform-specific
+ /// - **Windows:** A 16bit extended scancode
+ /// - **Wayland/X11**: A 32-bit X11-style keycode.
+ // TODO: Describe what this value contains for each platform
+ fn to_scancode(self) -> Option;
+
+ /// Constructs a `KeyCode` from a platform-specific physical key identifier.
+ ///
+ /// Note that this conversion may be lossy, i.e. converting the returned `KeyCode` back
+ /// using `to_scancode` might not yield the original value.
+ fn from_scancode(scancode: u32) -> KeyCode;
+}
diff --git a/src/platform/windows.rs b/src/platform/windows.rs
index 94d4f4040d..626e2fba6f 100644
--- a/src/platform/windows.rs
+++ b/src/platform/windows.rs
@@ -2,9 +2,11 @@ use std::{ffi::c_void, path::Path};
use crate::{
dpi::PhysicalSize,
- event::DeviceId,
+ event::{DeviceId, KeyEvent},
event_loop::EventLoopBuilder,
+ keyboard::Key,
monitor::MonitorHandle,
+ platform::modifier_supplement::KeyEventExtModifierSupplement,
platform_impl::WinIcon,
window::{BadIcon, Icon, Window, WindowBuilder},
};
@@ -344,3 +346,18 @@ impl IconExtWindows for Icon {
Ok(Icon { inner: win_icon })
}
}
+
+impl KeyEventExtModifierSupplement for KeyEvent {
+ #[inline]
+ fn text_with_all_modifiers(&self) -> Option<&str> {
+ self.platform_specific
+ .text_with_all_modifers
+ .as_ref()
+ .map(|s| s.as_str())
+ }
+
+ #[inline]
+ fn key_without_modifiers(&self) -> Key {
+ self.platform_specific.key_without_modifiers.clone()
+ }
+}
diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs
index 2e1924e27b..228a951085 100644
--- a/src/platform_impl/android/mod.rs
+++ b/src/platform_impl/android/mod.rs
@@ -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> = Lazy::new(|| RwLock::new(true));
-fn ndk_keycode_to_virtualkeycode(keycode: Keycode) -> Option {
- 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 {
recv: mpsc::Receiver,
first: Option,
@@ -287,6 +128,9 @@ impl RedrawRequester {
}
}
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct KeyEventExtra {}
+
pub struct EventLoop {
android_app: AndroidApp,
window_target: event_loop::EventLoopWindowTarget,
@@ -551,25 +395,48 @@ impl EventLoop {
}
}
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::();
+ // 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 EventLoop {
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,
+ }
+}
diff --git a/src/platform_impl/ios/mod.rs b/src/platform_impl/ios/mod.rs
index fc52ee9534..d94681547d 100644
--- a/src/platform_impl/ios/mod.rs
+++ b/src/platform_impl/ios/mod.rs
@@ -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 {}
diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs
index 880bd843ff..f773db5e6b 100644
--- a/src/platform_impl/ios/window.rs
+++ b/src/platform_impl/ios/window.rs
@@ -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 {
diff --git a/src/platform_impl/linux/common/keymap.rs b/src/platform_impl/linux/common/keymap.rs
new file mode 100644
index 0000000000..830cef9c7d
--- /dev/null
+++ b/src/platform_impl/linux/common/keymap.rs
@@ -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 {
+ 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,
+ }
+}
diff --git a/src/platform_impl/linux/common/mod.rs b/src/platform_impl/linux/common/mod.rs
new file mode 100644
index 0000000000..fa23919676
--- /dev/null
+++ b/src/platform_impl/linux/common/mod.rs
@@ -0,0 +1,2 @@
+pub mod keymap;
+pub mod xkb_state;
diff --git a/src/platform_impl/linux/common/xkb_state.rs b/src/platform_impl/linux/common/xkb_state.rs
new file mode 100644
index 0000000000..55e2119a99
--- /dev/null
+++ b/src/platform_impl/linux/common/xkb_state.rs
@@ -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,
+}
+
+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 {
+ 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 {
+ self.compose_feed(self.xkb_compose_state, keysym)
+ }
+
+ fn compose_feed_2(&mut self, keysym: u32) -> Option {
+ 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 {
+ 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 {
+ self.compose_status(self.xkb_compose_state)
+ }
+
+ fn compose_status(
+ &mut self,
+ xkb_compose_state: *mut ffi::xkb_compose_state,
+ ) -> Option {
+ 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 {
+ self.compose_get_utf8(self.xkb_compose_state)
+ }
+
+ fn compose_get_utf8_2(&mut self) -> Option {
+ self.compose_get_utf8(self.xkb_compose_state_2)
+ }
+
+ fn compose_get_utf8(
+ &mut self,
+ xkb_compose_state: *mut ffi::xkb_compose_state,
+ ) -> Option {
+ 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(&mut self, mut f: F) -> Option
+ 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 {
+ 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 {
+ 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 {
+ 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,
+}
+
+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 {
+ self.composed_text()
+ .unwrap_or_else(|_| self.state.keysym_to_utf8_raw(self.keysym))
+ }
+
+ pub fn text_with_all_modifiers(&mut self) -> Option {
+ // 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