diff --git a/CHANGELOG.md b/CHANGELOG.md index 92a8dfa2..d0459d48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,11 @@ - `RedrawEventsCleared` is issued after each set of `RedrawRequested` events. - Implement synthetic window focus key events on Windows. - **Breaking**: Change `ModifiersState` to a `bitflags` struct. +- On Windows, implement `VirtualKeyCode` translation for `LWin` and `RWin`. +- On Windows, fix closing the last opened window causing `DeviceEvent`s to stop getting emitted. - On Windows, fix `Window::set_visible` not setting internal flags correctly. This resulted in some weird behavior. +- Add `DeviceEvent::ModifiersChanged`. + - Deprecate `modifiers` fields in other events in favor of `ModifiersChanged`. # 0.20.0 Alpha 5 (2019-12-09) diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index ac3a0f27..13f14282 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -1,5 +1,5 @@ use winit::{ - event::{DeviceEvent, ElementState, Event, KeyboardInput, WindowEvent}, + event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::WindowBuilder, }; @@ -12,6 +12,8 @@ fn main() { .build(&event_loop) .unwrap(); + let mut modifiers = ModifiersState::default(); + event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; match event { @@ -22,7 +24,6 @@ fn main() { KeyboardInput { state: ElementState::Released, virtual_keycode: Some(key), - modifiers, .. }, .. @@ -43,6 +44,7 @@ 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 b66ca992..d93324f7 100644 --- a/src/event.rs +++ b/src/event.rs @@ -167,6 +167,7 @@ pub enum WindowEvent { /// 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: LogicalPosition, + #[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"] modifiers: ModifiersState, }, @@ -181,6 +182,7 @@ pub enum WindowEvent { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, + #[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"] modifiers: ModifiersState, }, @@ -189,6 +191,7 @@ pub enum WindowEvent { device_id: DeviceId, state: ElementState, button: MouseButton, + #[deprecated = "Deprecated in favor of DeviceEvent::ModifiersChanged"] modifiers: ModifiersState, }, @@ -295,11 +298,15 @@ pub enum DeviceEvent { Key(KeyboardInput), - /// Keyboard modifiers have changed - #[doc(hidden)] - ModifiersChanged { - modifiers: ModifiersState, - }, + /// 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, @@ -329,6 +336,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"] pub modifiers: ModifiersState, } diff --git a/src/platform_impl/linux/wayland/keyboard.rs b/src/platform_impl/linux/wayland/keyboard.rs index b361cb1b..7e0d2e3e 100644 --- a/src/platform_impl/linux/wayland/keyboard.rs +++ b/src/platform_impl/linux/wayland/keyboard.rs @@ -105,7 +105,7 @@ pub fn init_keyboard( my_sink .send(Event::DeviceEvent { device_id: device_id(), - event: DeviceEvent::ModifiersChanged { modifiers }, + event: DeviceEvent::ModifiersChanged(modifiers), }) .unwrap(); } diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index 762066ba..162b09df 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -137,7 +137,7 @@ impl EventProcessor { let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD); callback(Event::DeviceEvent { device_id, - event: DeviceEvent::ModifiersChanged { modifiers }, + event: DeviceEvent::ModifiersChanged(modifiers), }); } } @@ -1114,9 +1114,7 @@ impl EventProcessor { if modifiers != new_modifiers { callback(Event::DeviceEvent { device_id, - event: DeviceEvent::ModifiersChanged { - modifiers: new_modifiers, - }, + event: DeviceEvent::ModifiersChanged(new_modifiers), }); } } diff --git a/src/platform_impl/macos/view.rs b/src/platform_impl/macos/view.rs index eb373d98..daf3a6f9 100644 --- a/src/platform_impl/macos/view.rs +++ b/src/platform_impl/macos/view.rs @@ -688,9 +688,7 @@ extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) { AppState::queue_event(Event::DeviceEvent { device_id: DEVICE_ID, - event: DeviceEvent::ModifiersChanged { - modifiers: state.modifiers, - }, + event: DeviceEvent::ModifiersChanged(state.modifiers), }); } trace!("Completed `flagsChanged`"); diff --git a/src/platform_impl/windows/event.rs b/src/platform_impl/windows/event.rs index 04ef7692..49501f1a 100644 --- a/src/platform_impl/windows/event.rs +++ b/src/platform_impl/windows/event.rs @@ -36,6 +36,55 @@ pub fn get_key_mods() -> ModifiersState { mods } +bitflags! { + #[derive(Default)] + pub struct ModifiersStateSide: u32 { + const LSHIFT = 0b010 << 0; + const RSHIFT = 0b001 << 0; + + const LCTRL = 0b010 << 3; + const RCTRL = 0b001 << 3; + + const LALT = 0b010 << 6; + const RALT = 0b001 << 6; + + const LLOGO = 0b010 << 9; + const RLOGO = 0b001 << 9; + } +} + +impl ModifiersStateSide { + pub fn filter_out_altgr(&self) -> ModifiersStateSide { + match layout_uses_altgr() && self.contains(Self::RALT) { + false => *self, + true => *self & !(Self::LCTRL | Self::RCTRL | Self::LALT | Self::RALT), + } + } +} + +impl From for ModifiersState { + fn from(side: ModifiersStateSide) -> Self { + let mut state = ModifiersState::default(); + state.set( + Self::SHIFT, + side.intersects(ModifiersStateSide::LSHIFT | ModifiersStateSide::RSHIFT), + ); + state.set( + Self::CTRL, + side.intersects(ModifiersStateSide::LCTRL | ModifiersStateSide::RCTRL), + ); + state.set( + Self::ALT, + side.intersects(ModifiersStateSide::LALT | ModifiersStateSide::RALT), + ); + state.set( + Self::LOGO, + side.intersects(ModifiersStateSide::LLOGO | ModifiersStateSide::RLOGO), + ); + state + } +} + pub fn get_pressed_keys() -> impl Iterator { let mut keyboard_state = vec![0u8; 256]; unsafe { winuser::GetKeyboardState(keyboard_state.as_mut_ptr()) }; @@ -196,8 +245,8 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option { 0x58 => Some(VirtualKeyCode::X), 0x59 => Some(VirtualKeyCode::Y), 0x5A => Some(VirtualKeyCode::Z), - //winuser::VK_LWIN => Some(VirtualKeyCode::Lwin), - //winuser::VK_RWIN => Some(VirtualKeyCode::Rwin), + winuser::VK_LWIN => Some(VirtualKeyCode::LWin), + winuser::VK_RWIN => Some(VirtualKeyCode::RWin), winuser::VK_APPS => Some(VirtualKeyCode::Apps), winuser::VK_SLEEP => Some(VirtualKeyCode::Sleep), winuser::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0), diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 9d6b7eff..88de492c 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -53,10 +53,10 @@ use crate::{ become_dpi_aware, dpi_to_scale_factor, enable_non_client_dpi_scaling, hwnd_scale_factor, }, drop_handler::FileDropHandler, - event::{self, handle_extended_keys, process_key_params, vkey_to_winit_vkey}, - monitor, - raw_input::{get_raw_input_data, get_raw_mouse_button_state}, - util, + event::{ + self, handle_extended_keys, process_key_params, vkey_to_winit_vkey, ModifiersStateSide, + }, + monitor, raw_input, util, window::adjust_size, window_state::{CursorFlags, WindowFlags, WindowState}, wrap_device_id, WindowId, DEVICE_ID, @@ -112,6 +112,7 @@ impl SubclassInput { struct ThreadMsgTargetSubclassInput { event_loop_runner: EventLoopRunnerShared, user_event_receiver: Receiver, + modifiers_state: ModifiersStateSide, } impl ThreadMsgTargetSubclassInput { @@ -169,6 +170,7 @@ impl EventLoop { let runner_shared = Rc::new(ELRShared::new()); let (thread_msg_target, thread_msg_sender) = thread_event_target_window(runner_shared.clone()); + raw_input::register_all_mice_and_keyboards_for_raw_input(thread_msg_target); EventLoop { thread_msg_sender, @@ -535,6 +537,7 @@ 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( @@ -1115,120 +1118,6 @@ unsafe extern "system" fn public_window_callback( 0 } - winuser::WM_INPUT_DEVICE_CHANGE => { - let event = match wparam as _ { - winuser::GIDC_ARRIVAL => DeviceEvent::Added, - winuser::GIDC_REMOVAL => DeviceEvent::Removed, - _ => unreachable!(), - }; - - subclass_input.send_event(Event::DeviceEvent { - device_id: wrap_device_id(lparam as _), - event, - }); - - 0 - } - - winuser::WM_INPUT => { - use crate::event::{ - DeviceEvent::{Button, Key, Motion, MouseMotion, MouseWheel}, - ElementState::{Pressed, Released}, - MouseScrollDelta::LineDelta, - }; - - if let Some(data) = get_raw_input_data(lparam as _) { - let device_id = wrap_device_id(data.header.hDevice as _); - - if data.header.dwType == winuser::RIM_TYPEMOUSE { - let mouse = data.data.mouse(); - - if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { - let x = mouse.lLastX as f64; - let y = mouse.lLastY as f64; - - if x != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { axis: 0, value: x }, - }); - } - - if y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Motion { axis: 1, value: y }, - }); - } - - if x != 0.0 || y != 0.0 { - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: MouseMotion { delta: (x, y) }, - }); - } - } - - if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { - let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: MouseWheel { - delta: LineDelta(0.0, delta as f32), - }, - }); - } - - let button_state = get_raw_mouse_button_state(mouse.usButtonFlags); - // Left, middle, and right, respectively. - for (index, state) in button_state.iter().enumerate() { - if let Some(state) = *state { - // This gives us consistency with X11, since there doesn't - // seem to be anything else reasonable to do for a mouse - // button ID. - let button = (index + 1) as _; - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Button { button, state }, - }); - } - } - } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { - let keyboard = data.data.keyboard(); - - let pressed = keyboard.Message == winuser::WM_KEYDOWN - || keyboard.Message == winuser::WM_SYSKEYDOWN; - let released = keyboard.Message == winuser::WM_KEYUP - || keyboard.Message == winuser::WM_SYSKEYUP; - - if pressed || released { - let state = if pressed { Pressed } else { Released }; - - let scancode = keyboard.MakeCode as _; - let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) - | util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _); - if let Some((vkey, scancode)) = - handle_extended_keys(keyboard.VKey as _, scancode, extended) - { - let virtual_keycode = vkey_to_winit_vkey(vkey); - - subclass_input.send_event(Event::DeviceEvent { - device_id, - event: Key(KeyboardInput { - scancode, - state, - virtual_keycode, - modifiers: event::get_key_mods(), - }), - }); - } - } - } - } - - commctrl::DefSubclassProc(window, msg, wparam, lparam) - } - winuser::WM_TOUCH => { let pcount = LOWORD(wparam as DWORD) as usize; let mut inputs = Vec::with_capacity(pcount); @@ -1731,6 +1620,165 @@ unsafe extern "system" fn thread_event_target_callback( } 0 } + + winuser::WM_INPUT_DEVICE_CHANGE => { + let event = match wparam as _ { + winuser::GIDC_ARRIVAL => DeviceEvent::Added, + winuser::GIDC_REMOVAL => DeviceEvent::Removed, + _ => unreachable!(), + }; + + subclass_input.send_event(Event::DeviceEvent { + device_id: wrap_device_id(lparam as _), + event, + }); + + 0 + } + + winuser::WM_INPUT => { + use crate::event::{ + DeviceEvent::{Button, Key, ModifiersChanged, Motion, MouseMotion, MouseWheel}, + ElementState::{Pressed, Released}, + MouseScrollDelta::LineDelta, + VirtualKeyCode, + }; + + if let Some(data) = raw_input::get_raw_input_data(lparam as _) { + let device_id = wrap_device_id(data.header.hDevice as _); + + if data.header.dwType == winuser::RIM_TYPEMOUSE { + let mouse = data.data.mouse(); + + if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) { + let x = mouse.lLastX as f64; + let y = mouse.lLastY as f64; + + if x != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Motion { axis: 0, value: x }, + }); + } + + if y != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Motion { axis: 1, value: y }, + }); + } + + if x != 0.0 || y != 0.0 { + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: MouseMotion { delta: (x, y) }, + }); + } + } + + if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) { + let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA; + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: MouseWheel { + delta: LineDelta(0.0, delta as f32), + }, + }); + } + + let button_state = raw_input::get_raw_mouse_button_state(mouse.usButtonFlags); + // Left, middle, and right, respectively. + for (index, state) in button_state.iter().enumerate() { + if let Some(state) = *state { + // This gives us consistency with X11, since there doesn't + // seem to be anything else reasonable to do for a mouse + // button ID. + let button = (index + 1) as _; + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Button { button, state }, + }); + } + } + } else if data.header.dwType == winuser::RIM_TYPEKEYBOARD { + let keyboard = data.data.keyboard(); + + let pressed = keyboard.Message == winuser::WM_KEYDOWN + || keyboard.Message == winuser::WM_SYSKEYDOWN; + let released = keyboard.Message == winuser::WM_KEYUP + || keyboard.Message == winuser::WM_SYSKEYUP; + + if pressed || released { + let state = if pressed { Pressed } else { Released }; + + let scancode = keyboard.MakeCode as _; + let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _) + | util::has_flag(keyboard.Flags, winuser::RI_KEY_E1 as _); + + if let Some((vkey, scancode)) = + handle_extended_keys(keyboard.VKey as _, scancode, extended) + { + 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), + }); + } + + subclass_input.send_event(Event::DeviceEvent { + device_id, + event: Key(KeyboardInput { + scancode, + state, + virtual_keycode, + modifiers: new_modifiers_state, + }), + }); + } + } + } + } + + commctrl::DefSubclassProc(window, msg, wparam, lparam) + } + _ if msg == *USER_EVENT_MSG_ID => { if let Ok(event) = subclass_input.user_event_receiver.recv() { subclass_input.send_event(Event::UserEvent(event)); diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index d99f8645..012b8402 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -39,9 +39,7 @@ use crate::{ drop_handler::FileDropHandler, event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID}, icon::{self, IconType, WinIcon}, - monitor, - raw_input::register_all_mice_and_keyboards_for_raw_input, - util, + monitor, util, window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState}, PlatformSpecificWindowBuilderAttributes, WindowId, }, @@ -845,9 +843,6 @@ unsafe fn init( WindowWrapper(handle) }; - // Set up raw input - register_all_mice_and_keyboards_for_raw_input(real_window.0); - // Register for touch events if applicable { let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32;