From 28ff731d3d88c67ad65cc447f1b08a91f313cb51 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 00:23:26 +0700 Subject: [PATCH 01/31] keyevent: add side-aware modifier state on Windows --- src/platform_impl/windows/event_loop.rs | 8 +++--- src/platform_impl/windows/keyboard_layout.rs | 30 ++++++++++++++------ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 6d0648174c..2e78649f11 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -934,17 +934,17 @@ fn update_modifiers(window: HWND, userdata: &WindowData) { let modifiers = { let mut layouts = LAYOUT_CACHE.lock().unwrap(); - layouts.get_agnostic_mods() + layouts.get_mods() }; let mut window_state = userdata.window_state.lock().unwrap(); - if window_state.modifiers_state != modifiers { - window_state.modifiers_state = modifiers; + if window_state.modifiers_state != modifiers.state() { + window_state.modifiers_state = modifiers.state(); // Drop lock drop(window_state); - userdata.send_window_event(window, ModifiersChanged(modifiers.into())); + userdata.send_window_event(window, ModifiersChanged(modifiers)); } } diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index a31362f386..2a8d384b8d 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -40,7 +40,8 @@ use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ VK_SCROLL, VK_SELECT, VK_SEPARATOR, VK_SHIFT, VK_SLEEP, VK_SNAPSHOT, VK_SPACE, VK_SUBTRACT, VK_TAB, VK_UP, VK_VOLUME_DOWN, VK_VOLUME_MUTE, VK_VOLUME_UP, VK_XBUTTON1, VK_XBUTTON2, VK_ZOOM, }; -use winit_core::keyboard::{Key, KeyCode, ModifiersState, NamedKey, NativeKey, PhysicalKey}; +use winit_core::keyboard::{Key, KeyCode, ModifiersKeys, ModifiersState, NamedKey, NativeKey, PhysicalKey}; +use winit_core::event::Modifiers; use crate::platform_impl::{loword, primarylangid, scancode_to_physicalkey}; @@ -271,15 +272,28 @@ impl LayoutCache { } } - pub fn get_agnostic_mods(&mut self) -> ModifiersState { + pub fn get_mods(&mut self) -> Modifiers { let (_, layout) = self.get_current_layout(); let filter_out_altgr = layout.has_alt_graph && key_pressed(VK_RMENU); - let mut mods = ModifiersState::empty(); - mods.set(ModifiersState::SHIFT, key_pressed(VK_SHIFT)); - mods.set(ModifiersState::CONTROL, key_pressed(VK_CONTROL) && !filter_out_altgr); - mods.set(ModifiersState::ALT, key_pressed(VK_MENU) && !filter_out_altgr); - mods.set(ModifiersState::META, key_pressed(VK_LWIN) || key_pressed(VK_RWIN)); - mods + let mut state = ModifiersState::empty(); + let mut pressed_mods = ModifiersKeys::empty(); + + pressed_mods.set(ModifiersKeys::LSHIFT, key_pressed(VK_LSHIFT)); + pressed_mods.set(ModifiersKeys::RSHIFT, key_pressed(VK_RSHIFT)); + state.set(ModifiersState::SHIFT, pressed_mods.contains(ModifiersKeys::LSHIFT) || pressed_mods.contains(ModifiersKeys::RSHIFT)); + + pressed_mods.set(ModifiersKeys::LCONTROL, key_pressed(VK_LCONTROL) && !filter_out_altgr); + pressed_mods.set(ModifiersKeys::RCONTROL, key_pressed(VK_RCONTROL) && !filter_out_altgr); + state.set(ModifiersState::CONTROL, pressed_mods.contains(ModifiersKeys::LCONTROL) || pressed_mods.contains(ModifiersKeys::RCONTROL)); + + pressed_mods.set(ModifiersKeys::LALT, key_pressed(VK_LMENU) && !filter_out_altgr); + pressed_mods.set(ModifiersKeys::RALT, key_pressed(VK_RMENU) && !filter_out_altgr); + state.set(ModifiersState::ALT, pressed_mods.contains(ModifiersKeys::LALT) || pressed_mods.contains(ModifiersKeys::RALT)); + + pressed_mods.set(ModifiersKeys::LMETA, key_pressed(VK_LWIN)); + pressed_mods.set(ModifiersKeys::RMETA, key_pressed(VK_RWIN)); + state.set(ModifiersState::META, pressed_mods.contains(ModifiersKeys::LMETA) || pressed_mods.contains(ModifiersKeys::RMETA)); + Modifiers::new(state, pressed_mods) } fn prepare_layout(locale_id: u64) -> Layout { From a5629ee1407d6ec80cbd9feec152e945c5cc2568 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 16:34:44 +0700 Subject: [PATCH 02/31] add kbd_ev_print example --- examples/kbd_ev_print.rs | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 examples/kbd_ev_print.rs diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs new file mode 100644 index 0000000000..76c60c39e8 --- /dev/null +++ b/examples/kbd_ev_print.rs @@ -0,0 +1,84 @@ +//! Simple winit window example. + +use std::error::Error; + +use winit::application::ApplicationHandler; +use winit::event::WindowEvent; +use winit::event_loop::{ActiveEventLoop, EventLoop}; +#[cfg(web_platform)] +use winit::platform::web::WindowAttributesWeb; +use winit::window::{Window, WindowAttributes, WindowId}; + +#[path = "util/fill.rs"] +mod fill; +#[path = "util/tracing.rs"] +mod tracing; + +#[derive(Default, Debug)] +struct App { + window: Option>, +} + +impl ApplicationHandler for App { + fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { + #[cfg(not(web_platform))] + let window_attributes = WindowAttributes::default(); + #[cfg(web_platform)] + let window_attributes = WindowAttributes::default() + .with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true))); + self.window = match event_loop.create_window(window_attributes) { + Ok(window) => Some(window), + Err(err) => { + eprintln!("error creating window: {err}"); + event_loop.exit(); + return; + }, + } + } + + fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) { + println!("{event:?}"); + match event { + WindowEvent::CloseRequested => { + println!("Close was requested; stopping"); + event_loop.exit(); + }, + WindowEvent::SurfaceResized(_) => { + self.window.as_ref().expect("resize event without a window").request_redraw(); + }, + WindowEvent::RedrawRequested => { + // Redraw the application. + // + // It's preferable for applications that do not render continuously to render in + // this event rather than in AboutToWait, since rendering in here allows + // the program to gracefully handle redraws requested by the OS. + + let window = self.window.as_ref().expect("redraw request without a window"); + + // Notify that you're about to draw. + window.pre_present_notify(); + + // Draw. + fill::fill_window(window.as_ref()); + + // For contiguous redraw loop you can request a redraw from here. + // window.request_redraw(); + }, + _ => (), + } + } +} + +fn main() -> Result<(), Box> { + #[cfg(web_platform)] + console_error_panic_hook::set_once(); + + tracing::init(); + + let event_loop = EventLoop::new()?; + + // For alternative loop run options see `pump_events` and `run_on_demand` examples. + event_loop.run_app(App::default())?; + + Ok(()) +} From ae3c8da763d9ace4bc1835b6e20b3cb990e8828e Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 16:51:00 +0700 Subject: [PATCH 03/31] update example --- examples/kbd_ev_print.rs | 104 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 76c60c39e8..8e1cdd2f2b 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -1,4 +1,6 @@ -//! Simple winit window example. +//! Simple winit window example that prints keyboard events: + //! [KeyboardInput](https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.KeyboardInput) + //! [ModifiersChanged](https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.ModifiersChanged).) use std::error::Error; @@ -19,6 +21,86 @@ struct App { window: Option>, } + +use winit::event::{Modifiers, KeyEvent}; +// struct Modifiers + // state : ModifiersState, + // pressed_mods: ModifiersKeys , +// https://docs.rs/winit/latest/winit/keyboard/struct.ModifiersState.html +pub fn mod_state_side_agnostic_s(state:&ModifiersState) -> String { + let mut s = String::new(); + if state.contains(ModifiersState::SHIFT ){s.push_str(" ⇧ ")}else{s.push_str(" ")}; + if state.contains(ModifiersState::CONTROL){s.push_str(" ⎈ ")}else{s.push_str(" ")}; + if state.contains(ModifiersState::META ){s.push_str(" ◆ ")}else{s.push_str(" ")}; + if state.contains(ModifiersState::ALT ){s.push_str(" ⎇ ")}else{s.push_str(" ")}; + s +} +// https://docs.rs/winit/latest/winit/event/struct.Modifiers.html +pub fn mod_state_side_aware_s(mods:&Modifiers) -> String { + let mut s = String::new(); + if let ModifiersKeyState::Pressed = mods.lshift_state() {s.push_str("‹⇧"); + if let ModifiersKeyState::Pressed = mods.rshift_state() {s.push_str("›")}else{s.push_str(" ")}; + } else { + if let ModifiersKeyState::Pressed = mods.rshift_state() {s.push_str(" ⇧›")}else{s.push_str(" ")};} + if let ModifiersKeyState::Pressed = mods.lcontrol_state() {s.push_str("‹⎈"); + if let ModifiersKeyState::Pressed = mods.rcontrol_state() {s.push_str("›")}else{s.push_str(" ")}; + } else { + if let ModifiersKeyState::Pressed = mods.rcontrol_state() {s.push_str(" ⎈›")}else{s.push_str(" ")}; + } + if let ModifiersKeyState::Pressed = mods.lsuper_state() {s.push_str("‹◆"); + if let ModifiersKeyState::Pressed = mods.rsuper_state() {s.push_str("›")}else{s.push_str(" ")}; + } else { + if let ModifiersKeyState::Pressed = mods.rsuper_state() {s.push_str(" ◆›")}else{s.push_str(" ")}; + } + if let ModifiersKeyState::Pressed = mods.lalt_state() {s.push_str("‹⎇"); + if let ModifiersKeyState::Pressed = mods.ralt_state() {s.push_str("›")}else{s.push_str(" ")}; + } else { + if let ModifiersKeyState::Pressed = mods.ralt_state() {s.push_str(" ⎇›")}else{s.push_str(" ")}; + } + s +} +// pub struct KeyEvent + // physical_key: PhysicalKey, enum PhysicalKey + // Code ( KeyCode) + // �Unidentified(NativeKeyCode) + // logical_key: Key, enum Key + // Named(NamedKey) + // Character(Str) + // �Unidentified(NativeKey) + // 🕱Dead(Option) + // text : Option + // location: KeyLocation, enum KeyLocation Standard,Left,Right,Numpad + // state : ElementState, pressed/released + //🔁repeat : bool +use winit::keyboard::{PhysicalKey, Key, ModifiersState, ModifiersKeyState, KeyLocation}; +use winit::event::ElementState; +pub fn ev_key_s(key:&KeyEvent) -> String { + let mut s = String::new(); + match &key.state { + ElementState::Pressed => {s.push('↓')}, + ElementState::Released => {s.push('↑')}, + } + if key.repeat {s.push('🔁')}else{s.push(' ')}; //𜱣⚛ + match &key.physical_key { + PhysicalKey::Code (key_code ) => {s.push_str(&format!( "{:?} " ,key_code ))}, + PhysicalKey::Unidentified(key_code_native ) => {s.push_str(&format!("�{:?} ",key_code_native))}, + }; + match &key.logical_key { + Key ::Named (key_named ) => {s.push_str(&format!("{:?} " ,key_named ))}, + Key ::Character (key_char ) => {s.push_str(&format!("{} " ,key_char ))}, + Key ::Unidentified(key_native ) => {s.push_str(&format!("�{:?} ",key_native ))}, + Key ::Dead (maybe_char ) => {s.push_str(&format!("🕱{:?} " ,maybe_char ))}, + }; + match &key.location { + KeyLocation::Standard => {s.push('≝')}, + KeyLocation::Left => {s.push('←')}, + KeyLocation::Right => {s.push('→')}, + KeyLocation::Numpad => {s.push('🔢')}, + } + s +} + +use winit_core::keyboard::NamedKey; impl ApplicationHandler for App { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { #[cfg(not(web_platform))] @@ -37,10 +119,26 @@ impl ApplicationHandler for App { } fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) { - println!("{event:?}"); match event { + WindowEvent::ModifiersChanged(mods) => { + let state = mods.state(); + let state_s = mod_state_side_agnostic_s(&state); + let pressed_mods_s = mod_state_side_aware_s(&mods); + println!("Δ {}\tside-agnostic\n {}\tside-aware",state_s, pressed_mods_s); + }, + WindowEvent::KeyboardInput { event, is_synthetic, .. } => { + let is_synthetic_s = if is_synthetic{"⚗"}else{" "}; + let key_event_s = ev_key_s(&event); + println!("🖮 {}{}",is_synthetic_s,key_event_s); + + match event.logical_key.as_ref() { + Key::Named(NamedKey::Escape) => { + event_loop.exit(); + }, + _ => (), + } + }, WindowEvent::CloseRequested => { - println!("Close was requested; stopping"); event_loop.exit(); }, WindowEvent::SurfaceResized(_) => { From 729f04b006e29ed2d2f6b71727d628335e037e60 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 17:10:14 +0700 Subject: [PATCH 04/31] fmt example --- examples/kbd_ev_print.rs | 210 +++++++++++++++++++++++++-------------- 1 file changed, 134 insertions(+), 76 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 8e1cdd2f2b..496e1eccc3 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -1,6 +1,6 @@ //! Simple winit window example that prints keyboard events: - //! [KeyboardInput](https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.KeyboardInput) - //! [ModifiersChanged](https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.ModifiersChanged).) +//! [KeyboardInput](https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.KeyboardInput) +//! [ModifiersChanged](https://docs.rs/winit/latest/winit/event/enum.WindowEvent.html#variant.ModifiersChanged).) use std::error::Error; @@ -21,83 +21,141 @@ struct App { window: Option>, } - -use winit::event::{Modifiers, KeyEvent}; +use winit::event::{KeyEvent, Modifiers}; // struct Modifiers - // state : ModifiersState, - // pressed_mods: ModifiersKeys , +// state : ModifiersState, +// pressed_mods: ModifiersKeys , // https://docs.rs/winit/latest/winit/keyboard/struct.ModifiersState.html -pub fn mod_state_side_agnostic_s(state:&ModifiersState) -> String { - let mut s = String::new(); - if state.contains(ModifiersState::SHIFT ){s.push_str(" ⇧ ")}else{s.push_str(" ")}; - if state.contains(ModifiersState::CONTROL){s.push_str(" ⎈ ")}else{s.push_str(" ")}; - if state.contains(ModifiersState::META ){s.push_str(" ◆ ")}else{s.push_str(" ")}; - if state.contains(ModifiersState::ALT ){s.push_str(" ⎇ ")}else{s.push_str(" ")}; - s +pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { + let mut s = String::new(); + if state.contains(ModifiersState::SHIFT) { + s.push_str(" ⇧ ") + } else { + s.push_str(" ") + }; + if state.contains(ModifiersState::CONTROL) { + s.push_str(" ⎈ ") + } else { + s.push_str(" ") + }; + if state.contains(ModifiersState::META) { + s.push_str(" ◆ ") + } else { + s.push_str(" ") + }; + if state.contains(ModifiersState::ALT) { + s.push_str(" ⎇ ") + } else { + s.push_str(" ") + }; + s } // https://docs.rs/winit/latest/winit/event/struct.Modifiers.html -pub fn mod_state_side_aware_s(mods:&Modifiers) -> String { - let mut s = String::new(); - if let ModifiersKeyState::Pressed = mods.lshift_state() {s.push_str("‹⇧"); - if let ModifiersKeyState::Pressed = mods.rshift_state() {s.push_str("›")}else{s.push_str(" ")}; - } else { - if let ModifiersKeyState::Pressed = mods.rshift_state() {s.push_str(" ⇧›")}else{s.push_str(" ")};} - if let ModifiersKeyState::Pressed = mods.lcontrol_state() {s.push_str("‹⎈"); - if let ModifiersKeyState::Pressed = mods.rcontrol_state() {s.push_str("›")}else{s.push_str(" ")}; - } else { - if let ModifiersKeyState::Pressed = mods.rcontrol_state() {s.push_str(" ⎈›")}else{s.push_str(" ")}; - } - if let ModifiersKeyState::Pressed = mods.lsuper_state() {s.push_str("‹◆"); - if let ModifiersKeyState::Pressed = mods.rsuper_state() {s.push_str("›")}else{s.push_str(" ")}; - } else { - if let ModifiersKeyState::Pressed = mods.rsuper_state() {s.push_str(" ◆›")}else{s.push_str(" ")}; - } - if let ModifiersKeyState::Pressed = mods.lalt_state() {s.push_str("‹⎇"); - if let ModifiersKeyState::Pressed = mods.ralt_state() {s.push_str("›")}else{s.push_str(" ")}; - } else { - if let ModifiersKeyState::Pressed = mods.ralt_state() {s.push_str(" ⎇›")}else{s.push_str(" ")}; - } - s +pub fn mod_state_side_aware_s(mods: &Modifiers) -> String { + let mut s = String::new(); + if let ModifiersKeyState::Pressed = mods.lshift_state() { + s.push_str("‹⇧"); + if let ModifiersKeyState::Pressed = mods.rshift_state() { + s.push_str("›") + } else { + s.push_str(" ") + }; + } else { + if let ModifiersKeyState::Pressed = mods.rshift_state() { + s.push_str(" ⇧›") + } else { + s.push_str(" ") + }; + } + if let ModifiersKeyState::Pressed = mods.lcontrol_state() { + s.push_str("‹⎈"); + if let ModifiersKeyState::Pressed = mods.rcontrol_state() { + s.push_str("›") + } else { + s.push_str(" ") + }; + } else { + if let ModifiersKeyState::Pressed = mods.rcontrol_state() { + s.push_str(" ⎈›") + } else { + s.push_str(" ") + }; + } + if let ModifiersKeyState::Pressed = mods.lsuper_state() { + s.push_str("‹◆"); + if let ModifiersKeyState::Pressed = mods.rsuper_state() { + s.push_str("›") + } else { + s.push_str(" ") + }; + } else { + if let ModifiersKeyState::Pressed = mods.rsuper_state() { + s.push_str(" ◆›") + } else { + s.push_str(" ") + }; + } + if let ModifiersKeyState::Pressed = mods.lalt_state() { + s.push_str("‹⎇"); + if let ModifiersKeyState::Pressed = mods.ralt_state() { + s.push_str("›") + } else { + s.push_str(" ") + }; + } else { + if let ModifiersKeyState::Pressed = mods.ralt_state() { + s.push_str(" ⎇›") + } else { + s.push_str(" ") + }; + } + s } // pub struct KeyEvent - // physical_key: PhysicalKey, enum PhysicalKey - // Code ( KeyCode) - // �Unidentified(NativeKeyCode) - // logical_key: Key, enum Key - // Named(NamedKey) - // Character(Str) - // �Unidentified(NativeKey) - // 🕱Dead(Option) - // text : Option - // location: KeyLocation, enum KeyLocation Standard,Left,Right,Numpad - // state : ElementState, pressed/released - //🔁repeat : bool -use winit::keyboard::{PhysicalKey, Key, ModifiersState, ModifiersKeyState, KeyLocation}; +// physical_key: PhysicalKey, enum PhysicalKey +// Code ( KeyCode) +// �Unidentified(NativeKeyCode) +// logical_key: Key, enum Key +// Named(NamedKey) +// Character(Str) +// �Unidentified(NativeKey) +// 🕱Dead(Option) +// text : Option +// location: KeyLocation, enum KeyLocation Standard,Left,Right,Numpad +// state : ElementState, pressed/released +//🔁repeat : bool use winit::event::ElementState; -pub fn ev_key_s(key:&KeyEvent) -> String { - let mut s = String::new(); - match &key.state { - ElementState::Pressed => {s.push('↓')}, - ElementState::Released => {s.push('↑')}, - } - if key.repeat {s.push('🔁')}else{s.push(' ')}; //𜱣⚛ - match &key.physical_key { - PhysicalKey::Code (key_code ) => {s.push_str(&format!( "{:?} " ,key_code ))}, - PhysicalKey::Unidentified(key_code_native ) => {s.push_str(&format!("�{:?} ",key_code_native))}, - }; - match &key.logical_key { - Key ::Named (key_named ) => {s.push_str(&format!("{:?} " ,key_named ))}, - Key ::Character (key_char ) => {s.push_str(&format!("{} " ,key_char ))}, - Key ::Unidentified(key_native ) => {s.push_str(&format!("�{:?} ",key_native ))}, - Key ::Dead (maybe_char ) => {s.push_str(&format!("🕱{:?} " ,maybe_char ))}, - }; - match &key.location { - KeyLocation::Standard => {s.push('≝')}, - KeyLocation::Left => {s.push('←')}, - KeyLocation::Right => {s.push('→')}, - KeyLocation::Numpad => {s.push('🔢')}, - } - s +use winit::keyboard::{Key, KeyLocation, ModifiersKeyState, ModifiersState, PhysicalKey}; +pub fn ev_key_s(key: &KeyEvent) -> String { + let mut s = String::new(); + match &key.state { + ElementState::Pressed => s.push('↓'), + ElementState::Released => s.push('↑'), + } + if key.repeat { + s.push('🔁') + } else { + s.push(' ') + }; //𜱣⚛ + match &key.physical_key { + PhysicalKey::Code(key_code) => s.push_str(&format!("{:?} ", key_code)), + PhysicalKey::Unidentified(key_code_native) => { + s.push_str(&format!("�{:?} ", key_code_native)) + }, + }; + match &key.logical_key { + Key::Named(key_named) => s.push_str(&format!("{:?} ", key_named)), + Key::Character(key_char) => s.push_str(&format!("{} ", key_char)), + Key::Unidentified(key_native) => s.push_str(&format!("�{:?} ", key_native)), + Key::Dead(maybe_char) => s.push_str(&format!("🕱{:?} ", maybe_char)), + }; + match &key.location { + KeyLocation::Standard => s.push('≝'), + KeyLocation::Left => s.push('←'), + KeyLocation::Right => s.push('→'), + KeyLocation::Numpad => s.push('🔢'), + } + s } use winit_core::keyboard::NamedKey; @@ -121,15 +179,15 @@ impl ApplicationHandler for App { fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) { match event { WindowEvent::ModifiersChanged(mods) => { - let state = mods.state(); + let state = mods.state(); let state_s = mod_state_side_agnostic_s(&state); let pressed_mods_s = mod_state_side_aware_s(&mods); - println!("Δ {}\tside-agnostic\n {}\tside-aware",state_s, pressed_mods_s); + println!("Δ {}\tside-agnostic\n {}\tside-aware", state_s, pressed_mods_s); }, WindowEvent::KeyboardInput { event, is_synthetic, .. } => { - let is_synthetic_s = if is_synthetic{"⚗"}else{" "}; + let is_synthetic_s = if is_synthetic { "⚗" } else { " " }; let key_event_s = ev_key_s(&event); - println!("🖮 {}{}",is_synthetic_s,key_event_s); + println!("🖮 {}{}", is_synthetic_s, key_event_s); match event.logical_key.as_ref() { Key::Named(NamedKey::Escape) => { From 57fdf68231544e9aefb460ff5c914ba9805243ec Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 17:10:16 +0700 Subject: [PATCH 05/31] fmt --- src/platform_impl/windows/keyboard_layout.rs | 28 ++++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 2a8d384b8d..655003578f 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -40,8 +40,10 @@ use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ VK_SCROLL, VK_SELECT, VK_SEPARATOR, VK_SHIFT, VK_SLEEP, VK_SNAPSHOT, VK_SPACE, VK_SUBTRACT, VK_TAB, VK_UP, VK_VOLUME_DOWN, VK_VOLUME_MUTE, VK_VOLUME_UP, VK_XBUTTON1, VK_XBUTTON2, VK_ZOOM, }; -use winit_core::keyboard::{Key, KeyCode, ModifiersKeys, ModifiersState, NamedKey, NativeKey, PhysicalKey}; use winit_core::event::Modifiers; +use winit_core::keyboard::{ + Key, KeyCode, ModifiersKeys, ModifiersState, NamedKey, NativeKey, PhysicalKey, +}; use crate::platform_impl::{loword, primarylangid, scancode_to_physicalkey}; @@ -280,19 +282,35 @@ impl LayoutCache { pressed_mods.set(ModifiersKeys::LSHIFT, key_pressed(VK_LSHIFT)); pressed_mods.set(ModifiersKeys::RSHIFT, key_pressed(VK_RSHIFT)); - state.set(ModifiersState::SHIFT, pressed_mods.contains(ModifiersKeys::LSHIFT) || pressed_mods.contains(ModifiersKeys::RSHIFT)); + state.set( + ModifiersState::SHIFT, + pressed_mods.contains(ModifiersKeys::LSHIFT) + || pressed_mods.contains(ModifiersKeys::RSHIFT), + ); pressed_mods.set(ModifiersKeys::LCONTROL, key_pressed(VK_LCONTROL) && !filter_out_altgr); pressed_mods.set(ModifiersKeys::RCONTROL, key_pressed(VK_RCONTROL) && !filter_out_altgr); - state.set(ModifiersState::CONTROL, pressed_mods.contains(ModifiersKeys::LCONTROL) || pressed_mods.contains(ModifiersKeys::RCONTROL)); + state.set( + ModifiersState::CONTROL, + pressed_mods.contains(ModifiersKeys::LCONTROL) + || pressed_mods.contains(ModifiersKeys::RCONTROL), + ); pressed_mods.set(ModifiersKeys::LALT, key_pressed(VK_LMENU) && !filter_out_altgr); pressed_mods.set(ModifiersKeys::RALT, key_pressed(VK_RMENU) && !filter_out_altgr); - state.set(ModifiersState::ALT, pressed_mods.contains(ModifiersKeys::LALT) || pressed_mods.contains(ModifiersKeys::RALT)); + state.set( + ModifiersState::ALT, + pressed_mods.contains(ModifiersKeys::LALT) + || pressed_mods.contains(ModifiersKeys::RALT), + ); pressed_mods.set(ModifiersKeys::LMETA, key_pressed(VK_LWIN)); pressed_mods.set(ModifiersKeys::RMETA, key_pressed(VK_RWIN)); - state.set(ModifiersState::META, pressed_mods.contains(ModifiersKeys::LMETA) || pressed_mods.contains(ModifiersKeys::RMETA)); + state.set( + ModifiersState::META, + pressed_mods.contains(ModifiersKeys::LMETA) + || pressed_mods.contains(ModifiersKeys::RMETA), + ); Modifiers::new(state, pressed_mods) } From 7f9788a87f2ffd5a7dbd31f35b65b508f5d2a95f Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 17:15:45 +0700 Subject: [PATCH 06/31] Windows: update side-aware `event::Modifiers` information on state change. --- src/changelog/unreleased.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 67ea934912..13f80dd3d1 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -201,6 +201,7 @@ changelog entry. - Move `IconExtWindows` into `WinIcon`. - Move `EventLoopExtPumpEvents` and `PumpStatus` from platform module to `winit::event_loop::pump_events`. - Move `EventLoopExtRunOnDemand` from platform module to `winit::event_loop::run_on_demand`. +- On Windows, update side-aware `event::Modifiers` information on state change. ### Removed From 19d9ce087eede692432ff2059b972f5d4c68384c Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 17:24:07 +0700 Subject: [PATCH 07/31] clippy --- examples/kbd_ev_print.rs | 55 ++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 496e1eccc3..8606cf10b4 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -56,58 +56,50 @@ pub fn mod_state_side_aware_s(mods: &Modifiers) -> String { if let ModifiersKeyState::Pressed = mods.lshift_state() { s.push_str("‹⇧"); if let ModifiersKeyState::Pressed = mods.rshift_state() { - s.push_str("›") + s.push('›') } else { - s.push_str(" ") + s.push(' ') }; + } else if let ModifiersKeyState::Pressed = mods.rshift_state() { + s.push_str(" ⇧›") } else { - if let ModifiersKeyState::Pressed = mods.rshift_state() { - s.push_str(" ⇧›") - } else { - s.push_str(" ") - }; + s.push_str(" ") } if let ModifiersKeyState::Pressed = mods.lcontrol_state() { s.push_str("‹⎈"); if let ModifiersKeyState::Pressed = mods.rcontrol_state() { - s.push_str("›") + s.push('›') } else { - s.push_str(" ") + s.push(' ') }; + } else if let ModifiersKeyState::Pressed = mods.rcontrol_state() { + s.push_str(" ⎈›") } else { - if let ModifiersKeyState::Pressed = mods.rcontrol_state() { - s.push_str(" ⎈›") - } else { - s.push_str(" ") - }; + s.push_str(" ") } if let ModifiersKeyState::Pressed = mods.lsuper_state() { s.push_str("‹◆"); if let ModifiersKeyState::Pressed = mods.rsuper_state() { - s.push_str("›") + s.push('›') } else { - s.push_str(" ") + s.push(' ') }; + } else if let ModifiersKeyState::Pressed = mods.rsuper_state() { + s.push_str(" ◆›") } else { - if let ModifiersKeyState::Pressed = mods.rsuper_state() { - s.push_str(" ◆›") - } else { - s.push_str(" ") - }; + s.push_str(" ") } if let ModifiersKeyState::Pressed = mods.lalt_state() { s.push_str("‹⎇"); if let ModifiersKeyState::Pressed = mods.ralt_state() { - s.push_str("›") + s.push('›') } else { - s.push_str(" ") + s.push(' ') }; + } else if let ModifiersKeyState::Pressed = mods.ralt_state() { + s.push_str(" ⎇›") } else { - if let ModifiersKeyState::Pressed = mods.ralt_state() { - s.push_str(" ⎇›") - } else { - s.push_str(" ") - }; + s.push_str(" ") } s } @@ -189,11 +181,8 @@ impl ApplicationHandler for App { let key_event_s = ev_key_s(&event); println!("🖮 {}{}", is_synthetic_s, key_event_s); - match event.logical_key.as_ref() { - Key::Named(NamedKey::Escape) => { - event_loop.exit(); - }, - _ => (), + if let Key::Named(NamedKey::Escape) = event.logical_key.as_ref() { + event_loop.exit(); } }, WindowEvent::CloseRequested => { From f54ff8804240ba393695c47f39d7e871bd9ca94b Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 17:27:50 +0700 Subject: [PATCH 08/31] don't quit on Escape --- examples/kbd_ev_print.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 8606cf10b4..44b1fffab8 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -150,7 +150,6 @@ pub fn ev_key_s(key: &KeyEvent) -> String { s } -use winit_core::keyboard::NamedKey; impl ApplicationHandler for App { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { #[cfg(not(web_platform))] @@ -180,10 +179,6 @@ impl ApplicationHandler for App { let is_synthetic_s = if is_synthetic { "⚗" } else { " " }; let key_event_s = ev_key_s(&event); println!("🖮 {}{}", is_synthetic_s, key_event_s); - - if let Key::Named(NamedKey::Escape) = event.logical_key.as_ref() { - event_loop.exit(); - } }, WindowEvent::CloseRequested => { event_loop.exit(); From d6c38257c6e55cae15377fcf32d3d3a4ea8c0b6b Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 18:04:43 +0700 Subject: [PATCH 09/31] add more info to examples --- examples/kbd_ev_print.rs | 41 ++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 44b1fffab8..015814442a 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -129,18 +129,40 @@ pub fn ev_key_s(key: &KeyEvent) -> String { } else { s.push(' ') }; //𜱣⚛ + s.push(' '); match &key.physical_key { - PhysicalKey::Code(key_code) => s.push_str(&format!("{:?} ", key_code)), + PhysicalKey::Code(key_code) => s.push_str(&format!("{:?}", key_code)), PhysicalKey::Unidentified(key_code_native) => { - s.push_str(&format!("�{:?} ", key_code_native)) + s.push_str(&format!("�{:?}", key_code_native)) }, }; + s.push(' '); match &key.logical_key { - Key::Named(key_named) => s.push_str(&format!("{:?} ", key_named)), - Key::Character(key_char) => s.push_str(&format!("{} ", key_char)), - Key::Unidentified(key_native) => s.push_str(&format!("�{:?} ", key_native)), - Key::Dead(maybe_char) => s.push_str(&format!("🕱{:?} ", maybe_char)), + Key::Named(key_named) => s.push_str(&format!("{:?}", key_named)), + Key::Character(key_char) => s.push_str(&format!("{}", key_char)), + Key::Unidentified(key_native) => s.push_str(&format!("�{:?}", key_native)), + Key::Dead(maybe_char) => s.push_str(&format!("🕱{:?}", maybe_char)), }; + s.push_str(" "); + if let Some(txt) = &key.text { + s.push_str(&format!("{}", txt)); + } else { + s.push(' '); + } + s.push(' '); + if let Some(txt) = &key.text_with_all_modifiers { + s.push_str(&format!("{}", txt)); + } else { + s.push(' '); + } + s.push(' '); + match &key.key_without_modifiers { + Key::Named(key_named) => s.push_str(&format!("{:?}", key_named)), + Key::Character(key_char) => s.push_str(&format!("{}", key_char)), + Key::Unidentified(key_native) => s.push_str(&format!("�{:?}", key_native)), + Key::Dead(maybe_char) => s.push_str(&format!("🕱{:?}", maybe_char)), + }; + s.push_str(" "); match &key.location { KeyLocation::Standard => s.push('≝'), KeyLocation::Left => s.push('←'), @@ -217,6 +239,13 @@ fn main() -> Result<(), Box> { let event_loop = EventLoop::new()?; + println!( + "Δ is ModifiersChanged event, showing (line #1) side-agnostic modifier state as well as \ + (#2) side-aware one." + ); + println!("🖮 is KeyboardInput: ⚗=synthetic, ↓↑=pressed/released 🔁=repeat"); + println!(" phys logic txt +mod −mod location"); + // For alternative loop run options see `pump_events` and `run_on_demand` examples. event_loop.run_app(App::default())?; From 8b891bab68a0f0faf25f5537029d1ab23614d3dd Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 17:25:57 +0700 Subject: [PATCH 10/31] update changelog --- src/changelog/unreleased.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 13f80dd3d1..3df56f7002 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -81,6 +81,7 @@ changelog entry. - `keyboard::ModifiersKey` to track which modifier is exactly pressed. - `ActivationToken::as_raw` to get a ref to raw token. - Each platform now has corresponding `WindowAttributes` struct instead of trait extension. +- On Windows, update side-aware `event::Modifiers` information on state change. ### Changed @@ -201,7 +202,6 @@ changelog entry. - Move `IconExtWindows` into `WinIcon`. - Move `EventLoopExtPumpEvents` and `PumpStatus` from platform module to `winit::event_loop::pump_events`. - Move `EventLoopExtRunOnDemand` from platform module to `winit::event_loop::run_on_demand`. -- On Windows, update side-aware `event::Modifiers` information on state change. ### Removed From 2ce6f6e7d34245414f0ca5cef3b254c0528c80e4 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 17:35:48 +0700 Subject: [PATCH 11/31] undo removing into --- src/platform_impl/windows/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 2e78649f11..48c62f7657 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -944,7 +944,7 @@ fn update_modifiers(window: HWND, userdata: &WindowData) { // Drop lock drop(window_state); - userdata.send_window_event(window, ModifiersChanged(modifiers)); + userdata.send_window_event(window, ModifiersChanged(modifiers.into())); } } From e4139a63297c3d0d9cfa638ff02c447bbfa0c6e3 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 18:02:26 +0700 Subject: [PATCH 12/31] clippy --- src/platform_impl/windows/event_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 48c62f7657..2e78649f11 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -944,7 +944,7 @@ fn update_modifiers(window: HWND, userdata: &WindowData) { // Drop lock drop(window_state); - userdata.send_window_event(window, ModifiersChanged(modifiers.into())); + userdata.send_window_event(window, ModifiersChanged(modifiers)); } } From db353a6126e0f2efd069889cf2ea62d1d8a9d926 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 18:54:24 +0700 Subject: [PATCH 13/31] save 1 keystate call for Shift --- src/platform_impl/windows/keyboard_layout.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 655003578f..b929d34c6b 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -280,12 +280,14 @@ impl LayoutCache { let mut state = ModifiersState::empty(); let mut pressed_mods = ModifiersKeys::empty(); - pressed_mods.set(ModifiersKeys::LSHIFT, key_pressed(VK_LSHIFT)); - pressed_mods.set(ModifiersKeys::RSHIFT, key_pressed(VK_RSHIFT)); - state.set( - ModifiersState::SHIFT, - pressed_mods.contains(ModifiersKeys::LSHIFT) - || pressed_mods.contains(ModifiersKeys::RSHIFT), + state.set(ModifiersState::SHIFT, key_pressed(VK_SHIFT)); + pressed_mods.set( + ModifiersKeys::LSHIFT, + state.contains(ModifiersState::SHIFT) && key_pressed(VK_LSHIFT), + ); + pressed_mods.set( + ModifiersKeys::RSHIFT, + state.contains(ModifiersState::SHIFT) && key_pressed(VK_RSHIFT), ); pressed_mods.set(ModifiersKeys::LCONTROL, key_pressed(VK_LCONTROL) && !filter_out_altgr); From 96feb0015aa741d9f8fb0ca4df88d8093d5027a4 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 01:56:41 +0700 Subject: [PATCH 14/31] add side-aware keys comparison on mod update --- src/platform_impl/windows/event_loop.rs | 9 ++++++++- src/platform_impl/windows/window_state.rs | 4 +++- winit-core/src/event.rs | 5 +++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 2e78649f11..2e05e3c2bc 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -937,10 +937,17 @@ fn update_modifiers(window: HWND, userdata: &WindowData) { layouts.get_mods() }; + let mut send_event = false; let mut window_state = userdata.window_state.lock().unwrap(); + if window_state.modifiers_keys != modifiers.pressed_mods() { + window_state.modifiers_keys = modifiers.pressed_mods(); + send_event = true; + } if window_state.modifiers_state != modifiers.state() { window_state.modifiers_state = modifiers.state(); - + send_event = true; + } + if send_event { // Drop lock drop(window_state); diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index a0ca3a539e..52ad4f8c59 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -17,7 +17,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ WS_MINIMIZEBOX, WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SIZEBOX, WS_SYSMENU, WS_VISIBLE, }; use winit_core::icon::Icon; -use winit_core::keyboard::ModifiersState; +use winit_core::keyboard::{ModifiersKeys, ModifiersState}; use winit_core::monitor::Fullscreen; use winit_core::window::{Theme, WindowAttributes}; @@ -41,6 +41,7 @@ pub(crate) struct WindowState { pub scale_factor: f64, pub modifiers_state: ModifiersState, + pub modifiers_keys: ModifiersKeys, pub fullscreen: Option, pub current_theme: Theme, pub preferred_theme: Option, @@ -172,6 +173,7 @@ impl WindowState { scale_factor, modifiers_state: ModifiersState::default(), + modifiers_keys: ModifiersKeys::default(), fullscreen: None, current_theme, preferred_theme, diff --git a/winit-core/src/event.rs b/winit-core/src/event.rs index 416723749c..68de2f63bb 100644 --- a/winit-core/src/event.rs +++ b/winit-core/src/event.rs @@ -825,6 +825,11 @@ impl Modifiers { self.state } + /// The logical state of the modifier keys. + pub fn pressed_mods(&self) -> ModifiersKeys { + self.pressed_mods + } + /// The logical state of the left shift key. pub fn lshift_state(&self) -> ModifiersKeyState { self.mod_state(ModifiersKeys::LSHIFT) From 6a3ab480565308be2fd085c3df81a19309b5ff39 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 23:13:06 +0700 Subject: [PATCH 15/31] add AltGr to the example --- examples/kbd_ev_print.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 015814442a..61e4907ae9 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -48,6 +48,11 @@ pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { } else { s.push_str(" ") }; + if state.contains(ModifiersState::ALTGR) { + s.push_str("⎇Gr") + } else { + s.push_str(" ") + }; s } // https://docs.rs/winit/latest/winit/event/struct.Modifiers.html @@ -101,6 +106,7 @@ pub fn mod_state_side_aware_s(mods: &Modifiers) -> String { } else { s.push_str(" ") } + s.push_str(" "); s } // pub struct KeyEvent From 5c96dfc503676ab56441be2d04526717cf43e625 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Mon, 19 May 2025 23:13:15 +0700 Subject: [PATCH 16/31] add ALTGR modifier flag --- winit-core/src/keyboard.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/winit-core/src/keyboard.rs b/winit-core/src/keyboard.rs index 156a32d4ad..6f3e85667b 100644 --- a/winit-core/src/keyboard.rs +++ b/winit-core/src/keyboard.rs @@ -1705,6 +1705,8 @@ bitflags! { const ALT = 0b100 << 6; /// This is the "windows" key on PC and "command" key on Mac. const META = 0b100 << 9; + /// The "AltGraph" key, usually used to insert symbols. + const ALTGR = 0b100 << 10; #[deprecated = "use META instead"] const SUPER = Self::META.bits(); } From 8aecc93fea8f9b674a4d93371560eb12dde7a1ff Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 01:15:43 +0700 Subject: [PATCH 17/31] add ALTGR to modifiersstate --- src/platform_impl/windows/keyboard_layout.rs | 29 ++++++++++++-------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index b929d34c6b..ca312ab721 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -276,7 +276,6 @@ impl LayoutCache { pub fn get_mods(&mut self) -> Modifiers { let (_, layout) = self.get_current_layout(); - let filter_out_altgr = layout.has_alt_graph && key_pressed(VK_RMENU); let mut state = ModifiersState::empty(); let mut pressed_mods = ModifiersKeys::empty(); @@ -290,21 +289,29 @@ impl LayoutCache { state.contains(ModifiersState::SHIFT) && key_pressed(VK_RSHIFT), ); - pressed_mods.set(ModifiersKeys::LCONTROL, key_pressed(VK_LCONTROL) && !filter_out_altgr); - pressed_mods.set(ModifiersKeys::RCONTROL, key_pressed(VK_RCONTROL) && !filter_out_altgr); - state.set( - ModifiersState::CONTROL, - pressed_mods.contains(ModifiersKeys::LCONTROL) - || pressed_mods.contains(ModifiersKeys::RCONTROL), - ); - - pressed_mods.set(ModifiersKeys::LALT, key_pressed(VK_LMENU) && !filter_out_altgr); - pressed_mods.set(ModifiersKeys::RALT, key_pressed(VK_RMENU) && !filter_out_altgr); + pressed_mods.set(ModifiersKeys::LALT, key_pressed(VK_LMENU)); + let is_ralt = key_pressed(VK_RMENU); + let is_altgr = layout.has_alt_graph && is_ralt; + pressed_mods.set(ModifiersKeys::RALT, is_ralt && !is_altgr); state.set( ModifiersState::ALT, pressed_mods.contains(ModifiersKeys::LALT) || pressed_mods.contains(ModifiersKeys::RALT), ); + state.set(ModifiersState::ALTGR, is_altgr); + + // On Windows AltGr = RAlt + LCtrl, and OS sends artificial LCtrl key event, which needs to + // be filtered out without touching "real" LCtrl events to allow separate bindings of + // LCtrl+AltGr+X and AltGr+X. TODO: this is likely only possible by tracking the + // physical LCtrl state via raw keyboard events as the message loop isn't capable of + // excluding artificial LCtrl events? + pressed_mods.set(ModifiersKeys::RCONTROL, key_pressed(VK_RCONTROL)); + pressed_mods.set(ModifiersKeys::LCONTROL, key_pressed(VK_LCONTROL) && !is_altgr); + state.set( + ModifiersState::CONTROL, + pressed_mods.contains(ModifiersKeys::LCONTROL) + || pressed_mods.contains(ModifiersKeys::RCONTROL), + ); pressed_mods.set(ModifiersKeys::LMETA, key_pressed(VK_LWIN)); pressed_mods.set(ModifiersKeys::RMETA, key_pressed(VK_RWIN)); From 4fdc15e93735665003c5c257c5fdb04a3ad8f114 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 01:50:37 +0700 Subject: [PATCH 18/31] comment on modifier change limitations --- src/platform_impl/windows/event_loop.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 2e05e3c2bc..a4d6e88020 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -1047,6 +1047,9 @@ unsafe fn public_window_callback_inner( let mut result = ProcResult::DefWindowProc(wparam); // Send new modifiers before sending key events. + // NOTE: Some modifier key presses are not reported as KeyDown/Up events when the same + // alternative side modifier is being held, e.g., in a sequence of ↓LShift ↓RShift ↑RShift the + // last event is not reported. let mods_changed_callback = || match msg { WM_KEYDOWN | WM_SYSKEYDOWN | WM_KEYUP | WM_SYSKEYUP => { update_modifiers(window, userdata); From 1b18f39f3816a6bbbd24836f86b6f20382b9d5d6 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 01:56:29 +0700 Subject: [PATCH 19/31] update changelog --- src/changelog/unreleased.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 3df56f7002..d85f3d821a 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -82,6 +82,7 @@ changelog entry. - `ActivationToken::as_raw` to get a ref to raw token. - Each platform now has corresponding `WindowAttributes` struct instead of trait extension. - On Windows, update side-aware `event::Modifiers` information on state change. +- On Windows, added AltGr as a separate modifier (though currently AltGr+LCtrl can't be differentiated from just AltGr). ### Changed From 3381e4aa2d10a2ff494a5acb3e90c48f24d3b0b3 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 21:06:47 +0700 Subject: [PATCH 20/31] rename AltGr to AltGraph --- examples/kbd_ev_print.rs | 2 +- src/platform_impl/windows/keyboard_layout.rs | 2 +- winit-core/src/keyboard.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 61e4907ae9..7db654e2a2 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -48,7 +48,7 @@ pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { } else { s.push_str(" ") }; - if state.contains(ModifiersState::ALTGR) { + if state.contains(ModifiersState::ALT_GRAPH) { s.push_str("⎇Gr") } else { s.push_str(" ") diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index ca312ab721..208628b713 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -298,7 +298,7 @@ impl LayoutCache { pressed_mods.contains(ModifiersKeys::LALT) || pressed_mods.contains(ModifiersKeys::RALT), ); - state.set(ModifiersState::ALTGR, is_altgr); + state.set(ModifiersState::ALT_GRAPH, is_altgr); // On Windows AltGr = RAlt + LCtrl, and OS sends artificial LCtrl key event, which needs to // be filtered out without touching "real" LCtrl events to allow separate bindings of diff --git a/winit-core/src/keyboard.rs b/winit-core/src/keyboard.rs index 6f3e85667b..898915a36b 100644 --- a/winit-core/src/keyboard.rs +++ b/winit-core/src/keyboard.rs @@ -1706,7 +1706,7 @@ bitflags! { /// This is the "windows" key on PC and "command" key on Mac. const META = 0b100 << 9; /// The "AltGraph" key, usually used to insert symbols. - const ALTGR = 0b100 << 10; + const ALT_GRAPH = 0b100 << 10; #[deprecated = "use META instead"] const SUPER = Self::META.bits(); } From 50773da96aa57edaad9c3d361ee78d21602dddd4 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 02:28:02 +0700 Subject: [PATCH 21/31] add lock keys to the example --- examples/kbd_ev_print.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 7db654e2a2..758c16ce50 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -53,6 +53,25 @@ pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { } else { s.push_str(" ") }; + s.push(' '); + if state.contains(ModifiersState::CAPS_LOCK) { + s.push_str("⇪") + } else { + s.push_str(" ") + }; + s.push(' '); + if state.contains(ModifiersState::NUM_LOCK) { + s.push_str("⇭") //🔢 + } else { + s.push_str(" ") + }; + s.push(' '); + if state.contains(ModifiersState::SCROLL_LOCK) { + s.push_str("⇳🔒") + } else { + s.push_str(" ") + }; + s.push(' '); s } // https://docs.rs/winit/latest/winit/event/struct.Modifiers.html From 2658fa737c9e23607d0b60e4d2e1f27eed8fa4d6 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 02:28:07 +0700 Subject: [PATCH 22/31] add lock keys to the enum --- winit-core/src/keyboard.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/winit-core/src/keyboard.rs b/winit-core/src/keyboard.rs index 898915a36b..93a4f163e3 100644 --- a/winit-core/src/keyboard.rs +++ b/winit-core/src/keyboard.rs @@ -1707,6 +1707,12 @@ bitflags! { const META = 0b100 << 9; /// The "AltGraph" key, usually used to insert symbols. const ALT_GRAPH = 0b100 << 10; + /// The "Caps Lock" key. + const CAPS_LOCK = 0b100 << 11; + /// The "Num Lock" key. + const NUM_LOCK = 0b100 << 12; + /// The "Scroll Lock" key. + const SCROLL_LOCK = 0b100 << 13; #[deprecated = "use META instead"] const SUPER = Self::META.bits(); } From c26c9196afcc0f67fd4223a97f4e13a36ea1ea4f Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 02:40:50 +0700 Subject: [PATCH 23/31] add lock keys to setting Modifiers --- src/platform_impl/windows/keyboard_layout.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index 208628b713..c7164c40d0 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -53,6 +53,11 @@ pub(crate) static LAYOUT_CACHE: LazyLock> = fn key_pressed(vkey: VIRTUAL_KEY) -> bool { unsafe { (GetKeyState(vkey as i32) & (1 << 15)) == (1 << 15) } } +fn key_toggled(vkey: VIRTUAL_KEY) -> bool { + // learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeystate + // If the low-order bit is 1, the key is toggled. GetKeyState is SHORT = 16bit + unsafe { (GetKeyState(vkey as i32) & (1 << 0)) == (1 << 0) } +} const NUMPAD_VKEYS: [VIRTUAL_KEY; 16] = [ VK_NUMPAD0, @@ -320,6 +325,11 @@ impl LayoutCache { pressed_mods.contains(ModifiersKeys::LMETA) || pressed_mods.contains(ModifiersKeys::RMETA), ); + + state.set(ModifiersState::CAPS_LOCK, key_toggled(VK_CAPITAL)); + state.set(ModifiersState::NUM_LOCK, key_toggled(VK_NUMLOCK)); + state.set(ModifiersState::SCROLL_LOCK, key_toggled(VK_SCROLL)); + Modifiers::new(state, pressed_mods) } From 834cc27501f01e8dabb7f16749f1fed5d8b2634e Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 03:05:35 +0700 Subject: [PATCH 24/31] clippy --- examples/kbd_ev_print.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 758c16ce50..32e14a1bda 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -55,15 +55,15 @@ pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { }; s.push(' '); if state.contains(ModifiersState::CAPS_LOCK) { - s.push_str("⇪") + s.push('⇪') } else { - s.push_str(" ") + s.push(' ') }; s.push(' '); if state.contains(ModifiersState::NUM_LOCK) { - s.push_str("⇭") //🔢 + s.push('⇭') //🔢 } else { - s.push_str(" ") + s.push(' ') }; s.push(' '); if state.contains(ModifiersState::SCROLL_LOCK) { From cc9bbfa141ddff4955b29b2faa9de7fe22404d66 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 22:54:56 +0700 Subject: [PATCH 25/31] add all lock keys and other missing modifiers to the enum --- winit-core/src/keyboard.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/winit-core/src/keyboard.rs b/winit-core/src/keyboard.rs index 93a4f163e3..9481a6499b 100644 --- a/winit-core/src/keyboard.rs +++ b/winit-core/src/keyboard.rs @@ -1713,6 +1713,21 @@ bitflags! { const NUM_LOCK = 0b100 << 12; /// The "Scroll Lock" key. const SCROLL_LOCK = 0b100 << 13; + /// The "Function" switch key. Often handled directly in the keyboard hardware and does not generate key events. + /// - **macOS**: Generates `ModifiersChanged` events on Apple hardware. + const FN = 0b100 << 14; + /// The "Function-Lock" key. + const FN_LOCK = 0b100 << 15; + /// The "Kana Mode" ("Kana Lock") key, typically used to enter hiragana mode (typically from romaji mode). + const KANA_LOCK = 0b100 << 16; + /// The "Left OYAYUBI" key (OEM-specific). + const LOYA = 0b100 << 17; + /// The "Right OYAYUBI" key (OEM-specific). + const ROYA = 0b100 << 18; + /// The "Symbol" modifier key used on some virtual keyboards. + const SYMBOL = 0b100 << 19; + /// The "Symbol" Lock key. + const SYMBOL_LOCK = 0b100 << 20; #[deprecated = "use META instead"] const SUPER = Self::META.bits(); } From 69a5e138ceda243538cfc648123f5a2410842cde Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 23:03:38 +0700 Subject: [PATCH 26/31] add all lock keys and other missing modifiers to example --- examples/kbd_ev_print.rs | 47 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 32e14a1bda..7a08605b72 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -72,6 +72,49 @@ pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { s.push_str(" ") }; s.push(' '); + + if state.contains(ModifiersState::FN) { + s.push_str("🄵") + } else { + s.push(' ') + }; + s.push(' '); + if state.contains(ModifiersState::FN_LOCK) { + s.push_str("🄵🔒") + } else { + s.push_str(" ") + }; + s.push(' '); + if state.contains(ModifiersState::KANA_LOCK) { + s.push_str("カナ🔒") + } else { + s.push_str(" ") + }; + s.push(' '); + if state.contains(ModifiersState::LOYA) { + s.push_str("‹👍") + } else { + s.push_str(" ") + }; + s.push(' '); + if state.contains(ModifiersState::ROYA) { + s.push_str("👍›") + } else { + s.push_str(" ") + }; + s.push(' '); + if state.contains(ModifiersState::SYMBOL) { + s.push_str("🔣") + } else { + s.push(' ') + }; + s.push(' '); + if state.contains(ModifiersState::SYMBOL_LOCK) { + s.push_str("🔣🔒") + } else { + s.push_str(" ") + }; + s.push(' '); s } // https://docs.rs/winit/latest/winit/event/struct.Modifiers.html @@ -125,7 +168,7 @@ pub fn mod_state_side_aware_s(mods: &Modifiers) -> String { } else { s.push_str(" ") } - s.push_str(" "); + s.push_str(" "); s } // pub struct KeyEvent @@ -220,7 +263,7 @@ impl ApplicationHandler for App { let state = mods.state(); let state_s = mod_state_side_agnostic_s(&state); let pressed_mods_s = mod_state_side_aware_s(&mods); - println!("Δ {}\tside-agnostic\n {}\tside-aware", state_s, pressed_mods_s); + println!("Δ {}\tside-agnostic (mostly)\n {}\tside-aware", state_s, pressed_mods_s); }, WindowEvent::KeyboardInput { event, is_synthetic, .. } => { let is_synthetic_s = if is_synthetic { "⚗" } else { " " }; From 6bf241e31bcdc8eaef2519cecb407b015286d193 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 23:07:29 +0700 Subject: [PATCH 27/31] win: all lock keys and other missing modifiers to setting Modifiers --- src/platform_impl/windows/keyboard_layout.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platform_impl/windows/keyboard_layout.rs b/src/platform_impl/windows/keyboard_layout.rs index c7164c40d0..cc7009a695 100644 --- a/src/platform_impl/windows/keyboard_layout.rs +++ b/src/platform_impl/windows/keyboard_layout.rs @@ -330,6 +330,10 @@ impl LayoutCache { state.set(ModifiersState::NUM_LOCK, key_toggled(VK_NUMLOCK)); state.set(ModifiersState::SCROLL_LOCK, key_toggled(VK_SCROLL)); + state.set(ModifiersState::KANA_LOCK, key_toggled(VK_KANA)); + state.set(ModifiersState::LOYA, key_pressed(VK_OEM_FJ_LOYA)); + state.set(ModifiersState::ROYA, key_pressed(VK_OEM_FJ_ROYA)); + Modifiers::new(state, pressed_mods) } From aa6a4948e563b9515e5bf6d3198a02aebbd42afd Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 23:15:40 +0700 Subject: [PATCH 28/31] clippy --- examples/kbd_ev_print.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 7a08605b72..50c90cc878 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -74,7 +74,7 @@ pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { s.push(' '); if state.contains(ModifiersState::FN) { - s.push_str("🄵") + s.push('🄵') } else { s.push(' ') }; @@ -104,7 +104,7 @@ pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { }; s.push(' '); if state.contains(ModifiersState::SYMBOL) { - s.push_str("🔣") + s.push('🔣') } else { s.push(' ') }; From 293a6ac21478025d532a98fdc72cab7a76eff41f Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 23:27:00 +0700 Subject: [PATCH 29/31] add helper methods for checking modifier state --- winit-core/src/keyboard.rs | 55 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/winit-core/src/keyboard.rs b/winit-core/src/keyboard.rs index 9481a6499b..d51d245a41 100644 --- a/winit-core/src/keyboard.rs +++ b/winit-core/src/keyboard.rs @@ -1753,6 +1753,61 @@ impl ModifiersState { pub fn meta_key(&self) -> bool { self.intersects(Self::META) } + + /// Returns `true` if the AltGraph modifier is active. + pub fn alt_graph_key(&self) -> bool { + self.contains(Self::ALT_GRAPH) + } + + /// Returns `true` if the CapsLock modifier is active. + pub fn caps_lock_key(&self) -> bool { + self.contains(Self::CAPS_LOCK) + } + + /// Returns `true` if the NumLock modifier is active. + pub fn num_lock_key(&self) -> bool { + self.contains(Self::NUM_LOCK) + } + + /// Returns `true` if the ScrollLock modifier is active. + pub fn scroll_lock_key(&self) -> bool { + self.contains(Self::SCROLL_LOCK) + } + + /// Returns `true` if the Fn modifier is active. + pub fn fn_key(&self) -> bool { + self.contains(Self::FN) + } + + /// Returns `true` if the FnLock modifier is active. + pub fn fn_lock_key(&self) -> bool { + self.contains(Self::FN_LOCK) + } + + /// Returns `true` if the KanaLock modifier is active. + pub fn kana_lock_key(&self) -> bool { + self.contains(Self::KANA_LOCK) + } + + /// Returns `true` if the Loya modifier is active. + pub fn loya_key(&self) -> bool { + self.contains(Self::LOYA) + } + + /// Returns `true` if the Roya modifier is active. + pub fn roya_key(&self) -> bool { + self.contains(Self::ROYA) + } + + /// Returns `true` if the Symbol modifier is active. + pub fn symbol_key(&self) -> bool { + self.contains(Self::SYMBOL) + } + + /// Returns `true` if the SymbolLock modifier is active. + pub fn symbol_lock_key(&self) -> bool { + self.contains(Self::SYMBOL_LOCK) + } } /// The logical state of the particular modifiers key. From f24f970aa0429b0d683cfc03f9e74b5ecfb0eebd Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 21 May 2025 23:32:52 +0700 Subject: [PATCH 30/31] update example --- examples/kbd_ev_print.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/kbd_ev_print.rs b/examples/kbd_ev_print.rs index 50c90cc878..f1b4727016 100644 --- a/examples/kbd_ev_print.rs +++ b/examples/kbd_ev_print.rs @@ -74,13 +74,13 @@ pub fn mod_state_side_agnostic_s(state: &ModifiersState) -> String { s.push(' '); if state.contains(ModifiersState::FN) { - s.push('🄵') + s.push('ƒ') } else { s.push(' ') }; s.push(' '); if state.contains(ModifiersState::FN_LOCK) { - s.push_str("🄵🔒") + s.push_str("ƒ🔒") } else { s.push_str(" ") }; @@ -309,10 +309,11 @@ fn main() -> Result<(), Box> { println!( "Δ is ModifiersChanged event, showing (line #1) side-agnostic modifier state as well as \ - (#2) side-aware one." + (#2) side-aware one.\n ⇧ Shift ⎈ Control ◆ Meta ⎇ Alt ⎇Gr AltGraph ⇪ CapsLock ⇭ \ + NumLock ⇳🔒 ScrollLock\n ƒ Fn ƒ🔒 FnLock カナ🔒 KanaLock ‹👍 Loya 👍› Roya 🔣 \ + Symbol 🔣🔒 SymbolLock\n🖮 is KeyboardInput: ⚗ synthetic, ↓↑ pressed/unknown, 🔁 \ + repeat\n phys logic txt +mod −mod location" ); - println!("🖮 is KeyboardInput: ⚗=synthetic, ↓↑=pressed/released 🔁=repeat"); - println!(" phys logic txt +mod −mod location"); // For alternative loop run options see `pump_events` and `run_on_demand` examples. event_loop.run_app(App::default())?; From 37224d2a4705532283e10b49fa1f2999c89c920a Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 20 May 2025 17:25:57 +0700 Subject: [PATCH 31/31] update changelog --- src/changelog/unreleased.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index d85f3d821a..d169889702 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -81,8 +81,10 @@ changelog entry. - `keyboard::ModifiersKey` to track which modifier is exactly pressed. - `ActivationToken::as_raw` to get a ref to raw token. - Each platform now has corresponding `WindowAttributes` struct instead of trait extension. +- Added support for using AltGr, CapsLock,NumLock, ScrollLock, Fn, FnLock, KanaLock, Loya, Roya, Symbol, SymbolLock as separate modifiers. - On Windows, update side-aware `event::Modifiers` information on state change. - On Windows, added AltGr as a separate modifier (though currently AltGr+LCtrl can't be differentiated from just AltGr). +- On Windows, added CapsLock,NumLock, ScrollLock, KanaLock, Loya, Roya as separate modifiers. ### Changed