diff --git a/CHANGELOG.md b/CHANGELOG.md index fa1d6bfb..6bd417c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Fix `Event::to_static` returning `None` for user events. - On Wayland, Hide CSD for fullscreen windows. - On Windows, ignore spurious mouse move messages. +- **Breaking:** Move `ModifiersChanged` variant from `DeviceEvent` to `WindowEvent`. # 0.21.0 (2020-02-04) diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 5ed193ec..317577e3 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -38,6 +38,7 @@ fn main() { _ => (), } } + WindowEvent::ModifiersChanged(m) => modifiers = m, _ => (), }, Event::DeviceEvent { event, .. } => match event { @@ -46,7 +47,6 @@ fn main() { ElementState::Pressed => println!("mouse button {} pressed", button), ElementState::Released => println!("mouse button {} released", button), }, - DeviceEvent::ModifiersChanged(m) => modifiers = m, _ => (), }, _ => (), diff --git a/src/event.rs b/src/event.rs index 494a4a1d..e7e7be9e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -235,6 +235,13 @@ pub enum WindowEvent<'a> { is_synthetic: bool, }, + /// The keyboard modifiers have changed. + /// + /// Platform-specific behavior: + /// - **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), + /// The cursor has moved on the window. CursorMoved { device_id: DeviceId, @@ -243,7 +250,7 @@ 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 DeviceEvent::ModifiersChanged"] + #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] modifiers: ModifiersState, }, @@ -258,7 +265,7 @@ pub enum WindowEvent<'a> { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, - #[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"] + #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] modifiers: ModifiersState, }, @@ -267,7 +274,7 @@ pub enum WindowEvent<'a> { device_id: DeviceId, state: ElementState, button: MouseButton, - #[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"] + #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] modifiers: ModifiersState, }, @@ -341,6 +348,7 @@ impl<'a> WindowEvent<'a> { input, is_synthetic, }), + ModifiersChanged(modifiers) => Some(ModifiersChanged(modifiers)), #[allow(deprecated)] CursorMoved { device_id, @@ -464,16 +472,6 @@ pub enum DeviceEvent { Key(KeyboardInput), - /// The keyboard modifiers have changed. - /// - /// 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. - /// - /// Platform-specific behavior: - /// - **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), - Text { codepoint: char, }, @@ -502,7 +500,7 @@ pub struct KeyboardInput { /// /// 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 DeviceEvent::ModifiersChanged"] + #[deprecated = "Deprecated in favor of WindowEvent::ModifiersChanged"] pub modifiers: ModifiersState, } diff --git a/src/platform_impl/linux/wayland/keyboard.rs b/src/platform_impl/linux/wayland/keyboard.rs index ed7aa20a..8f911888 100644 --- a/src/platform_impl/linux/wayland/keyboard.rs +++ b/src/platform_impl/linux/wayland/keyboard.rs @@ -8,9 +8,7 @@ use smithay_client_toolkit::{ reexports::client::protocol::{wl_keyboard, wl_seat}, }; -use crate::event::{ - DeviceEvent, ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent, -}; +use crate::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent}; pub fn init_keyboard( seat: &wl_seat::WlSeat, @@ -33,9 +31,24 @@ pub fn init_keyboard( let wid = make_wid(&surface); my_sink.send_window_event(WindowEvent::Focused(true), wid); *target.lock().unwrap() = Some(wid); + + let modifiers = *modifiers_tracker.lock().unwrap(); + + if !modifiers.is_empty() { + my_sink.send_window_event(WindowEvent::ModifiersChanged(modifiers), wid); + } } KbEvent::Leave { surface, .. } => { let wid = make_wid(&surface); + let modifiers = *modifiers_tracker.lock().unwrap(); + + if !modifiers.is_empty() { + my_sink.send_window_event( + WindowEvent::ModifiersChanged(ModifiersState::empty()), + wid, + ); + } + my_sink.send_window_event(WindowEvent::Focused(false), wid); *target.lock().unwrap() = None; } @@ -88,7 +101,9 @@ pub fn init_keyboard( *modifiers_tracker.lock().unwrap() = modifiers; - my_sink.send_device_event(DeviceEvent::ModifiersChanged(modifiers), DeviceId); + if let Some(wid) = *target.lock().unwrap() { + my_sink.send_window_event(WindowEvent::ModifiersChanged(modifiers), wid); + } } } }, diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 424bb89c..976c17b6 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -32,6 +32,8 @@ pub(super) struct EventProcessor { // Number of touch events currently in progress pub(super) num_touch: u32, pub(super) first_touch: Option, + // Currently focused window belonging to this process + pub(super) active_window: Option, } impl EventProcessor { @@ -136,11 +138,12 @@ impl EventProcessor { if let Some(modifiers) = self.device_mod_state.update_state(&state, modifier) { - let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); - callback(Event::DeviceEvent { - device_id, - event: DeviceEvent::ModifiersChanged(modifiers), - }); + if let Some(window_id) = self.active_window { + callback(Event::WindowEvent { + window_id: mkwid(window_id), + event: WindowEvent::ModifiersChanged(modifiers), + }); + } } } } @@ -872,44 +875,58 @@ impl EventProcessor { ffi::XI_FocusIn => { let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) }; - let window_id = mkwid(xev.event); - wt.ime .borrow_mut() .focus(xev.event) .expect("Failed to focus input context"); - callback(Event::WindowEvent { - window_id, - event: Focused(true), - }); - let modifiers = ModifiersState::from_x11(&xev.mods); - update_modifiers!(modifiers, None); + self.device_mod_state.update_state(&modifiers, None); - // The deviceid for this event is for a keyboard instead of a pointer, - // so we have to do a little extra work. - let pointer_id = self - .devices - .borrow() - .get(&DeviceId(xev.deviceid)) - .map(|device| device.attachment) - .unwrap_or(2); + if self.active_window != Some(xev.event) { + self.active_window = Some(xev.event); - let position = PhysicalPosition::new(xev.event_x, xev.event_y); + let window_id = mkwid(xev.event); + let position = PhysicalPosition::new(xev.event_x, xev.event_y); - callback(Event::WindowEvent { - window_id, - event: CursorMoved { - device_id: mkdid(pointer_id), - position, - modifiers, - }, - }); + callback(Event::WindowEvent { + window_id, + event: Focused(true), + }); - // Issue key press events for all pressed keys - self.handle_pressed_keys(window_id, ElementState::Pressed, &mut callback); + if !modifiers.is_empty() { + callback(Event::WindowEvent { + window_id, + event: WindowEvent::ModifiersChanged(modifiers), + }); + } + + // The deviceid for this event is for a keyboard instead of a pointer, + // so we have to do a little extra work. + let pointer_id = self + .devices + .borrow() + .get(&DeviceId(xev.deviceid)) + .map(|device| device.attachment) + .unwrap_or(2); + + callback(Event::WindowEvent { + window_id, + event: CursorMoved { + device_id: mkdid(pointer_id), + position, + modifiers, + }, + }); + + // Issue key press events for all pressed keys + self.handle_pressed_keys( + window_id, + ElementState::Pressed, + &mut callback, + ); + } } ffi::XI_FocusOut => { let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) }; @@ -921,15 +938,26 @@ impl EventProcessor { .unfocus(xev.event) .expect("Failed to unfocus input context"); - let window_id = mkwid(xev.event); + if self.active_window.take() == Some(xev.event) { + let window_id = mkwid(xev.event); - // Issue key release events for all pressed keys - self.handle_pressed_keys(window_id, ElementState::Released, &mut callback); + // Issue key release events for all pressed keys + self.handle_pressed_keys( + window_id, + ElementState::Released, + &mut callback, + ); - callback(Event::WindowEvent { - window_id, - event: Focused(false), - }) + callback(Event::WindowEvent { + window_id, + event: WindowEvent::ModifiersChanged(ModifiersState::empty()), + }); + + callback(Event::WindowEvent { + window_id, + event: Focused(false), + }) + } } ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => { @@ -1084,10 +1112,12 @@ impl EventProcessor { let new_modifiers = self.device_mod_state.modifiers(); if modifiers != new_modifiers { - callback(Event::DeviceEvent { - device_id, - event: DeviceEvent::ModifiersChanged(new_modifiers), - }); + if let Some(window_id) = self.active_window { + callback(Event::WindowEvent { + window_id: mkwid(window_id), + event: WindowEvent::ModifiersChanged(new_modifiers), + }); + } } } } diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index f58b4207..f9e0ea56 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -225,6 +225,7 @@ impl EventLoop { device_mod_state: Default::default(), num_touch: 0, first_touch: None, + active_window: None, }; // Register for device hotplug events diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index 741ecb48..2307d3e8 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -50,13 +50,13 @@ impl Default for CursorState { } } -struct ViewState { +pub(super) struct ViewState { ns_window: id, pub cursor_state: Arc>, ime_spot: Option<(f64, f64)>, raw_characters: Option, is_key_down: bool, - modifiers: ModifiersState, + pub(super) modifiers: ModifiersState, tracking_rect: Option, } @@ -618,6 +618,19 @@ fn retrieve_keycode(event: id) -> Option { }) } +// Update `state.modifiers` if `event` has something different +fn update_potentially_stale_modifiers(state: &mut ViewState, event: id) { + let event_modifiers = event_mods(event); + if state.modifiers != event_modifiers { + state.modifiers = event_modifiers; + + AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { + window_id: WindowId(get_window_id(state.ns_window)), + event: WindowEvent::ModifiersChanged(state.modifiers), + })); + } +} + extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { trace!("Triggered `keyDown`"); unsafe { @@ -633,6 +646,8 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) { let is_repeat = msg_send![event, isARepeat]; + update_potentially_stale_modifiers(state, event); + #[allow(deprecated)] let window_event = Event::WindowEvent { window_id, @@ -686,6 +701,8 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) { let scancode = get_scancode(event) as u32; let virtual_keycode = retrieve_keycode(event); + update_potentially_stale_modifiers(state, event); + #[allow(deprecated)] let window_event = Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), @@ -750,16 +767,18 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) { events.push_back(window_event); } + let window_id = WindowId(get_window_id(state.ns_window)); + for event in events { AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { - window_id: WindowId(get_window_id(state.ns_window)), + window_id, event, })); } - AppState::queue_event(EventWrapper::StaticEvent(Event::DeviceEvent { - device_id: DEVICE_ID, - event: DeviceEvent::ModifiersChanged(state.modifiers), + AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent { + window_id, + event: WindowEvent::ModifiersChanged(state.modifiers), })); } trace!("Completed `flagsChanged`"); @@ -801,6 +820,8 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) { let event: id = msg_send![NSApp(), currentEvent]; + update_potentially_stale_modifiers(state, event); + #[allow(deprecated)] let window_event = Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), @@ -826,6 +847,8 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); + update_potentially_stale_modifiers(state, event); + let window_event = Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), event: WindowEvent::MouseInput { @@ -889,6 +912,8 @@ fn mouse_motion(this: &Object, event: id) { let y = view_rect.size.height as f64 - view_point.y as f64; let logical_position = LogicalPosition::new(x, y); + update_potentially_stale_modifiers(state, event); + let window_event = Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), event: WindowEvent::CursorMoved { @@ -918,7 +943,7 @@ extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) { mouse_motion(this, event); } -extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) { +extern "C" fn mouse_entered(this: &Object, _sel: Sel, _event: id) { trace!("Triggered `mouseEntered`"); unsafe { let state_ptr: *mut c_void = *this.get_ivar("winitState"); @@ -932,7 +957,6 @@ extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) { }; AppState::queue_event(EventWrapper::StaticEvent(enter_event)); - mouse_motion(this, event); } trace!("Completed `mouseEntered`"); } @@ -982,6 +1006,8 @@ extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) { let state_ptr: *mut c_void = *this.get_ivar("winitState"); let state = &mut *(state_ptr as *mut ViewState); + update_potentially_stale_modifiers(state, event); + let window_event = Event::WindowEvent { window_id: WindowId(get_window_id(state.ns_window)), event: WindowEvent::MouseWheel { diff --git a/src/platform_impl/macos/window_delegate.rs b/src/platform_impl/macos/window_delegate.rs index f04d7c73..a89e99fc 100644 --- a/src/platform_impl/macos/window_delegate.rs +++ b/src/platform_impl/macos/window_delegate.rs @@ -16,11 +16,12 @@ use objc::{ use crate::{ dpi::LogicalSize, - event::{Event, WindowEvent}, + event::{Event, ModifiersState, WindowEvent}, platform_impl::platform::{ app_state::AppState, event::{EventProxy, EventWrapper}, util::{self, IdRef}, + view::ViewState, window::{get_window_id, UnownedWindow}, }, window::{Fullscreen, WindowId}, @@ -319,6 +320,29 @@ extern "C" fn window_did_become_key(this: &Object, _: Sel, _: id) { extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) { trace!("Triggered `windowDidResignKey:`"); with_state(this, |state| { + // It happens rather often, e.g. when the user is Cmd+Tabbing, that the + // NSWindowDelegate will receive a didResignKey event despite no event + // being received when the modifiers are released. This is because + // flagsChanged events are received by the NSView instead of the + // NSWindowDelegate, and as a result a tracked modifiers state can quite + // easily fall out of synchrony with reality. This requires us to emit + // a synthetic ModifiersChanged event when we lose focus. + // + // Here we (very unsafely) acquire the winitState (a ViewState) from the + // Object referenced by state.ns_view (an IdRef, which is dereferenced + // to an id) + let view_state: &mut ViewState = unsafe { + let ns_view: &Object = (*state.ns_view).as_ref().expect("failed to deref"); + let state_ptr: *mut c_void = *ns_view.get_ivar("winitState"); + &mut *(state_ptr as *mut ViewState) + }; + + // Both update the state and emit a ModifiersChanged event. + if !view_state.modifiers.is_empty() { + view_state.modifiers = ModifiersState::empty(); + state.emit_event(WindowEvent::ModifiersChanged(view_state.modifiers)); + } + state.emit_event(WindowEvent::Focused(false)); }); trace!("Completed `windowDidResignKey:`"); diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 38181089..a443a6ce 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -50,9 +50,7 @@ use crate::{ dark_mode::try_dark_mode, dpi::{become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling}, drop_handler::FileDropHandler, - event::{ - self, handle_extended_keys, process_key_params, vkey_to_winit_vkey, ModifiersStateSide, - }, + event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, monitor, raw_input, util, window_state::{CursorFlags, WindowFlags, WindowState}, wrap_device_id, WindowId, DEVICE_ID, @@ -108,7 +106,6 @@ impl SubclassInput { struct ThreadMsgTargetSubclassInput { event_loop_runner: EventLoopRunnerShared, user_event_receiver: Receiver, - modifiers_state: ModifiersStateSide, } impl ThreadMsgTargetSubclassInput { @@ -553,7 +550,6 @@ fn thread_event_target_window(event_loop_runner: EventLoopRunnerShared) -> let subclass_input = ThreadMsgTargetSubclassInput { event_loop_runner, user_event_receiver: rx, - modifiers_state: ModifiersStateSide::default(), }; let input_ptr = Box::into_raw(Box::new(subclass_input)); let subclass_result = commctrl::SetWindowSubclass( @@ -606,6 +602,24 @@ fn normalize_pointer_pressure(pressure: u32) -> Option { } } +/// Emit a `ModifiersChanged` event whenever modifiers have changed. +fn update_modifiers(window: HWND, subclass_input: &SubclassInput) { + use crate::event::WindowEvent::ModifiersChanged; + + let modifiers = event::get_key_mods(); + let mut window_state = subclass_input.window_state.lock(); + if window_state.modifiers_state != modifiers { + window_state.modifiers_state = modifiers; + + unsafe { + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: ModifiersChanged(modifiers), + }); + } + } +} + /// Any window whose callback is configured to this function will have its events propagated /// through the events loop of the thread the window was created in. // @@ -866,6 +880,8 @@ unsafe extern "system" fn public_window_callback( w.mouse.last_position = Some(position); } if cursor_moved { + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: CursorMoved { @@ -905,6 +921,8 @@ unsafe extern "system" fn public_window_callback( let value = value as i32; let value = value as f32 / winuser::WHEEL_DELTA as f32; + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::MouseWheel { @@ -925,6 +943,8 @@ unsafe extern "system" fn public_window_callback( let value = value as i32; let value = value as f32 / winuser::WHEEL_DELTA as f32; + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: WindowEvent::MouseWheel { @@ -944,6 +964,8 @@ unsafe extern "system" fn public_window_callback( commctrl::DefSubclassProc(window, msg, wparam, lparam) } else { if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { + update_modifiers(window, subclass_input); + #[allow(deprecated)] subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -974,6 +996,8 @@ unsafe extern "system" fn public_window_callback( winuser::WM_KEYUP | winuser::WM_SYSKEYUP => { use crate::event::ElementState::Released; if let Some((scancode, vkey)) = process_key_params(wparam, lparam) { + update_modifiers(window, subclass_input); + #[allow(deprecated)] subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -997,6 +1021,8 @@ unsafe extern "system" fn public_window_callback( capture_mouse(window, &mut *subclass_input.window_state.lock()); + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { @@ -1016,6 +1042,8 @@ unsafe extern "system" fn public_window_callback( release_mouse(&mut *subclass_input.window_state.lock()); + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { @@ -1035,6 +1063,8 @@ unsafe extern "system" fn public_window_callback( capture_mouse(window, &mut *subclass_input.window_state.lock()); + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { @@ -1054,6 +1084,8 @@ unsafe extern "system" fn public_window_callback( release_mouse(&mut *subclass_input.window_state.lock()); + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { @@ -1073,6 +1105,8 @@ unsafe extern "system" fn public_window_callback( capture_mouse(window, &mut *subclass_input.window_state.lock()); + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { @@ -1092,6 +1126,8 @@ unsafe extern "system" fn public_window_callback( release_mouse(&mut *subclass_input.window_state.lock()); + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { @@ -1112,6 +1148,8 @@ unsafe extern "system" fn public_window_callback( capture_mouse(window, &mut *subclass_input.window_state.lock()); + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { @@ -1132,6 +1170,8 @@ unsafe extern "system" fn public_window_callback( release_mouse(&mut *subclass_input.window_state.lock()); + update_modifiers(window, subclass_input); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: MouseInput { @@ -1340,6 +1380,8 @@ unsafe extern "system" fn public_window_callback( winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC); let virtual_keycode = event::vkey_to_winit_vkey(windows_keycode); + update_modifiers(window, subclass_input); + #[allow(deprecated)] subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), @@ -1365,7 +1407,11 @@ unsafe extern "system" fn public_window_callback( } winuser::WM_KILLFOCUS => { - use crate::event::{ElementState::Released, WindowEvent::Focused}; + use crate::event::{ + ElementState::Released, + ModifiersState, + WindowEvent::{Focused, ModifiersChanged}, + }; for windows_keycode in event::get_pressed_keys() { let scancode = winuser::MapVirtualKeyA(windows_keycode as _, winuser::MAPVK_VK_TO_VSC); @@ -1387,6 +1433,12 @@ unsafe extern "system" fn public_window_callback( }) } + subclass_input.window_state.lock().modifiers_state = ModifiersState::empty(); + subclass_input.send_event(Event::WindowEvent { + window_id: RootWindowId(WindowId(window)), + event: ModifiersChanged(ModifiersState::empty()), + }); + subclass_input.send_event(Event::WindowEvent { window_id: RootWindowId(WindowId(window)), event: Focused(false), @@ -1795,10 +1847,9 @@ unsafe extern "system" fn thread_event_target_callback( winuser::WM_INPUT => { use crate::event::{ - DeviceEvent::{Button, Key, ModifiersChanged, Motion, MouseMotion, MouseWheel}, + DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel}, ElementState::{Pressed, Released}, MouseScrollDelta::LineDelta, - VirtualKeyCode, }; if let Some(data) = raw_input::get_raw_input_data(lparam as _) { @@ -1877,48 +1928,6 @@ unsafe extern "system" fn thread_event_target_callback( { let virtual_keycode = vkey_to_winit_vkey(vkey); - // If we ever change the DeviceEvent API to only emit events when a - // window is focused, we'll need to emit synthetic `ModifiersChanged` - // events when Winit windows lose focus so that these don't drift out - // of sync with the actual modifier state. - let old_modifiers_state = - subclass_input.modifiers_state.filter_out_altgr().into(); - match virtual_keycode { - Some(VirtualKeyCode::LShift) => subclass_input - .modifiers_state - .set(ModifiersStateSide::LSHIFT, pressed), - Some(VirtualKeyCode::RShift) => subclass_input - .modifiers_state - .set(ModifiersStateSide::RSHIFT, pressed), - Some(VirtualKeyCode::LControl) => subclass_input - .modifiers_state - .set(ModifiersStateSide::LCTRL, pressed), - Some(VirtualKeyCode::RControl) => subclass_input - .modifiers_state - .set(ModifiersStateSide::RCTRL, pressed), - Some(VirtualKeyCode::LAlt) => subclass_input - .modifiers_state - .set(ModifiersStateSide::LALT, pressed), - Some(VirtualKeyCode::RAlt) => subclass_input - .modifiers_state - .set(ModifiersStateSide::RALT, pressed), - Some(VirtualKeyCode::LWin) => subclass_input - .modifiers_state - .set(ModifiersStateSide::LLOGO, pressed), - Some(VirtualKeyCode::RWin) => subclass_input - .modifiers_state - .set(ModifiersStateSide::RLOGO, pressed), - _ => (), - } - let new_modifiers_state = - subclass_input.modifiers_state.filter_out_altgr().into(); - if new_modifiers_state != old_modifiers_state { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: ModifiersChanged(new_modifiers_state), - }); - } - #[allow(deprecated)] subclass_input.send_event(Event::DeviceEvent { device_id, @@ -1926,7 +1935,7 @@ unsafe extern "system" fn thread_event_target_callback( scancode, state, virtual_keycode, - modifiers: new_modifiers_state, + modifiers: event::get_key_mods(), }), }); } diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 76485f46..4be0c796 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -1,5 +1,6 @@ use crate::{ dpi::{PhysicalPosition, Size}, + event::ModifiersState, platform_impl::platform::{event_loop, icon::WinIcon, util}, window::{CursorIcon, Fullscreen, WindowAttributes}, }; @@ -28,6 +29,7 @@ pub struct WindowState { pub saved_window: Option, pub scale_factor: f64, + pub modifiers_state: ModifiersState, pub fullscreen: Option, /// Used to supress duplicate redraw attempts when calling `request_redraw` multiple /// times in `MainEventsCleared`. @@ -119,6 +121,7 @@ impl WindowState { saved_window: None, scale_factor, + modifiers_state: ModifiersState::default(), fullscreen: None, queued_out_of_band_redraw: false, is_dark_mode,