Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows: Implement DeviceEvents #482

Merged
merged 2 commits into from
Apr 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
- Corrected `get_position` on Windows to be relative to the screen rather than to the taskbar.
- Corrected `Moved` event on Windows to use position values equivalent to those returned by `get_position`. It previously supplied client area positions instead of window positions, and would additionally interpret negative values as being very large (around `u16::MAX`).
- Implemented `Moved` event on macOS.
- On Windows, implemented all variants of `DeviceEvent` other than `Text`. Mouse `DeviceEvent`s are now received even if the window isn't in the foreground.
- `DeviceId` on Windows is no longer a unit struct, and now contains a `u32`. For `WindowEvent`s, this will always be 0, but on `DeviceEvent`s it will be the handle to that device. `DeviceIdExt::get_persistent_identifier` can be used to acquire a unique identifier for that device that persists across replugs/reboots/etc.

# Version 0.13.1 (2018-04-26)

Expand Down
16 changes: 16 additions & 0 deletions src/os/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::os::raw::c_void;
use libc;
use MonitorId;
use DeviceId;
use Window;
use WindowBuilder;
use winapi::shared::windef::HWND;
Expand Down Expand Up @@ -56,3 +57,18 @@ impl MonitorIdExt for MonitorId {
self.inner.get_hmonitor() as *mut _
}
}

/// Additional methods on `DeviceId` that are specific to Windows.
pub trait DeviceIdExt {
/// Returns an identifier that persistently refers to this specific device.
///
/// Will return `None` if the device is no longer available.
fn get_persistent_identifier(&self) -> Option<String>;
}

impl DeviceIdExt for DeviceId {
#[inline]
fn get_persistent_identifier(&self) -> Option<String> {
self.0.get_persistent_identifier()
}
}
67 changes: 42 additions & 25 deletions src/platform/windows/event.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::char;
use std::os::raw::c_int;

use events::VirtualKeyCode;
use events::ModifiersState;

use winapi::shared::minwindef::{WPARAM, LPARAM};
use winapi::shared::minwindef::{WPARAM, LPARAM, UINT};
use winapi::um::winuser;

use ScanCode;
use std::char;

const MAPVK_VK_TO_CHAR: u32 = 2;
const MAPVK_VSC_TO_VK_EX: u32 = 3;

pub fn get_key_mods() -> ModifiersState {
let mut mods = ModifiersState::default();
Expand All @@ -29,18 +28,9 @@ pub fn get_key_mods() -> ModifiersState {
mods
}

pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
let scancode = ((lparam >> 16) & 0xff) as u32;
let extended = (lparam & 0x01000000) != 0;
let vk = match wparam as i32 {
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(scancode, MAPVK_VSC_TO_VK_EX) as i32 },
winuser::VK_CONTROL => if extended { winuser::VK_RCONTROL } else { winuser::VK_LCONTROL },
winuser::VK_MENU => if extended { winuser::VK_RMENU } else { winuser::VK_LMENU },
other => other
};

pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
// VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
(scancode, match vk {
match vkey {
//winuser::VK_LBUTTON => Some(VirtualKeyCode::Lbutton),
//winuser::VK_RBUTTON => Some(VirtualKeyCode::Rbutton),
//winuser::VK_CANCEL => Some(VirtualKeyCode::Cancel),
Expand Down Expand Up @@ -191,13 +181,13 @@ pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<
winuser::VK_OEM_COMMA => Some(VirtualKeyCode::Comma),
winuser::VK_OEM_MINUS => Some(VirtualKeyCode::Minus),
winuser::VK_OEM_PERIOD => Some(VirtualKeyCode::Period),
winuser::VK_OEM_1 => map_text_keys(vk),
winuser::VK_OEM_2 => map_text_keys(vk),
winuser::VK_OEM_3 => map_text_keys(vk),
winuser::VK_OEM_4 => map_text_keys(vk),
winuser::VK_OEM_5 => map_text_keys(vk),
winuser::VK_OEM_6 => map_text_keys(vk),
winuser::VK_OEM_7 => map_text_keys(vk),
winuser::VK_OEM_1 => map_text_keys(vkey),
winuser::VK_OEM_2 => map_text_keys(vkey),
winuser::VK_OEM_3 => map_text_keys(vkey),
winuser::VK_OEM_4 => map_text_keys(vkey),
winuser::VK_OEM_5 => map_text_keys(vkey),
winuser::VK_OEM_6 => map_text_keys(vkey),
winuser::VK_OEM_7 => map_text_keys(vkey),
/*winuser::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */
winuser::VK_OEM_102 => Some(VirtualKeyCode::OEM102),
/*winuser::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
Expand All @@ -212,13 +202,40 @@ pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<
winuser::VK_PA1 => Some(VirtualKeyCode::Pa1),
winuser::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
_ => None
})
}
}

pub fn vkey_left_right(vkey: c_int, scancode: UINT, extended: bool) -> c_int {
match vkey {
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(
scancode,
winuser::MAPVK_VSC_TO_VK_EX,
) as _ },
winuser::VK_CONTROL => if extended {
winuser::VK_RCONTROL
} else {
winuser::VK_LCONTROL
},
winuser::VK_MENU => if extended {
winuser::VK_RMENU
} else {
winuser::VK_LMENU
},
_ => vkey,
}
}

pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
let scancode = ((lparam >> 16) & 0xff) as UINT;
let extended = (lparam & 0x01000000) != 0;
let vkey = vkey_left_right(wparam as _, scancode, extended);
(scancode, vkey_to_winit_vkey(vkey))
}

// This is needed as windows doesn't properly distinguish
// some virtual key codes for different keyboard layouts
fn map_text_keys(win_virtual_key: i32) -> Option<VirtualKeyCode> {
let char_key = unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, MAPVK_VK_TO_CHAR) } & 0x7FFF;
let char_key = unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, winuser::MAPVK_VK_TO_CHAR) } & 0x7FFF;
match char::from_u32(char_key) {
Some(';') => Some(VirtualKeyCode::Semicolon),
Some('/') => Some(VirtualKeyCode::Slash),
Expand Down
147 changes: 108 additions & 39 deletions src/platform/windows/events_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, INT, UINT
use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::shared::windowsx;
use winapi::um::{winuser, shellapi, processthreadsapi};
use winapi::um::winnt::LONG;
use winapi::um::winnt::{LONG, SHORT};

use platform::platform::event;
use events::DeviceEvent;
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
use platform::platform::event::{vkey_to_winit_vkey, vkey_left_right};
use platform::platform::raw_input::*;
use platform::platform::window::adjust_size;
use platform::platform::Cursor;
use platform::platform::WindowId;
use platform::platform::DEVICE_ID;

use ControlFlow;
use CursorState;
Expand Down Expand Up @@ -567,7 +567,6 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
},

winuser::WM_MOUSEWHEEL => {
use events::{DeviceEvent, WindowEvent};
use events::MouseScrollDelta::LineDelta;
use events::TouchPhase;

Expand All @@ -580,11 +579,6 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() },
});

send_event(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::MouseWheel { delta: LineDelta(0.0, value) },
});

0
},

Expand Down Expand Up @@ -751,46 +745,121 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
0
},

winuser::WM_INPUT_DEVICE_CHANGE => {
let event = match wparam as _ {
winuser::GIDC_ARRIVAL => DeviceEvent::Added,
winuser::GIDC_REMOVAL => DeviceEvent::Removed,
_ => unreachable!(),
};

send_event(Event::DeviceEvent {
device_id: wrap_device_id(lparam as _),
event,
});

winuser::DefWindowProcW(window, msg, wparam, lparam)
},

winuser::WM_INPUT => {
use events::DeviceEvent::{Motion, MouseMotion};
let mut data: winuser::RAWINPUT = mem::uninitialized();
let mut data_size = mem::size_of::<winuser::RAWINPUT>() as UINT;
winuser::GetRawInputData(mem::transmute(lparam), winuser::RID_INPUT,
mem::transmute(&mut data), &mut data_size,
mem::size_of::<winuser::RAWINPUTHEADER>() as UINT);

if data.header.dwType == winuser::RIM_TYPEMOUSE {
let mouse = data.data.mouse();
if mouse.usFlags & winuser::MOUSE_MOVE_RELATIVE == winuser::MOUSE_MOVE_RELATIVE {
let x = mouse.lLastX as f64;
let y = mouse.lLastY as f64;

if x != 0.0 {
send_event(Event::DeviceEvent {
device_id: DEVICE_ID,
event: Motion { axis: 0, value: x }
});
use events::DeviceEvent::{Motion, MouseMotion, MouseWheel, Button, Key};
use events::MouseScrollDelta::LineDelta;
use events::ElementState::{Pressed, Released};

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 {
send_event(Event::DeviceEvent {
device_id,
event: Motion { axis: 0, value: x }
});
}

if y != 0.0 {
send_event(Event::DeviceEvent {
device_id,
event: Motion { axis: 1, value: y }
});
}

if x != 0.0 || y != 0.0 {
send_event(Event::DeviceEvent {
device_id,
event: MouseMotion { delta: (x, y) }
});
}
}

if y != 0.0 {
if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) {
let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA;
send_event(Event::DeviceEvent {
device_id: DEVICE_ID,
event: Motion { axis: 1, value: y }
device_id,
event: MouseWheel { delta: LineDelta(0.0, delta as f32) }
});
}

if x != 0.0 || y != 0.0 {
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 _;
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 _);
let vkey = vkey_left_right(
keyboard.VKey as _,
scancode,
extended,
);
let virtual_keycode = vkey_to_winit_vkey(vkey);

send_event(Event::DeviceEvent {
device_id: DEVICE_ID,
event: MouseMotion { delta: (x, y) }
device_id,
event: Key(KeyboardInput {
scancode,
state,
virtual_keycode,
modifiers: event::get_key_mods(),
}),
});
}
}

0
} else {
winuser::DefWindowProcW(window, msg, wparam, lparam)
}

winuser::DefWindowProcW(window, msg, wparam, lparam)
},

winuser::WM_TOUCH => {
Expand Down
23 changes: 20 additions & 3 deletions src/platform/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,25 @@ unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
// TODO: document what this means
pub type Cursor = *const winapi::ctypes::wchar_t;

// Constant device ID, to be removed when this backend is updated to report real device IDs.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId;
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
pub struct DeviceId(u32);

impl DeviceId {
pub fn get_persistent_identifier(&self) -> Option<String> {
if self.0 != 0 {
raw_input::get_raw_input_device_name(self.0 as _)
} else {
None
}
}
}

// Constant device ID, to be removed when this backend is updated to report real device IDs.
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId(0));

fn wrap_device_id(id: u32) -> ::DeviceId {
::DeviceId(DeviceId(id))
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(HWND);
Expand All @@ -31,4 +46,6 @@ unsafe impl Sync for WindowId {}
mod event;
mod events_loop;
mod monitor;
mod raw_input;
mod util;
mod window;
Loading