diff --git a/examples/cursor.rs b/examples/cursor.rs index bba013a9..0f65e10a 100644 --- a/examples/cursor.rs +++ b/examples/cursor.rs @@ -13,7 +13,7 @@ fn main() { events_loop.run_forever(|event| { match event { - Event::WindowEvent { event: WindowEvent::KeyboardInput(ElementState::Pressed, _, _), .. } => { + Event::WindowEvent { event: WindowEvent::KeyboardInput(ElementState::Pressed, _, _, _), .. } => { println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]); window.set_cursor(cursors[cursor_idx]); if cursor_idx < cursors.len() - 1 { diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index bfb179e0..308d9dcf 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -37,7 +37,7 @@ fn main() { winit::Event::WindowEvent { event, .. } => { match event { winit::WindowEvent::Closed => events_loop.interrupt(), - winit::WindowEvent::KeyboardInput(_, _, Some(winit::VirtualKeyCode::Escape)) => events_loop.interrupt(), + winit::WindowEvent::KeyboardInput(_, _, Some(winit::VirtualKeyCode::Escape), _) => events_loop.interrupt(), _ => () } }, diff --git a/examples/grabbing.rs b/examples/grabbing.rs index ff3afb2a..d156da43 100644 --- a/examples/grabbing.rs +++ b/examples/grabbing.rs @@ -16,7 +16,7 @@ fn main() { match event { winit::Event::WindowEvent { event, .. } => { match event { - WindowEvent::KeyboardInput(ElementState::Pressed, _, _) => { + WindowEvent::KeyboardInput(ElementState::Pressed, _, _, _) => { if grabbed { grabbed = false; window.set_cursor_state(winit::CursorState::Normal) diff --git a/src/api/wayland/context.rs b/src/api/wayland/context.rs index debd5e6b..de9d21bf 100644 --- a/src/api/wayland/context.rs +++ b/src/api/wayland/context.rs @@ -1,5 +1,7 @@ use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase}; +use events::ModifiersState; + use std::collections::VecDeque; use std::sync::{Arc, Mutex}; @@ -627,10 +629,12 @@ impl wl_keyboard::Handler for WaylandEnv { wl_keyboard::KeyState::Released => ElementState::Released, }; let mut guard = eviter.lock().unwrap(); + // TODO implement ModifiersState guard.push_back(Event::KeyboardInput( state, key as u8, - None + None, + ModifiersState::default() )); }, KbdType::Plain(None) => () diff --git a/src/api/wayland/keyboard.rs b/src/api/wayland/keyboard.rs index 89186579..8b571a1a 100644 --- a/src/api/wayland/keyboard.rs +++ b/src/api/wayland/keyboard.rs @@ -3,6 +3,8 @@ use std::sync::{Arc, Mutex}; use {VirtualKeyCode, ElementState, WindowEvent as Event}; +use events::ModifiersState; + use super::wayland_kbd; use wayland_client::EventQueueHandle; use wayland_client::protocol::wl_keyboard; @@ -35,7 +37,8 @@ impl wayland_kbd::Handler for KbdHandler { }; let vkcode = key_to_vkey(rawkey, keysym); let mut guard = eviter.lock().unwrap(); - guard.push_back(Event::KeyboardInput(state, rawkey as u8, vkcode)); + // TODO implement ModifiersState + guard.push_back(Event::KeyboardInput(state, rawkey as u8, vkcode, ModifiersState::default())); // send char event only on key press, not release if let ElementState::Released = state { return } if let Some(txt) = utf8 { diff --git a/src/api/x11/input.rs b/src/api/x11/input.rs index 7185a449..6f05704c 100644 --- a/src/api/x11/input.rs +++ b/src/api/x11/input.rs @@ -8,6 +8,7 @@ use std::slice::from_raw_parts; use WindowAttributes; use events::WindowEvent as Event; +use events::ModifiersState; use super::{events, ffi}; use super::XConnection; @@ -139,6 +140,8 @@ impl XInputEventHandler { let mut kp_keysym = 0; + let mut ev_mods = ModifiersState::default(); + let written = unsafe { use std::str; @@ -148,6 +151,26 @@ impl XInputEventHandler { mem::transmute(buffer.as_mut_ptr()), buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut()); + { + // Translate x event state to mods + let state = event.state; + if (state & ffi::Mod1Mask) != 0 { + ev_mods.alt = true; + } + + if (state & ffi::ShiftMask) != 0 { + ev_mods.shift = true; + } + + if (state & ffi::ControlMask) != 0 { + ev_mods.ctrl = true; + } + + if (state & ffi::Mod4Mask) != 0 { + ev_mods.logo = true; + } + } + str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string() }; @@ -165,7 +188,7 @@ impl XInputEventHandler { let vkey = events::keycode_to_element(keysym as libc::c_uint); - translated_events.push(KeyboardInput(state, event.keycode as u8, vkey)); + translated_events.push(KeyboardInput(state, event.keycode as u8, vkey, ev_mods)); translated_events } diff --git a/src/events.rs b/src/events.rs index 7288ea9e..851ef0c6 100644 --- a/src/events.rs +++ b/src/events.rs @@ -35,7 +35,7 @@ pub enum WindowEvent { Focused(bool), /// An event from the keyboard has been received. - KeyboardInput(ElementState, ScanCode, Option), + KeyboardInput(ElementState, ScanCode, Option, ModifiersState), /// The cursor has moved on the window. /// @@ -320,3 +320,20 @@ pub enum VirtualKeyCode { WebStop, Yen, } + +/// Represents the current state of the keyboard modifiers +/// +/// Each field of this struct represents a modifier and is `true` if this modifier is active. +#[derive(Default, Debug, Clone, Copy)] +pub struct ModifiersState { + /// The "shift" key + pub shift: bool, + /// The "control" key + pub ctrl: bool, + /// The "alt" key + pub alt: bool, + /// The "logo" key + /// + /// This is the "windows" key on PC and "command" key on Mac. + pub logo: bool +} diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index 458c69b5..f656468e 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -1,6 +1,6 @@ use cocoa::{self, appkit, foundation}; use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow}; -use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent}; +use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState}; use super::window::Window; use std; @@ -271,7 +271,7 @@ impl EventsLoop { let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event)); let state = ElementState::Pressed; let code = NSEvent::keyCode(ns_event) as u8; - let window_event = WindowEvent::KeyboardInput(state, code, vkey); + let window_event = WindowEvent::KeyboardInput(state, code, vkey, event_mods(ns_event)); events.push_back(into_event(window_event)); let event = events.pop_front(); self.pending_events.lock().unwrap().extend(events.into_iter()); @@ -283,7 +283,7 @@ impl EventsLoop { let state = ElementState::Released; let code = NSEvent::keyCode(ns_event) as u8; - let window_event = WindowEvent::KeyboardInput(state, code, vkey); + let window_event = WindowEvent::KeyboardInput(state, code, vkey, event_mods(ns_event)); Some(into_event(window_event)) }, @@ -298,13 +298,13 @@ impl EventsLoop { if !key_pressed && NSEvent::modifierFlags(event).contains(keymask) { let state = ElementState::Pressed; let code = NSEvent::keyCode(event) as u8; - let window_event = WindowEvent::KeyboardInput(state, code, Some(key)); + let window_event = WindowEvent::KeyboardInput(state, code, Some(key), event_mods(event)); Some(window_event) } else if key_pressed && !NSEvent::modifierFlags(event).contains(keymask) { let state = ElementState::Released; let code = NSEvent::keyCode(event) as u8; - let window_event = WindowEvent::KeyboardInput(state, code, Some(key)); + let window_event = WindowEvent::KeyboardInput(state, code, Some(key), event_mods(event)); Some(window_event) } else { @@ -575,3 +575,15 @@ fn to_virtual_key_code(code: u16) -> Option { _ => return None, }) } + +fn event_mods(event: cocoa::base::id) -> ModifiersState { + let flags = unsafe { + NSEvent::modifierFlags(event) + }; + ModifiersState { + shift: flags.contains(appkit::NSShiftKeyMask), + ctrl: flags.contains(appkit::NSControlKeyMask), + alt: flags.contains(appkit::NSAlternateKeyMask), + logo: flags.contains(appkit::NSCommandKeyMask), + } +} \ No newline at end of file diff --git a/src/platform/windows/callback.rs b/src/platform/windows/callback.rs index de611640..e8330107 100644 --- a/src/platform/windows/callback.rs +++ b/src/platform/windows/callback.rs @@ -8,6 +8,7 @@ use std::os::windows::ffi::OsStringExt; use CursorState; use WindowEvent as Event; +use events::ModifiersState; use super::event; use super::WindowState; @@ -200,7 +201,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, user32::DefWindowProcW(window, msg, wparam, lparam) } else { let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam); - send_event(window, KeyboardInput(Pressed, scancode, vkey)); + send_event(window, KeyboardInput(Pressed, scancode, vkey, event::get_key_mods())); 0 } }, @@ -209,7 +210,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, use events::WindowEvent::KeyboardInput; use events::ElementState::Released; let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam); - send_event(window, KeyboardInput(Released, scancode, vkey)); + send_event(window, KeyboardInput(Released, scancode, vkey, event::get_key_mods())); 0 }, diff --git a/src/platform/windows/event.rs b/src/platform/windows/event.rs index a4de7503..699f05d1 100644 --- a/src/platform/windows/event.rs +++ b/src/platform/windows/event.rs @@ -1,10 +1,30 @@ use events::VirtualKeyCode; +use events::ModifiersState; use winapi; use user32; use ScanCode; const MAPVK_VSC_TO_VK_EX: u32 = 3; +pub fn get_key_mods() -> ModifiersState { + let mut mods = ModifiersState::default(); + unsafe { + if user32::GetKeyState(winapi::VK_SHIFT) & (1 << 15) == (1 << 15) { + mods.shift = true; + } + if user32::GetKeyState(winapi::VK_CONTROL) & (1 << 15) == (1 << 15) { + mods.ctrl = true; + } + if user32::GetKeyState(winapi::VK_MENU) & (1 << 15) == (1 << 15) { + mods.alt = true; + } + if (user32::GetKeyState(winapi::VK_LWIN) | user32::GetKeyState(winapi::VK_RWIN)) & (1 << 15) == (1 << 15) { + mods.logo = true; + } + } + mods +} + pub fn vkeycode_to_element(wparam: winapi::WPARAM, lparam: winapi::LPARAM) -> (ScanCode, Option) { let scancode = ((lparam >> 16) & 0xff) as u8; let extended = (lparam & 0x01000000) != 0;