Implement ModifiersChanged on Windows, and fix bugs discovered in implementation process (#1344)

* Move DeviceEvent handling to the message target window.

Previously, device events seem to have only been sent to one particular
window, and when that window was closed Winit would stop receiving
device events. This also allows users to create windowless event loops
that process device events - an intriguing idea, to say the least.

* Emit LWin and RWin VirtualKeyCodes on Windows

* Implement ModifiersChanged on Windows

* Make ModifiersChanged a tuple variant instead of a struct variant

* Add changelog entries

* Format

* Update changelog entry

* Fix AltGr handling

* Reformat

* Publicly expose ModifiersChanged and deprecate misc. modifiers fields
This commit is contained in:
Osspial 2019-12-30 14:11:11 -05:00 committed by GitHub
parent fa7a3025ec
commit d9bda3e985
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 243 additions and 141 deletions

View file

@ -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)

View file

@ -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,
_ => (),
},
_ => (),

View file

@ -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,
}

View file

@ -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();
}

View file

@ -137,7 +137,7 @@ impl<T: 'static> EventProcessor<T> {
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<T: 'static> EventProcessor<T> {
if modifiers != new_modifiers {
callback(Event::DeviceEvent {
device_id,
event: DeviceEvent::ModifiersChanged {
modifiers: new_modifiers,
},
event: DeviceEvent::ModifiersChanged(new_modifiers),
});
}
}

View file

@ -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`");

View file

@ -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<ModifiersStateSide> for ModifiersState {
fn from(side: ModifiersStateSide) -> Self {
let mut state = ModifiersState::default();
state.set(
Self::SHIFT,
side.intersects(ModifiersStateSide::LSHIFT | ModifiersStateSide::RSHIFT),
);
state.set(
Self::CTRL,
side.intersects(ModifiersStateSide::LCTRL | ModifiersStateSide::RCTRL),
);
state.set(
Self::ALT,
side.intersects(ModifiersStateSide::LALT | ModifiersStateSide::RALT),
);
state.set(
Self::LOGO,
side.intersects(ModifiersStateSide::LLOGO | ModifiersStateSide::RLOGO),
);
state
}
}
pub fn get_pressed_keys() -> impl Iterator<Item = c_int> {
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<VirtualKeyCode> {
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),

View file

@ -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<T> SubclassInput<T> {
struct ThreadMsgTargetSubclassInput<T> {
event_loop_runner: EventLoopRunnerShared<T>,
user_event_receiver: Receiver<T>,
modifiers_state: ModifiersStateSide,
}
impl<T> ThreadMsgTargetSubclassInput<T> {
@ -169,6 +170,7 @@ impl<T: 'static> EventLoop<T> {
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<T>(event_loop_runner: EventLoopRunnerShared<T>) ->
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<T>(
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<T>(
}
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));

View file

@ -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<T: 'static>(
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;