From 9748e16ebe9346dd6830c24599308d7f912bf1d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Fri, 13 Nov 2020 20:41:17 +0100 Subject: [PATCH 1/8] Add support for macOS and X11 key events with code from druid --- Cargo.toml | 1 + src/event.rs | 10 +- src/keyboard.rs | 274 +++++++++++++++++++++++++-- src/lib.rs | 2 +- src/macos/keyboard.rs | 367 +++++++++++++++++++++++++++++++++++ src/macos/mod.rs | 1 + src/macos/view.rs | 39 +++- src/macos/window.rs | 14 +- src/x11/keyboard.rs | 431 ++++++++++++++++++++++++++++++++++++++++++ src/x11/mod.rs | 3 +- src/x11/window.rs | 19 +- 11 files changed, 1124 insertions(+), 37 deletions(-) create mode 100644 src/macos/keyboard.rs create mode 100644 src/x11/keyboard.rs diff --git a/Cargo.toml b/Cargo.toml index 31b76e7..8f4c5d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ license = "MIT OR Apache-2.0" [dependencies] log = "0.4.11" +keyboard-types = "0.5.0" raw-window-handle = "0.3.3" [target.'cfg(target_os="linux")'.dependencies] diff --git a/src/event.rs b/src/event.rs index 6fae242..23face7 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,12 +1,12 @@ -use crate::{WindowInfo, Point}; +use crate::{WindowInfo, Point, KeyEvent}; -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub enum KeyboardEvent { - KeyPressed(u32), - KeyReleased(u32), - CharacterInput(char), + KeyPressed(KeyEvent), + KeyReleased(KeyEvent), } + #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum MouseButton { Left, diff --git a/src/keyboard.rs b/src/keyboard.rs index 796bf7e..796e180 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,26 +1,260 @@ -// TODO: Add a method to the Window that returns the -// current modifier state. +// Copyright 2020 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -/// The current state of the keyboard modifiers. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct ModifiersState { - pub shift: bool, - pub control: bool, - pub alt: bool, - pub logo: bool, +// Baseview modifications to druid code: +// - move code_to_location function to this file + +//! Keyboard types. + +// This is a reasonable lint, but we keep signatures in sync with the +// bitflags implementation of the inner Modifiers type. +#![allow(clippy::trivially_copy_pass_by_ref)] + +use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; + +pub use keyboard_types::{Code, KeyState, Location}; + +/// The meaning (mapped value) of a keypress. +pub type KbKey = keyboard_types::Key; + +/// Information about a keyboard event. +/// +/// Note that this type is similar to [`KeyboardEvent`] in keyboard-types, +/// but has a few small differences for convenience. It is missing the `state` +/// field because that is already implicit in the event. +/// +/// [`KeyboardEvent`]: keyboard_types::KeyboardEvent +#[non_exhaustive] +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct KeyEvent { + /// Whether the key is pressed or released. + pub state: KeyState, + /// Logical key value. + pub key: KbKey, + /// Physical key position. + pub code: Code, + /// Location for keys with multiple instances on common keyboards. + pub location: Location, + /// Flags for pressed modifier keys. + pub mods: Modifiers, + /// True if the key is currently auto-repeated. + pub repeat: bool, + /// Events with this flag should be ignored in a text editor + /// and instead composition events should be used. + pub is_composing: bool, } -impl ModifiersState { - /// Returns true if the current [`ModifiersState`] has at least the same - /// modifiers enabled as the given value, and false otherwise. - /// - /// [`ModifiersState`]: struct.ModifiersState.html - pub fn matches_atleast(&self, modifiers: ModifiersState) -> bool { - let shift = !modifiers.shift || self.shift; - let control = !modifiers.control || self.control; - let alt = !modifiers.alt || self.alt; - let logo = !modifiers.logo || self.logo; +/// The modifiers. +/// +/// This type is a thin wrappers around [`keyboard_types::Modifiers`], +/// mostly for the convenience methods. If those get upstreamed, it +/// will simply become that type. +/// +/// [`keyboard_types::Modifiers`]: keyboard_types::Modifiers +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +pub struct Modifiers(keyboard_types::Modifiers); - shift && control && alt && logo +/// A convenience trait for creating Key objects. +/// +/// This trait is implemented by [`KbKey`] itself and also strings, which are +/// converted into the `Character` variant. It is defined this way and not +/// using the standard `Into` mechanism because `KbKey` is a type in an external +/// crate. +/// +/// [`KbKey`]: KbKey +pub trait IntoKey { + fn into_key(self) -> KbKey; +} + +impl KeyEvent { + #[doc(hidden)] + /// Create a key event for testing purposes. + pub fn for_test(mods: impl Into, key: impl IntoKey) -> KeyEvent { + let mods = mods.into(); + let key = key.into_key(); + KeyEvent { + key, + code: Code::Unidentified, + location: Location::Standard, + state: KeyState::Down, + mods, + is_composing: false, + repeat: false, + } } } + +impl Modifiers { + pub const ALT: Modifiers = Modifiers(keyboard_types::Modifiers::ALT); + pub const ALT_GRAPH: Modifiers = Modifiers(keyboard_types::Modifiers::ALT_GRAPH); + pub const CAPS_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::CAPS_LOCK); + pub const CONTROL: Modifiers = Modifiers(keyboard_types::Modifiers::CONTROL); + pub const FN: Modifiers = Modifiers(keyboard_types::Modifiers::FN); + pub const FN_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::FN_LOCK); + pub const META: Modifiers = Modifiers(keyboard_types::Modifiers::META); + pub const NUM_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::NUM_LOCK); + pub const SCROLL_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::SCROLL_LOCK); + pub const SHIFT: Modifiers = Modifiers(keyboard_types::Modifiers::SHIFT); + pub const SYMBOL: Modifiers = Modifiers(keyboard_types::Modifiers::SYMBOL); + pub const SYMBOL_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::SYMBOL_LOCK); + pub const HYPER: Modifiers = Modifiers(keyboard_types::Modifiers::HYPER); + pub const SUPER: Modifiers = Modifiers(keyboard_types::Modifiers::SUPER); + + /// Get the inner value. + /// + /// Note that this function might go away if our changes are upstreamed. + pub fn raw(&self) -> keyboard_types::Modifiers { + self.0 + } + + /// Determine whether Shift is set. + pub fn shift(&self) -> bool { + self.contains(Modifiers::SHIFT) + } + + /// Determine whether Ctrl is set. + pub fn ctrl(&self) -> bool { + self.contains(Modifiers::CONTROL) + } + + /// Determine whether Alt is set. + pub fn alt(&self) -> bool { + self.contains(Modifiers::ALT) + } + + /// Determine whether Meta is set. + pub fn meta(&self) -> bool { + self.contains(Modifiers::META) + } + + /// Returns an empty set of modifiers. + pub fn empty() -> Modifiers { + Default::default() + } + + /// Returns `true` if no modifiers are set. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Returns `true` if all the modifiers in `other` are set. + pub fn contains(&self, other: Modifiers) -> bool { + self.0.contains(other.0) + } + + /// Inserts or removes the specified modifiers depending on the passed value. + pub fn set(&mut self, other: Modifiers, value: bool) { + self.0.set(other.0, value) + } +} + +impl BitAnd for Modifiers { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self { + Modifiers(self.0 & rhs.0) + } +} + +impl BitAndAssign for Modifiers { + // rhs is the "right-hand side" of the expression `a &= b` + fn bitand_assign(&mut self, rhs: Self) { + *self = Modifiers(self.0 & rhs.0) + } +} + +impl BitOr for Modifiers { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self { + Modifiers(self.0 | rhs.0) + } +} + +impl BitOrAssign for Modifiers { + // rhs is the "right-hand side" of the expression `a &= b` + fn bitor_assign(&mut self, rhs: Self) { + *self = Modifiers(self.0 | rhs.0) + } +} + +impl BitXor for Modifiers { + type Output = Self; + + fn bitxor(self, rhs: Self) -> Self { + Modifiers(self.0 ^ rhs.0) + } +} + +impl BitXorAssign for Modifiers { + // rhs is the "right-hand side" of the expression `a &= b` + fn bitxor_assign(&mut self, rhs: Self) { + *self = Modifiers(self.0 ^ rhs.0) + } +} + +impl Not for Modifiers { + type Output = Self; + + fn not(self) -> Self { + Modifiers(!self.0) + } +} + +impl IntoKey for KbKey { + fn into_key(self) -> KbKey { + self + } +} + +impl IntoKey for &str { + fn into_key(self) -> KbKey { + KbKey::Character(self.into()) + } +} + + +#[cfg(any(all(feature = "x11", target_os = "linux"), target_os = "macos"))] +/// Map key code to location. +/// +/// The logic for this is adapted from InitKeyEvent in TextInputHandler (in the Mozilla +/// mac port). +/// +/// Note: in the original, this is based on kVK constants, but since we don't have those +/// readily available, we use the mapping to code (which should be effectively lossless). +pub fn code_to_location(code: Code) -> Location { + match code { + Code::MetaLeft | Code::ShiftLeft | Code::AltLeft | Code::ControlLeft => Location::Left, + Code::MetaRight | Code::ShiftRight | Code::AltRight | Code::ControlRight => Location::Right, + Code::Numpad0 + | Code::Numpad1 + | Code::Numpad2 + | Code::Numpad3 + | Code::Numpad4 + | Code::Numpad5 + | Code::Numpad6 + | Code::Numpad7 + | Code::Numpad8 + | Code::Numpad9 + | Code::NumpadAdd + | Code::NumpadComma + | Code::NumpadDecimal + | Code::NumpadDivide + | Code::NumpadEnter + | Code::NumpadEqual + | Code::NumpadMultiply + | Code::NumpadSubtract => Location::Numpad, + _ => Location::Standard, + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 642ac54..ef2f728 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ mod mouse_cursor; mod window_info; mod window_open_options; pub use event::*; -pub use keyboard::*; +pub use keyboard::KeyEvent; pub use mouse_cursor::MouseCursor; pub use window_info::*; pub use window_open_options::*; diff --git a/src/macos/keyboard.rs b/src/macos/keyboard.rs new file mode 100644 index 0000000..40cc511 --- /dev/null +++ b/src/macos/keyboard.rs @@ -0,0 +1,367 @@ +// Copyright 2020 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Baseview modifications to druid code: +// - move from_nsstring function to this file +// - update imports, paths etc + +//! Conversion of platform keyboard event into cross-platform event. + +use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventType}; +use cocoa::foundation::NSString; +use cocoa::base::id; +use keyboard_types::{Code, KeyState}; +use objc::{msg_send, sel, sel_impl}; + +use crate::keyboard::{code_to_location, KbKey, KeyEvent, Modifiers}; + + +pub(crate) fn from_nsstring(s: id) -> String { + unsafe { + let slice = std::slice::from_raw_parts(s.UTF8String() as *const _, s.len()); + let result = std::str::from_utf8_unchecked(slice); + result.into() + } +} + +/// State for processing of keyboard events. +/// +/// This needs to be stateful for proper processing of dead keys. The current +/// implementation is somewhat primitive and is not based on IME; in the future +/// when IME is implemented, it will need to be redone somewhat, letting the IME +/// be the authoritative source of truth for Unicode string values of keys. +/// +/// Most of the logic in this module is adapted from Mozilla, and in particular +/// TextInputHandler.mm. +pub(crate) struct KeyboardState { + last_mods: NSEventModifierFlags, +} + +/// Convert a macOS platform key code (keyCode field of NSEvent). +/// +/// The primary source for this mapping is: +/// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values +/// +/// It should also match up with CODE_MAP_MAC bindings in +/// NativeKeyToDOMCodeName.h. +fn key_code_to_code(key_code: u16) -> Code { + match key_code { + 0x00 => Code::KeyA, + 0x01 => Code::KeyS, + 0x02 => Code::KeyD, + 0x03 => Code::KeyF, + 0x04 => Code::KeyH, + 0x05 => Code::KeyG, + 0x06 => Code::KeyZ, + 0x07 => Code::KeyX, + 0x08 => Code::KeyC, + 0x09 => Code::KeyV, + 0x0a => Code::IntlBackslash, + 0x0b => Code::KeyB, + 0x0c => Code::KeyQ, + 0x0d => Code::KeyW, + 0x0e => Code::KeyE, + 0x0f => Code::KeyR, + 0x10 => Code::KeyY, + 0x11 => Code::KeyT, + 0x12 => Code::Digit1, + 0x13 => Code::Digit2, + 0x14 => Code::Digit3, + 0x15 => Code::Digit4, + 0x16 => Code::Digit6, + 0x17 => Code::Digit5, + 0x18 => Code::Equal, + 0x19 => Code::Digit9, + 0x1a => Code::Digit7, + 0x1b => Code::Minus, + 0x1c => Code::Digit8, + 0x1d => Code::Digit0, + 0x1e => Code::BracketRight, + 0x1f => Code::KeyO, + 0x20 => Code::KeyU, + 0x21 => Code::BracketLeft, + 0x22 => Code::KeyI, + 0x23 => Code::KeyP, + 0x24 => Code::Enter, + 0x25 => Code::KeyL, + 0x26 => Code::KeyJ, + 0x27 => Code::Quote, + 0x28 => Code::KeyK, + 0x29 => Code::Semicolon, + 0x2a => Code::Backslash, + 0x2b => Code::Comma, + 0x2c => Code::Slash, + 0x2d => Code::KeyN, + 0x2e => Code::KeyM, + 0x2f => Code::Period, + 0x30 => Code::Tab, + 0x31 => Code::Space, + 0x32 => Code::Backquote, + 0x33 => Code::Backspace, + 0x34 => Code::NumpadEnter, + 0x35 => Code::Escape, + 0x36 => Code::MetaRight, + 0x37 => Code::MetaLeft, + 0x38 => Code::ShiftLeft, + 0x39 => Code::CapsLock, + // Note: in the linked source doc, this is "OSLeft" + 0x3a => Code::AltLeft, + 0x3b => Code::ControlLeft, + 0x3c => Code::ShiftRight, + // Note: in the linked source doc, this is "OSRight" + 0x3d => Code::AltRight, + 0x3e => Code::ControlRight, + 0x3f => Code::Fn, // No events fired + //0x40 => Code::F17, + 0x41 => Code::NumpadDecimal, + 0x43 => Code::NumpadMultiply, + 0x45 => Code::NumpadAdd, + 0x47 => Code::NumLock, + 0x48 => Code::AudioVolumeUp, + 0x49 => Code::AudioVolumeDown, + 0x4a => Code::AudioVolumeMute, + 0x4b => Code::NumpadDivide, + 0x4c => Code::NumpadEnter, + 0x4e => Code::NumpadSubtract, + //0x4f => Code::F18, + //0x50 => Code::F19, + 0x51 => Code::NumpadEqual, + 0x52 => Code::Numpad0, + 0x53 => Code::Numpad1, + 0x54 => Code::Numpad2, + 0x55 => Code::Numpad3, + 0x56 => Code::Numpad4, + 0x57 => Code::Numpad5, + 0x58 => Code::Numpad6, + 0x59 => Code::Numpad7, + //0x5a => Code::F20, + 0x5b => Code::Numpad8, + 0x5c => Code::Numpad9, + 0x5d => Code::IntlYen, + 0x5e => Code::IntlRo, + 0x5f => Code::NumpadComma, + 0x60 => Code::F5, + 0x61 => Code::F6, + 0x62 => Code::F7, + 0x63 => Code::F3, + 0x64 => Code::F8, + 0x65 => Code::F9, + 0x66 => Code::Lang2, + 0x67 => Code::F11, + 0x68 => Code::Lang1, + // Note: this is listed as F13, but in testing with a standard + // USB kb, this the code produced by PrtSc. + 0x69 => Code::PrintScreen, + //0x6a => Code::F16, + //0x6b => Code::F14, + 0x6d => Code::F10, + 0x6e => Code::ContextMenu, + 0x6f => Code::F12, + //0x71 => Code::F15, + 0x72 => Code::Help, + 0x73 => Code::Home, + 0x74 => Code::PageUp, + 0x75 => Code::Delete, + 0x76 => Code::F4, + 0x77 => Code::End, + 0x78 => Code::F2, + 0x79 => Code::PageDown, + 0x7a => Code::F1, + 0x7b => Code::ArrowLeft, + 0x7c => Code::ArrowRight, + 0x7d => Code::ArrowDown, + 0x7e => Code::ArrowUp, + _ => Code::Unidentified, + } +} + +/// Convert code to key. +/// +/// On macOS, for non-printable keys, the keyCode we get from the event serves is +/// really more of a key than a physical scan code. +/// +/// When this function returns None, the code can be considered printable. +/// +/// The logic for this function is derived from KEY_MAP_COCOA bindings in +/// NativeKeyToDOMKeyName.h. +fn code_to_key(code: Code) -> Option { + Some(match code { + Code::Escape => KbKey::Escape, + Code::ShiftLeft | Code::ShiftRight => KbKey::Shift, + Code::AltLeft | Code::AltRight => KbKey::Alt, + Code::MetaLeft | Code::MetaRight => KbKey::Meta, + Code::ControlLeft | Code::ControlRight => KbKey::Control, + Code::CapsLock => KbKey::CapsLock, + // kVK_ANSI_KeypadClear + Code::NumLock => KbKey::Clear, + Code::Fn => KbKey::Fn, + Code::F1 => KbKey::F1, + Code::F2 => KbKey::F2, + Code::F3 => KbKey::F3, + Code::F4 => KbKey::F4, + Code::F5 => KbKey::F5, + Code::F6 => KbKey::F6, + Code::F7 => KbKey::F7, + Code::F8 => KbKey::F8, + Code::F9 => KbKey::F9, + Code::F10 => KbKey::F10, + Code::F11 => KbKey::F11, + Code::F12 => KbKey::F12, + Code::Pause => KbKey::Pause, + Code::ScrollLock => KbKey::ScrollLock, + Code::PrintScreen => KbKey::PrintScreen, + Code::Insert => KbKey::Insert, + Code::Delete => KbKey::Delete, + Code::Tab => KbKey::Tab, + Code::Backspace => KbKey::Backspace, + Code::ContextMenu => KbKey::ContextMenu, + // kVK_JIS_Kana + Code::Lang1 => KbKey::KanjiMode, + // kVK_JIS_Eisu + Code::Lang2 => KbKey::Eisu, + Code::Home => KbKey::Home, + Code::End => KbKey::End, + Code::PageUp => KbKey::PageUp, + Code::PageDown => KbKey::PageDown, + Code::ArrowLeft => KbKey::ArrowLeft, + Code::ArrowRight => KbKey::ArrowRight, + Code::ArrowUp => KbKey::ArrowUp, + Code::ArrowDown => KbKey::ArrowDown, + Code::Enter => KbKey::Enter, + Code::NumpadEnter => KbKey::Enter, + Code::Help => KbKey::Help, + _ => return None, + }) +} + + +fn is_valid_key(s: &str) -> bool { + match s.chars().next() { + None => false, + Some(c) => c >= ' ' && c != '\x7f' && !('\u{e000}'..'\u{f900}').contains(&c), + } +} + + +fn is_modifier_code(code: Code) -> bool { + matches!( + code, + Code::ShiftLeft + | Code::ShiftRight + | Code::AltLeft + | Code::AltRight + | Code::ControlLeft + | Code::ControlRight + | Code::MetaLeft + | Code::MetaRight + | Code::CapsLock + | Code::Help + ) +} + + +impl KeyboardState { + pub(crate) fn new() -> KeyboardState { + let last_mods = NSEventModifierFlags::empty(); + KeyboardState { last_mods } + } + + pub(crate) fn process_native_event(&mut self, event: id) -> Option { + unsafe { + let event_type = event.eventType(); + let key_code = event.keyCode(); + let code = key_code_to_code(key_code); + let location = code_to_location(code); + let raw_mods = event.modifierFlags(); + let mods = make_modifiers(raw_mods); + let state = match event_type { + NSEventType::NSKeyDown => KeyState::Down, + NSEventType::NSKeyUp => KeyState::Up, + NSEventType::NSFlagsChanged => { + // We use `bits` here because we want to distinguish the + // device dependent bits (when both left and right keys + // may be pressed, for example). + let any_down = raw_mods.bits() & !self.last_mods.bits(); + self.last_mods = raw_mods; + if is_modifier_code(code) { + if any_down == 0 { + KeyState::Up + } else { + KeyState::Down + } + } else { + // HandleFlagsChanged has some logic for this; it might + // happen when an app is deactivated by Command-Tab. In + // that case, the best thing to do is synthesize the event + // from the modifiers. But a challenge there is that we + // might get multiple events. + return None; + } + } + _ => unreachable!(), + }; + let is_composing = false; + let repeat: bool = event_type == NSEventType::NSKeyDown && msg_send![event, isARepeat]; + let key = if let Some(key) = code_to_key(code) { + key + } else { + let characters = from_nsstring(event.characters()); + if is_valid_key(&characters) { + KbKey::Character(characters) + } else { + let chars_ignoring = from_nsstring(event.charactersIgnoringModifiers()); + if is_valid_key(&chars_ignoring) { + KbKey::Character(chars_ignoring) + } else { + // There may be more heroic things we can do here. + KbKey::Unidentified + } + } + }; + let event = KeyEvent { + code, + key, + location, + mods, + state, + is_composing, + repeat, + }; + Some(event) + } + } +} + + +const MODIFIER_MAP: &[(NSEventModifierFlags, Modifiers)] = &[ + (NSEventModifierFlags::NSShiftKeyMask, Modifiers::SHIFT), + (NSEventModifierFlags::NSAlternateKeyMask, Modifiers::ALT), + (NSEventModifierFlags::NSControlKeyMask, Modifiers::CONTROL), + (NSEventModifierFlags::NSCommandKeyMask, Modifiers::META), + ( + NSEventModifierFlags::NSAlphaShiftKeyMask, + Modifiers::CAPS_LOCK, + ), +]; + + +pub(crate) fn make_modifiers(raw: NSEventModifierFlags) -> Modifiers { + let mut modifiers = Modifiers::empty(); + for &(flags, mods) in MODIFIER_MAP { + if raw.contains(flags) { + modifiers |= mods; + } + } + modifiers +} \ No newline at end of file diff --git a/src/macos/mod.rs b/src/macos/mod.rs index c0efe14..181bc37 100644 --- a/src/macos/mod.rs +++ b/src/macos/mod.rs @@ -1,3 +1,4 @@ +mod keyboard; mod window; mod view; diff --git a/src/macos/view.rs b/src/macos/view.rs index d14373f..0de1dc2 100644 --- a/src/macos/view.rs +++ b/src/macos/view.rs @@ -15,7 +15,7 @@ use objc::{ use uuid::Uuid; use crate::{ - Event, MouseButton, MouseEvent, Point, WindowHandler, + Event, KeyboardEvent, MouseButton, MouseEvent, Point, WindowHandler, WindowOpenOptions }; use crate::MouseEvent::{ButtonPressed, ButtonReleased}; @@ -134,6 +134,15 @@ unsafe fn create_view_class() -> &'static Class { middle_mouse_up:: as extern "C" fn(&Object, Sel, id), ); + class.add_method( + sel!(keyDown:), + key_down:: as extern "C" fn(&Object, Sel, id), + ); + class.add_method( + sel!(keyUp:), + key_up:: as extern "C" fn(&Object, Sel, id), + ); + class.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR_NAME); class.register() @@ -333,4 +342,30 @@ mouse_simple_extern_fn!(middle_mouse_down, ButtonPressed(MouseButton::Middle)); mouse_simple_extern_fn!(middle_mouse_up, ButtonReleased(MouseButton::Middle)); mouse_simple_extern_fn!(mouse_entered, MouseEvent::CursorEntered); -mouse_simple_extern_fn!(mouse_exited, MouseEvent::CursorLeft); \ No newline at end of file +mouse_simple_extern_fn!(mouse_exited, MouseEvent::CursorLeft); + + +extern "C" fn key_down(this: &Object, _: Sel, event: id){ + let state: &mut WindowState = unsafe { + WindowState::from_field(this) + }; + + if let Some(key_event) = state.process_native_key_event(event){ + let event = Event::Keyboard(KeyboardEvent::KeyPressed(key_event)); + + state.trigger_event(event); + } +} + + +extern "C" fn key_up(this: &Object, _: Sel, event: id){ + let state: &mut WindowState = unsafe { + WindowState::from_field(this) + }; + + if let Some(key_event) = state.process_native_key_event(event){ + let event = Event::Keyboard(KeyboardEvent::KeyReleased(key_event)); + + state.trigger_event(event); + } +} \ No newline at end of file diff --git a/src/macos/window.rs b/src/macos/window.rs index 8901260..39dae1f 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -17,11 +17,12 @@ use objc::{msg_send, runtime::Object, sel, sel_impl}; use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; use crate::{ - Event, Parent, WindowHandler, WindowOpenOptions, WindowScalePolicy, - WindowInfo + Event, keyboard::KeyEvent, Parent, WindowHandler, WindowOpenOptions, + WindowScalePolicy, WindowInfo }; use super::view::create_view; +use super::keyboard::KeyboardState; /// Name of the field used to store the `WindowState` pointer in the custom @@ -156,6 +157,7 @@ impl Window { let window_state_arc = Arc::new(WindowState { window, window_handler, + keyboard_state: KeyboardState::new(), }); let window_state_pointer = Arc::into_raw( @@ -177,6 +179,7 @@ impl Window { pub(super) struct WindowState { window: Window, window_handler: H, + keyboard_state: KeyboardState, } @@ -195,6 +198,13 @@ impl WindowState { pub(super) fn trigger_event(&mut self, event: Event){ self.window_handler.on_event(&mut self.window, event); } + + pub(super) fn process_native_key_event( + &mut self, + event: *mut Object + ) -> Option { + self.keyboard_state.process_native_event(event) + } } diff --git a/src/x11/keyboard.rs b/src/x11/keyboard.rs new file mode 100644 index 0000000..8b705cf --- /dev/null +++ b/src/x11/keyboard.rs @@ -0,0 +1,431 @@ +// Copyright 2020 The Druid Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Baseview modifications to druid code: +// - collect functions from various files +// - update imports, paths etc + +//! X11 keyboard handling + +use super::super::shared; +use crate::keyboard::code_to_location; +use crate::keyboard::{Code, KbKey, Modifiers}; +use x11rb::protocol::xproto::{Keycode, KeyPressEvent}; + +use crate::keyboard::KeyEvent; + + +/// Convert a hardware scan code to a key. +/// +/// Note: this is a hardcoded layout. We need to detect the user's +/// layout from the system and apply it. +fn code_to_key(code: Code, m: Modifiers) -> KbKey { + fn a(s: &str) -> KbKey { + KbKey::Character(s.into()) + } + fn s(mods: Modifiers, base: &str, shifted: &str) -> KbKey { + if mods.shift() { + KbKey::Character(shifted.into()) + } else { + KbKey::Character(base.into()) + } + } + fn n(mods: Modifiers, base: KbKey, num: &str) -> KbKey { + if mods.contains(Modifiers::NUM_LOCK) != mods.shift() { + KbKey::Character(num.into()) + } else { + base + } + } + match code { + Code::KeyA => s(m, "a", "A"), + Code::KeyB => s(m, "b", "B"), + Code::KeyC => s(m, "c", "C"), + Code::KeyD => s(m, "d", "D"), + Code::KeyE => s(m, "e", "E"), + Code::KeyF => s(m, "f", "F"), + Code::KeyG => s(m, "g", "G"), + Code::KeyH => s(m, "h", "H"), + Code::KeyI => s(m, "i", "I"), + Code::KeyJ => s(m, "j", "J"), + Code::KeyK => s(m, "k", "K"), + Code::KeyL => s(m, "l", "L"), + Code::KeyM => s(m, "m", "M"), + Code::KeyN => s(m, "n", "N"), + Code::KeyO => s(m, "o", "O"), + Code::KeyP => s(m, "p", "P"), + Code::KeyQ => s(m, "q", "Q"), + Code::KeyR => s(m, "r", "R"), + Code::KeyS => s(m, "s", "S"), + Code::KeyT => s(m, "t", "T"), + Code::KeyU => s(m, "u", "U"), + Code::KeyV => s(m, "v", "V"), + Code::KeyW => s(m, "w", "W"), + Code::KeyX => s(m, "x", "X"), + Code::KeyY => s(m, "y", "Y"), + Code::KeyZ => s(m, "z", "Z"), + + Code::Digit0 => s(m, "0", ")"), + Code::Digit1 => s(m, "1", "!"), + Code::Digit2 => s(m, "2", "@"), + Code::Digit3 => s(m, "3", "#"), + Code::Digit4 => s(m, "4", "$"), + Code::Digit5 => s(m, "5", "%"), + Code::Digit6 => s(m, "6", "^"), + Code::Digit7 => s(m, "7", "&"), + Code::Digit8 => s(m, "8", "*"), + Code::Digit9 => s(m, "9", "("), + + Code::Backquote => s(m, "`", "~"), + Code::Minus => s(m, "-", "_"), + Code::Equal => s(m, "=", "+"), + Code::BracketLeft => s(m, "[", "{"), + Code::BracketRight => s(m, "]", "}"), + Code::Backslash => s(m, "\\", "|"), + Code::Semicolon => s(m, ";", ":"), + Code::Quote => s(m, "'", "\""), + Code::Comma => s(m, ",", "<"), + Code::Period => s(m, ".", ">"), + Code::Slash => s(m, "/", "?"), + + Code::Space => a(" "), + + Code::Escape => KbKey::Escape, + Code::Backspace => KbKey::Backspace, + Code::Tab => KbKey::Tab, + Code::Enter => KbKey::Enter, + Code::ControlLeft => KbKey::Control, + Code::ShiftLeft => KbKey::Shift, + Code::ShiftRight => KbKey::Shift, + Code::NumpadMultiply => a("*"), + Code::AltLeft => KbKey::Alt, + Code::CapsLock => KbKey::CapsLock, + Code::F1 => KbKey::F1, + Code::F2 => KbKey::F2, + Code::F3 => KbKey::F3, + Code::F4 => KbKey::F4, + Code::F5 => KbKey::F5, + Code::F6 => KbKey::F6, + Code::F7 => KbKey::F7, + Code::F8 => KbKey::F8, + Code::F9 => KbKey::F9, + Code::F10 => KbKey::F10, + Code::NumLock => KbKey::NumLock, + Code::ScrollLock => KbKey::ScrollLock, + Code::Numpad0 => n(m, KbKey::Insert, "0"), + Code::Numpad1 => n(m, KbKey::End, "1"), + Code::Numpad2 => n(m, KbKey::ArrowDown, "2"), + Code::Numpad3 => n(m, KbKey::PageDown, "3"), + Code::Numpad4 => n(m, KbKey::ArrowLeft, "4"), + Code::Numpad5 => n(m, KbKey::Clear, "5"), + Code::Numpad6 => n(m, KbKey::ArrowRight, "6"), + Code::Numpad7 => n(m, KbKey::Home, "7"), + Code::Numpad8 => n(m, KbKey::ArrowUp, "8"), + Code::Numpad9 => n(m, KbKey::PageUp, "9"), + Code::NumpadSubtract => a("-"), + Code::NumpadAdd => a("+"), + Code::NumpadDecimal => n(m, KbKey::Delete, "."), + Code::IntlBackslash => s(m, "\\", "|"), + Code::F11 => KbKey::F11, + Code::F12 => KbKey::F12, + // This mapping is based on the picture in the w3c spec. + Code::IntlRo => a("\\"), + Code::Convert => KbKey::Convert, + Code::KanaMode => KbKey::KanaMode, + Code::NonConvert => KbKey::NonConvert, + Code::NumpadEnter => KbKey::Enter, + Code::ControlRight => KbKey::Control, + Code::NumpadDivide => a("/"), + Code::PrintScreen => KbKey::PrintScreen, + Code::AltRight => KbKey::Alt, + Code::Home => KbKey::Home, + Code::ArrowUp => KbKey::ArrowUp, + Code::PageUp => KbKey::PageUp, + Code::ArrowLeft => KbKey::ArrowLeft, + Code::ArrowRight => KbKey::ArrowRight, + Code::End => KbKey::End, + Code::ArrowDown => KbKey::ArrowDown, + Code::PageDown => KbKey::PageDown, + Code::Insert => KbKey::Insert, + Code::Delete => KbKey::Delete, + Code::AudioVolumeMute => KbKey::AudioVolumeMute, + Code::AudioVolumeDown => KbKey::AudioVolumeDown, + Code::AudioVolumeUp => KbKey::AudioVolumeUp, + Code::NumpadEqual => a("="), + Code::Pause => KbKey::Pause, + Code::NumpadComma => a(","), + Code::Lang1 => KbKey::HangulMode, + Code::Lang2 => KbKey::HanjaMode, + Code::IntlYen => a("¥"), + Code::MetaLeft => KbKey::Meta, + Code::MetaRight => KbKey::Meta, + Code::ContextMenu => KbKey::ContextMenu, + Code::BrowserStop => KbKey::BrowserStop, + Code::Again => KbKey::Again, + Code::Props => KbKey::Props, + Code::Undo => KbKey::Undo, + Code::Select => KbKey::Select, + Code::Copy => KbKey::Copy, + Code::Open => KbKey::Open, + Code::Paste => KbKey::Paste, + Code::Find => KbKey::Find, + Code::Cut => KbKey::Cut, + Code::Help => KbKey::Help, + Code::LaunchApp2 => KbKey::LaunchApplication2, + Code::WakeUp => KbKey::WakeUp, + Code::LaunchApp1 => KbKey::LaunchApplication1, + Code::LaunchMail => KbKey::LaunchMail, + Code::BrowserFavorites => KbKey::BrowserFavorites, + Code::BrowserBack => KbKey::BrowserBack, + Code::BrowserForward => KbKey::BrowserForward, + Code::Eject => KbKey::Eject, + Code::MediaTrackNext => KbKey::MediaTrackNext, + Code::MediaPlayPause => KbKey::MediaPlayPause, + Code::MediaTrackPrevious => KbKey::MediaTrackPrevious, + Code::MediaStop => KbKey::MediaStop, + Code::MediaSelect => KbKey::LaunchMediaPlayer, + Code::BrowserHome => KbKey::BrowserHome, + Code::BrowserRefresh => KbKey::BrowserRefresh, + Code::BrowserSearch => KbKey::BrowserSearch, + + _ => KbKey::Unidentified, + } +} + + +#[cfg(target_os = "linux")] +/// Map hardware keycode to code. +/// +/// In theory, the hardware keycode is device dependent, but in +/// practice it's probably pretty reliable. +/// +/// The logic is based on NativeKeyToDOMCodeName.h in Mozilla. +fn hardware_keycode_to_code(hw_keycode: u16) -> Code { + match hw_keycode { + 0x0009 => Code::Escape, + 0x000A => Code::Digit1, + 0x000B => Code::Digit2, + 0x000C => Code::Digit3, + 0x000D => Code::Digit4, + 0x000E => Code::Digit5, + 0x000F => Code::Digit6, + 0x0010 => Code::Digit7, + 0x0011 => Code::Digit8, + 0x0012 => Code::Digit9, + 0x0013 => Code::Digit0, + 0x0014 => Code::Minus, + 0x0015 => Code::Equal, + 0x0016 => Code::Backspace, + 0x0017 => Code::Tab, + 0x0018 => Code::KeyQ, + 0x0019 => Code::KeyW, + 0x001A => Code::KeyE, + 0x001B => Code::KeyR, + 0x001C => Code::KeyT, + 0x001D => Code::KeyY, + 0x001E => Code::KeyU, + 0x001F => Code::KeyI, + 0x0020 => Code::KeyO, + 0x0021 => Code::KeyP, + 0x0022 => Code::BracketLeft, + 0x0023 => Code::BracketRight, + 0x0024 => Code::Enter, + 0x0025 => Code::ControlLeft, + 0x0026 => Code::KeyA, + 0x0027 => Code::KeyS, + 0x0028 => Code::KeyD, + 0x0029 => Code::KeyF, + 0x002A => Code::KeyG, + 0x002B => Code::KeyH, + 0x002C => Code::KeyJ, + 0x002D => Code::KeyK, + 0x002E => Code::KeyL, + 0x002F => Code::Semicolon, + 0x0030 => Code::Quote, + 0x0031 => Code::Backquote, + 0x0032 => Code::ShiftLeft, + 0x0033 => Code::Backslash, + 0x0034 => Code::KeyZ, + 0x0035 => Code::KeyX, + 0x0036 => Code::KeyC, + 0x0037 => Code::KeyV, + 0x0038 => Code::KeyB, + 0x0039 => Code::KeyN, + 0x003A => Code::KeyM, + 0x003B => Code::Comma, + 0x003C => Code::Period, + 0x003D => Code::Slash, + 0x003E => Code::ShiftRight, + 0x003F => Code::NumpadMultiply, + 0x0040 => Code::AltLeft, + 0x0041 => Code::Space, + 0x0042 => Code::CapsLock, + 0x0043 => Code::F1, + 0x0044 => Code::F2, + 0x0045 => Code::F3, + 0x0046 => Code::F4, + 0x0047 => Code::F5, + 0x0048 => Code::F6, + 0x0049 => Code::F7, + 0x004A => Code::F8, + 0x004B => Code::F9, + 0x004C => Code::F10, + 0x004D => Code::NumLock, + 0x004E => Code::ScrollLock, + 0x004F => Code::Numpad7, + 0x0050 => Code::Numpad8, + 0x0051 => Code::Numpad9, + 0x0052 => Code::NumpadSubtract, + 0x0053 => Code::Numpad4, + 0x0054 => Code::Numpad5, + 0x0055 => Code::Numpad6, + 0x0056 => Code::NumpadAdd, + 0x0057 => Code::Numpad1, + 0x0058 => Code::Numpad2, + 0x0059 => Code::Numpad3, + 0x005A => Code::Numpad0, + 0x005B => Code::NumpadDecimal, + 0x005E => Code::IntlBackslash, + 0x005F => Code::F11, + 0x0060 => Code::F12, + 0x0061 => Code::IntlRo, + 0x0064 => Code::Convert, + 0x0065 => Code::KanaMode, + 0x0066 => Code::NonConvert, + 0x0068 => Code::NumpadEnter, + 0x0069 => Code::ControlRight, + 0x006A => Code::NumpadDivide, + 0x006B => Code::PrintScreen, + 0x006C => Code::AltRight, + 0x006E => Code::Home, + 0x006F => Code::ArrowUp, + 0x0070 => Code::PageUp, + 0x0071 => Code::ArrowLeft, + 0x0072 => Code::ArrowRight, + 0x0073 => Code::End, + 0x0074 => Code::ArrowDown, + 0x0075 => Code::PageDown, + 0x0076 => Code::Insert, + 0x0077 => Code::Delete, + 0x0079 => Code::AudioVolumeMute, + 0x007A => Code::AudioVolumeDown, + 0x007B => Code::AudioVolumeUp, + 0x007D => Code::NumpadEqual, + 0x007F => Code::Pause, + 0x0081 => Code::NumpadComma, + 0x0082 => Code::Lang1, + 0x0083 => Code::Lang2, + 0x0084 => Code::IntlYen, + 0x0085 => Code::MetaLeft, + 0x0086 => Code::MetaRight, + 0x0087 => Code::ContextMenu, + 0x0088 => Code::BrowserStop, + 0x0089 => Code::Again, + 0x008A => Code::Props, + 0x008B => Code::Undo, + 0x008C => Code::Select, + 0x008D => Code::Copy, + 0x008E => Code::Open, + 0x008F => Code::Paste, + 0x0090 => Code::Find, + 0x0091 => Code::Cut, + 0x0092 => Code::Help, + 0x0094 => Code::LaunchApp2, + 0x0097 => Code::WakeUp, + 0x0098 => Code::LaunchApp1, + // key to right of volume controls on T430s produces 0x9C + // but no documentation of what it should map to :/ + 0x00A3 => Code::LaunchMail, + 0x00A4 => Code::BrowserFavorites, + 0x00A6 => Code::BrowserBack, + 0x00A7 => Code::BrowserForward, + 0x00A9 => Code::Eject, + 0x00AB => Code::MediaTrackNext, + 0x00AC => Code::MediaPlayPause, + 0x00AD => Code::MediaTrackPrevious, + 0x00AE => Code::MediaStop, + 0x00B3 => Code::MediaSelect, + 0x00B4 => Code::BrowserHome, + 0x00B5 => Code::BrowserRefresh, + 0x00E1 => Code::BrowserSearch, + _ => Code::Unidentified, + } +} + +// Extracts the keyboard modifiers from, e.g., the `state` field of +// `xcb::xproto::ButtonPressEvent` +fn key_mods(mods: u16) -> Modifiers { + let mut ret = Modifiers::default(); + let mut key_masks = [ + (xproto::ModMask::Shift, Modifiers::SHIFT), + (xproto::ModMask::Control, Modifiers::CONTROL), + // X11's mod keys are configurable, but this seems + // like a reasonable default for US keyboards, at least, + // where the "windows" key seems to be MOD_MASK_4. + (xproto::ModMask::M1, Modifiers::ALT), + (xproto::ModMask::M2, Modifiers::NUM_LOCK), + (xproto::ModMask::M4, Modifiers::META), + (xproto::ModMask::Lock, Modifiers::CAPS_LOCK), + ]; + for (mask, modifiers) in &mut key_masks { + if mods & (*mask as u16) != 0 { + ret |= *modifiers; + } + } + ret +} + + +pub(super) fn convert_key_press_event( + key_press: &xcb::KeyPressEvent, +) -> KeyEvent { + let hw_keycode = key_press.detail(); + let code = hardware_keycode_to_code(hw_keycode); + let mods = key_mods(key_press.state()); + let key = code_to_key(code, mods); + let location = code_to_location(code); + let state = KeyState::Down; + + KeyEvent { + code, + key, + mods, + location, + state, + repeat: false, + is_composing: false, + } +} + + +pub(super) fn convert_key_release_event( + key_release: &xcb::KeyReleaseEvent +) -> KeyEvent { + let hw_keycode = key_release.detail(); + let code = hardware_keycode_to_code(hw_keycode); + let mods = key_mods(key_release.state()); + let key = code_to_key(code, mods); + let location = code_to_location(code); + let state = KeyState::Up; + + KeyEvent { + code, + key, + mods, + location, + state, + repeat: false, + is_composing: false, + } +} \ No newline at end of file diff --git a/src/x11/mod.rs b/src/x11/mod.rs index ecf4080..e87c5ac 100644 --- a/src/x11/mod.rs +++ b/src/x11/mod.rs @@ -4,4 +4,5 @@ use xcb_connection::XcbConnection; mod window; pub use window::*; -mod cursor; \ No newline at end of file +mod cursor; +mod keyboard; \ No newline at end of file diff --git a/src/x11/window.rs b/src/x11/window.rs index 84b31c4..a393668 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -11,8 +11,9 @@ use raw_window_handle::{ use super::XcbConnection; use crate::{ - Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, WindowEvent, - WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint, PhySize, + Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, + ScrollDelta, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, + WindowScalePolicy, PhyPoint, PhySize, }; pub struct Window { @@ -392,21 +393,27 @@ impl Window { //// xcb::KEY_PRESS => { let event = unsafe { xcb::cast_event::(&event) }; - let detail = event.detail(); + + let event = Event::Keyboard(KeyboardEvent::KeyPressed( + convert_key_press_event(&event) + )); handler.on_event( self, - Event::Keyboard(KeyboardEvent::KeyPressed(detail as u32)), + event ); } xcb::KEY_RELEASE => { let event = unsafe { xcb::cast_event::(&event) }; - let detail = event.detail(); + + let event = Event::Keyboard(KeyboardEvent::KeyReleased( + convert_key_release_event(&event) + )); handler.on_event( self, - Event::Keyboard(KeyboardEvent::KeyReleased(detail as u32)), + event ); } From 86b2aff624f2c9226159312cac33c4a436b36911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Fri, 13 Nov 2020 20:57:45 +0100 Subject: [PATCH 2/8] Attempt to fix linux build --- src/keyboard.rs | 2 +- src/x11/keyboard.rs | 23 ++++++++++------------- src/x11/window.rs | 2 ++ 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/keyboard.rs b/src/keyboard.rs index 796e180..1e4475d 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -225,7 +225,7 @@ impl IntoKey for &str { } -#[cfg(any(all(feature = "x11", target_os = "linux"), target_os = "macos"))] +#[cfg(any(target_os = "linux", target_os = "macos"))] /// Map key code to location. /// /// The logic for this is adapted from InitKeyEvent in TextInputHandler (in the Mozilla diff --git a/src/x11/keyboard.rs b/src/x11/keyboard.rs index 8b705cf..cf55b28 100644 --- a/src/x11/keyboard.rs +++ b/src/x11/keyboard.rs @@ -18,12 +18,9 @@ //! X11 keyboard handling -use super::super::shared; -use crate::keyboard::code_to_location; -use crate::keyboard::{Code, KbKey, Modifiers}; -use x11rb::protocol::xproto::{Keycode, KeyPressEvent}; +use xcb::xproto; -use crate::keyboard::KeyEvent; +use crate::keyboard::*; /// Convert a hardware scan code to a key. @@ -368,15 +365,15 @@ fn hardware_keycode_to_code(hw_keycode: u16) -> Code { fn key_mods(mods: u16) -> Modifiers { let mut ret = Modifiers::default(); let mut key_masks = [ - (xproto::ModMask::Shift, Modifiers::SHIFT), - (xproto::ModMask::Control, Modifiers::CONTROL), + (xproto::MOD_MASK_SHIFT, Modifiers::SHIFT), + (xproto::MOD_MASK_CONTROL, Modifiers::CONTROL), // X11's mod keys are configurable, but this seems // like a reasonable default for US keyboards, at least, // where the "windows" key seems to be MOD_MASK_4. - (xproto::ModMask::M1, Modifiers::ALT), - (xproto::ModMask::M2, Modifiers::NUM_LOCK), - (xproto::ModMask::M4, Modifiers::META), - (xproto::ModMask::Lock, Modifiers::CAPS_LOCK), + (xproto::MOD_MASK_1, Modifiers::ALT), + (xproto::MOD_MASK_2, Modifiers::NUM_LOCK), + (xproto::MOD_MASK_4, Modifiers::META), + (xproto::MOD_MASK_LOCK, Modifiers::CAPS_LOCK), ]; for (mask, modifiers) in &mut key_masks { if mods & (*mask as u16) != 0 { @@ -391,7 +388,7 @@ pub(super) fn convert_key_press_event( key_press: &xcb::KeyPressEvent, ) -> KeyEvent { let hw_keycode = key_press.detail(); - let code = hardware_keycode_to_code(hw_keycode); + let code = hardware_keycode_to_code(hw_keycode.into()); let mods = key_mods(key_press.state()); let key = code_to_key(code, mods); let location = code_to_location(code); @@ -413,7 +410,7 @@ pub(super) fn convert_key_release_event( key_release: &xcb::KeyReleaseEvent ) -> KeyEvent { let hw_keycode = key_release.detail(); - let code = hardware_keycode_to_code(hw_keycode); + let code = hardware_keycode_to_code(hw_keycode.into()); let mods = key_mods(key_release.state()); let key = code_to_key(code, mods); let location = code_to_location(code); diff --git a/src/x11/window.rs b/src/x11/window.rs index a393668..2448117 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -16,6 +16,8 @@ use crate::{ WindowScalePolicy, PhyPoint, PhySize, }; +use super::keyboard::{convert_key_press_event, convert_key_release_event}; + pub struct Window { xcb_connection: XcbConnection, window_id: u32, From 546b0d6eaeaed4ec64ebb130c20043eead977017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Fri, 13 Nov 2020 21:37:13 +0100 Subject: [PATCH 3/8] Use keyboard-types types directly instead of druid wrappers --- src/event.rs | 6 +- src/keyboard.rs | 209 +-------------------------------------- src/lib.rs | 1 - src/macos/keyboard.rs | 102 ++++++++++---------- src/macos/window.rs | 5 +- src/x11/keyboard.rs | 220 +++++++++++++++++++++--------------------- 6 files changed, 170 insertions(+), 373 deletions(-) diff --git a/src/event.rs b/src/event.rs index 23face7..443f939 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,9 +1,9 @@ -use crate::{WindowInfo, Point, KeyEvent}; +use crate::{WindowInfo, Point}; #[derive(Debug, Clone, PartialEq)] pub enum KeyboardEvent { - KeyPressed(KeyEvent), - KeyReleased(KeyEvent), + KeyPressed(keyboard_types::KeyboardEvent), + KeyReleased(keyboard_types::KeyboardEvent), } diff --git a/src/keyboard.rs b/src/keyboard.rs index 1e4475d..38b2e9a 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -13,216 +13,11 @@ // limitations under the License. // Baseview modifications to druid code: -// - move code_to_location function to this file +// - only keep code_to_location function //! Keyboard types. -// This is a reasonable lint, but we keep signatures in sync with the -// bitflags implementation of the inner Modifiers type. -#![allow(clippy::trivially_copy_pass_by_ref)] - -use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; - -pub use keyboard_types::{Code, KeyState, Location}; - -/// The meaning (mapped value) of a keypress. -pub type KbKey = keyboard_types::Key; - -/// Information about a keyboard event. -/// -/// Note that this type is similar to [`KeyboardEvent`] in keyboard-types, -/// but has a few small differences for convenience. It is missing the `state` -/// field because that is already implicit in the event. -/// -/// [`KeyboardEvent`]: keyboard_types::KeyboardEvent -#[non_exhaustive] -#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] -pub struct KeyEvent { - /// Whether the key is pressed or released. - pub state: KeyState, - /// Logical key value. - pub key: KbKey, - /// Physical key position. - pub code: Code, - /// Location for keys with multiple instances on common keyboards. - pub location: Location, - /// Flags for pressed modifier keys. - pub mods: Modifiers, - /// True if the key is currently auto-repeated. - pub repeat: bool, - /// Events with this flag should be ignored in a text editor - /// and instead composition events should be used. - pub is_composing: bool, -} - -/// The modifiers. -/// -/// This type is a thin wrappers around [`keyboard_types::Modifiers`], -/// mostly for the convenience methods. If those get upstreamed, it -/// will simply become that type. -/// -/// [`keyboard_types::Modifiers`]: keyboard_types::Modifiers -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub struct Modifiers(keyboard_types::Modifiers); - -/// A convenience trait for creating Key objects. -/// -/// This trait is implemented by [`KbKey`] itself and also strings, which are -/// converted into the `Character` variant. It is defined this way and not -/// using the standard `Into` mechanism because `KbKey` is a type in an external -/// crate. -/// -/// [`KbKey`]: KbKey -pub trait IntoKey { - fn into_key(self) -> KbKey; -} - -impl KeyEvent { - #[doc(hidden)] - /// Create a key event for testing purposes. - pub fn for_test(mods: impl Into, key: impl IntoKey) -> KeyEvent { - let mods = mods.into(); - let key = key.into_key(); - KeyEvent { - key, - code: Code::Unidentified, - location: Location::Standard, - state: KeyState::Down, - mods, - is_composing: false, - repeat: false, - } - } -} - -impl Modifiers { - pub const ALT: Modifiers = Modifiers(keyboard_types::Modifiers::ALT); - pub const ALT_GRAPH: Modifiers = Modifiers(keyboard_types::Modifiers::ALT_GRAPH); - pub const CAPS_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::CAPS_LOCK); - pub const CONTROL: Modifiers = Modifiers(keyboard_types::Modifiers::CONTROL); - pub const FN: Modifiers = Modifiers(keyboard_types::Modifiers::FN); - pub const FN_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::FN_LOCK); - pub const META: Modifiers = Modifiers(keyboard_types::Modifiers::META); - pub const NUM_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::NUM_LOCK); - pub const SCROLL_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::SCROLL_LOCK); - pub const SHIFT: Modifiers = Modifiers(keyboard_types::Modifiers::SHIFT); - pub const SYMBOL: Modifiers = Modifiers(keyboard_types::Modifiers::SYMBOL); - pub const SYMBOL_LOCK: Modifiers = Modifiers(keyboard_types::Modifiers::SYMBOL_LOCK); - pub const HYPER: Modifiers = Modifiers(keyboard_types::Modifiers::HYPER); - pub const SUPER: Modifiers = Modifiers(keyboard_types::Modifiers::SUPER); - - /// Get the inner value. - /// - /// Note that this function might go away if our changes are upstreamed. - pub fn raw(&self) -> keyboard_types::Modifiers { - self.0 - } - - /// Determine whether Shift is set. - pub fn shift(&self) -> bool { - self.contains(Modifiers::SHIFT) - } - - /// Determine whether Ctrl is set. - pub fn ctrl(&self) -> bool { - self.contains(Modifiers::CONTROL) - } - - /// Determine whether Alt is set. - pub fn alt(&self) -> bool { - self.contains(Modifiers::ALT) - } - - /// Determine whether Meta is set. - pub fn meta(&self) -> bool { - self.contains(Modifiers::META) - } - - /// Returns an empty set of modifiers. - pub fn empty() -> Modifiers { - Default::default() - } - - /// Returns `true` if no modifiers are set. - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Returns `true` if all the modifiers in `other` are set. - pub fn contains(&self, other: Modifiers) -> bool { - self.0.contains(other.0) - } - - /// Inserts or removes the specified modifiers depending on the passed value. - pub fn set(&mut self, other: Modifiers, value: bool) { - self.0.set(other.0, value) - } -} - -impl BitAnd for Modifiers { - type Output = Self; - - fn bitand(self, rhs: Self) -> Self { - Modifiers(self.0 & rhs.0) - } -} - -impl BitAndAssign for Modifiers { - // rhs is the "right-hand side" of the expression `a &= b` - fn bitand_assign(&mut self, rhs: Self) { - *self = Modifiers(self.0 & rhs.0) - } -} - -impl BitOr for Modifiers { - type Output = Self; - - fn bitor(self, rhs: Self) -> Self { - Modifiers(self.0 | rhs.0) - } -} - -impl BitOrAssign for Modifiers { - // rhs is the "right-hand side" of the expression `a &= b` - fn bitor_assign(&mut self, rhs: Self) { - *self = Modifiers(self.0 | rhs.0) - } -} - -impl BitXor for Modifiers { - type Output = Self; - - fn bitxor(self, rhs: Self) -> Self { - Modifiers(self.0 ^ rhs.0) - } -} - -impl BitXorAssign for Modifiers { - // rhs is the "right-hand side" of the expression `a &= b` - fn bitxor_assign(&mut self, rhs: Self) { - *self = Modifiers(self.0 ^ rhs.0) - } -} - -impl Not for Modifiers { - type Output = Self; - - fn not(self) -> Self { - Modifiers(!self.0) - } -} - -impl IntoKey for KbKey { - fn into_key(self) -> KbKey { - self - } -} - -impl IntoKey for &str { - fn into_key(self) -> KbKey { - KbKey::Character(self.into()) - } -} +use keyboard_types::{Code, Location}; #[cfg(any(target_os = "linux", target_os = "macos"))] diff --git a/src/lib.rs b/src/lib.rs index ef2f728..67ac9dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,6 @@ mod mouse_cursor; mod window_info; mod window_open_options; pub use event::*; -pub use keyboard::KeyEvent; pub use mouse_cursor::MouseCursor; pub use window_info::*; pub use window_open_options::*; diff --git a/src/macos/keyboard.rs b/src/macos/keyboard.rs index 40cc511..4f1dd1b 100644 --- a/src/macos/keyboard.rs +++ b/src/macos/keyboard.rs @@ -21,10 +21,10 @@ use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventType}; use cocoa::foundation::NSString; use cocoa::base::id; -use keyboard_types::{Code, KeyState}; +use keyboard_types::{Code, KeyState, Key, KeyboardEvent, Modifiers}; use objc::{msg_send, sel, sel_impl}; -use crate::keyboard::{code_to_location, KbKey, KeyEvent, Modifiers}; +use crate::keyboard::code_to_location; pub(crate) fn from_nsstring(s: id) -> String { @@ -195,52 +195,52 @@ fn key_code_to_code(key_code: u16) -> Code { /// /// The logic for this function is derived from KEY_MAP_COCOA bindings in /// NativeKeyToDOMKeyName.h. -fn code_to_key(code: Code) -> Option { +fn code_to_key(code: Code) -> Option { Some(match code { - Code::Escape => KbKey::Escape, - Code::ShiftLeft | Code::ShiftRight => KbKey::Shift, - Code::AltLeft | Code::AltRight => KbKey::Alt, - Code::MetaLeft | Code::MetaRight => KbKey::Meta, - Code::ControlLeft | Code::ControlRight => KbKey::Control, - Code::CapsLock => KbKey::CapsLock, + Code::Escape => Key::Escape, + Code::ShiftLeft | Code::ShiftRight => Key::Shift, + Code::AltLeft | Code::AltRight => Key::Alt, + Code::MetaLeft | Code::MetaRight => Key::Meta, + Code::ControlLeft | Code::ControlRight => Key::Control, + Code::CapsLock => Key::CapsLock, // kVK_ANSI_KeypadClear - Code::NumLock => KbKey::Clear, - Code::Fn => KbKey::Fn, - Code::F1 => KbKey::F1, - Code::F2 => KbKey::F2, - Code::F3 => KbKey::F3, - Code::F4 => KbKey::F4, - Code::F5 => KbKey::F5, - Code::F6 => KbKey::F6, - Code::F7 => KbKey::F7, - Code::F8 => KbKey::F8, - Code::F9 => KbKey::F9, - Code::F10 => KbKey::F10, - Code::F11 => KbKey::F11, - Code::F12 => KbKey::F12, - Code::Pause => KbKey::Pause, - Code::ScrollLock => KbKey::ScrollLock, - Code::PrintScreen => KbKey::PrintScreen, - Code::Insert => KbKey::Insert, - Code::Delete => KbKey::Delete, - Code::Tab => KbKey::Tab, - Code::Backspace => KbKey::Backspace, - Code::ContextMenu => KbKey::ContextMenu, + Code::NumLock => Key::Clear, + Code::Fn => Key::Fn, + Code::F1 => Key::F1, + Code::F2 => Key::F2, + Code::F3 => Key::F3, + Code::F4 => Key::F4, + Code::F5 => Key::F5, + Code::F6 => Key::F6, + Code::F7 => Key::F7, + Code::F8 => Key::F8, + Code::F9 => Key::F9, + Code::F10 => Key::F10, + Code::F11 => Key::F11, + Code::F12 => Key::F12, + Code::Pause => Key::Pause, + Code::ScrollLock => Key::ScrollLock, + Code::PrintScreen => Key::PrintScreen, + Code::Insert => Key::Insert, + Code::Delete => Key::Delete, + Code::Tab => Key::Tab, + Code::Backspace => Key::Backspace, + Code::ContextMenu => Key::ContextMenu, // kVK_JIS_Kana - Code::Lang1 => KbKey::KanjiMode, + Code::Lang1 => Key::KanjiMode, // kVK_JIS_Eisu - Code::Lang2 => KbKey::Eisu, - Code::Home => KbKey::Home, - Code::End => KbKey::End, - Code::PageUp => KbKey::PageUp, - Code::PageDown => KbKey::PageDown, - Code::ArrowLeft => KbKey::ArrowLeft, - Code::ArrowRight => KbKey::ArrowRight, - Code::ArrowUp => KbKey::ArrowUp, - Code::ArrowDown => KbKey::ArrowDown, - Code::Enter => KbKey::Enter, - Code::NumpadEnter => KbKey::Enter, - Code::Help => KbKey::Help, + Code::Lang2 => Key::Eisu, + Code::Home => Key::Home, + Code::End => Key::End, + Code::PageUp => Key::PageUp, + Code::PageDown => Key::PageDown, + Code::ArrowLeft => Key::ArrowLeft, + Code::ArrowRight => Key::ArrowRight, + Code::ArrowUp => Key::ArrowUp, + Code::ArrowDown => Key::ArrowDown, + Code::Enter => Key::Enter, + Code::NumpadEnter => Key::Enter, + Code::Help => Key::Help, _ => return None, }) } @@ -277,14 +277,14 @@ impl KeyboardState { KeyboardState { last_mods } } - pub(crate) fn process_native_event(&mut self, event: id) -> Option { + pub(crate) fn process_native_event(&mut self, event: id) -> Option { unsafe { let event_type = event.eventType(); let key_code = event.keyCode(); let code = key_code_to_code(key_code); let location = code_to_location(code); let raw_mods = event.modifierFlags(); - let mods = make_modifiers(raw_mods); + let modifiers = make_modifiers(raw_mods); let state = match event_type { NSEventType::NSKeyDown => KeyState::Down, NSEventType::NSKeyUp => KeyState::Up, @@ -318,22 +318,22 @@ impl KeyboardState { } else { let characters = from_nsstring(event.characters()); if is_valid_key(&characters) { - KbKey::Character(characters) + Key::Character(characters) } else { let chars_ignoring = from_nsstring(event.charactersIgnoringModifiers()); if is_valid_key(&chars_ignoring) { - KbKey::Character(chars_ignoring) + Key::Character(chars_ignoring) } else { // There may be more heroic things we can do here. - KbKey::Unidentified + Key::Unidentified } } }; - let event = KeyEvent { + let event = KeyboardEvent { code, key, location, - mods, + modifiers, state, is_composing, repeat, diff --git a/src/macos/window.rs b/src/macos/window.rs index 39dae1f..5e228f5 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -11,13 +11,14 @@ use cocoa::appkit::{ }; use cocoa::base::{id, nil, NO}; use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString}; +use keyboard_types::KeyboardEvent; use objc::{msg_send, runtime::Object, sel, sel_impl}; use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; use crate::{ - Event, keyboard::KeyEvent, Parent, WindowHandler, WindowOpenOptions, + Event, Parent, WindowHandler, WindowOpenOptions, WindowScalePolicy, WindowInfo }; @@ -202,7 +203,7 @@ impl WindowState { pub(super) fn process_native_key_event( &mut self, event: *mut Object - ) -> Option { + ) -> Option { self.keyboard_state.process_native_event(event) } } diff --git a/src/x11/keyboard.rs b/src/x11/keyboard.rs index cf55b28..8568a01 100644 --- a/src/x11/keyboard.rs +++ b/src/x11/keyboard.rs @@ -20,27 +20,29 @@ use xcb::xproto; -use crate::keyboard::*; +use keyboard_types::*; + +use crate::keyboard::code_to_location; /// Convert a hardware scan code to a key. /// /// Note: this is a hardcoded layout. We need to detect the user's /// layout from the system and apply it. -fn code_to_key(code: Code, m: Modifiers) -> KbKey { - fn a(s: &str) -> KbKey { - KbKey::Character(s.into()) +fn code_to_key(code: Code, m: Modifiers) -> Key { + fn a(s: &str) -> Key { + Key::Character(s.into()) } - fn s(mods: Modifiers, base: &str, shifted: &str) -> KbKey { - if mods.shift() { - KbKey::Character(shifted.into()) + fn s(mods: Modifiers, base: &str, shifted: &str) -> Key { + if mods.contains(Modifiers::SHIFT) { + Key::Character(shifted.into()) } else { - KbKey::Character(base.into()) + Key::Character(base.into()) } } - fn n(mods: Modifiers, base: KbKey, num: &str) -> KbKey { - if mods.contains(Modifiers::NUM_LOCK) != mods.shift() { - KbKey::Character(num.into()) + fn n(mods: Modifiers, base: Key, num: &str) -> Key { + if mods.contains(Modifiers::NUM_LOCK) != mods.contains(Modifiers::SHIFT) { + Key::Character(num.into()) } else { base } @@ -98,105 +100,105 @@ fn code_to_key(code: Code, m: Modifiers) -> KbKey { Code::Space => a(" "), - Code::Escape => KbKey::Escape, - Code::Backspace => KbKey::Backspace, - Code::Tab => KbKey::Tab, - Code::Enter => KbKey::Enter, - Code::ControlLeft => KbKey::Control, - Code::ShiftLeft => KbKey::Shift, - Code::ShiftRight => KbKey::Shift, + Code::Escape => Key::Escape, + Code::Backspace => Key::Backspace, + Code::Tab => Key::Tab, + Code::Enter => Key::Enter, + Code::ControlLeft => Key::Control, + Code::ShiftLeft => Key::Shift, + Code::ShiftRight => Key::Shift, Code::NumpadMultiply => a("*"), - Code::AltLeft => KbKey::Alt, - Code::CapsLock => KbKey::CapsLock, - Code::F1 => KbKey::F1, - Code::F2 => KbKey::F2, - Code::F3 => KbKey::F3, - Code::F4 => KbKey::F4, - Code::F5 => KbKey::F5, - Code::F6 => KbKey::F6, - Code::F7 => KbKey::F7, - Code::F8 => KbKey::F8, - Code::F9 => KbKey::F9, - Code::F10 => KbKey::F10, - Code::NumLock => KbKey::NumLock, - Code::ScrollLock => KbKey::ScrollLock, - Code::Numpad0 => n(m, KbKey::Insert, "0"), - Code::Numpad1 => n(m, KbKey::End, "1"), - Code::Numpad2 => n(m, KbKey::ArrowDown, "2"), - Code::Numpad3 => n(m, KbKey::PageDown, "3"), - Code::Numpad4 => n(m, KbKey::ArrowLeft, "4"), - Code::Numpad5 => n(m, KbKey::Clear, "5"), - Code::Numpad6 => n(m, KbKey::ArrowRight, "6"), - Code::Numpad7 => n(m, KbKey::Home, "7"), - Code::Numpad8 => n(m, KbKey::ArrowUp, "8"), - Code::Numpad9 => n(m, KbKey::PageUp, "9"), + Code::AltLeft => Key::Alt, + Code::CapsLock => Key::CapsLock, + Code::F1 => Key::F1, + Code::F2 => Key::F2, + Code::F3 => Key::F3, + Code::F4 => Key::F4, + Code::F5 => Key::F5, + Code::F6 => Key::F6, + Code::F7 => Key::F7, + Code::F8 => Key::F8, + Code::F9 => Key::F9, + Code::F10 => Key::F10, + Code::NumLock => Key::NumLock, + Code::ScrollLock => Key::ScrollLock, + Code::Numpad0 => n(m, Key::Insert, "0"), + Code::Numpad1 => n(m, Key::End, "1"), + Code::Numpad2 => n(m, Key::ArrowDown, "2"), + Code::Numpad3 => n(m, Key::PageDown, "3"), + Code::Numpad4 => n(m, Key::ArrowLeft, "4"), + Code::Numpad5 => n(m, Key::Clear, "5"), + Code::Numpad6 => n(m, Key::ArrowRight, "6"), + Code::Numpad7 => n(m, Key::Home, "7"), + Code::Numpad8 => n(m, Key::ArrowUp, "8"), + Code::Numpad9 => n(m, Key::PageUp, "9"), Code::NumpadSubtract => a("-"), Code::NumpadAdd => a("+"), - Code::NumpadDecimal => n(m, KbKey::Delete, "."), + Code::NumpadDecimal => n(m, Key::Delete, "."), Code::IntlBackslash => s(m, "\\", "|"), - Code::F11 => KbKey::F11, - Code::F12 => KbKey::F12, + Code::F11 => Key::F11, + Code::F12 => Key::F12, // This mapping is based on the picture in the w3c spec. Code::IntlRo => a("\\"), - Code::Convert => KbKey::Convert, - Code::KanaMode => KbKey::KanaMode, - Code::NonConvert => KbKey::NonConvert, - Code::NumpadEnter => KbKey::Enter, - Code::ControlRight => KbKey::Control, + Code::Convert => Key::Convert, + Code::KanaMode => Key::KanaMode, + Code::NonConvert => Key::NonConvert, + Code::NumpadEnter => Key::Enter, + Code::ControlRight => Key::Control, Code::NumpadDivide => a("/"), - Code::PrintScreen => KbKey::PrintScreen, - Code::AltRight => KbKey::Alt, - Code::Home => KbKey::Home, - Code::ArrowUp => KbKey::ArrowUp, - Code::PageUp => KbKey::PageUp, - Code::ArrowLeft => KbKey::ArrowLeft, - Code::ArrowRight => KbKey::ArrowRight, - Code::End => KbKey::End, - Code::ArrowDown => KbKey::ArrowDown, - Code::PageDown => KbKey::PageDown, - Code::Insert => KbKey::Insert, - Code::Delete => KbKey::Delete, - Code::AudioVolumeMute => KbKey::AudioVolumeMute, - Code::AudioVolumeDown => KbKey::AudioVolumeDown, - Code::AudioVolumeUp => KbKey::AudioVolumeUp, + Code::PrintScreen => Key::PrintScreen, + Code::AltRight => Key::Alt, + Code::Home => Key::Home, + Code::ArrowUp => Key::ArrowUp, + Code::PageUp => Key::PageUp, + Code::ArrowLeft => Key::ArrowLeft, + Code::ArrowRight => Key::ArrowRight, + Code::End => Key::End, + Code::ArrowDown => Key::ArrowDown, + Code::PageDown => Key::PageDown, + Code::Insert => Key::Insert, + Code::Delete => Key::Delete, + Code::AudioVolumeMute => Key::AudioVolumeMute, + Code::AudioVolumeDown => Key::AudioVolumeDown, + Code::AudioVolumeUp => Key::AudioVolumeUp, Code::NumpadEqual => a("="), - Code::Pause => KbKey::Pause, + Code::Pause => Key::Pause, Code::NumpadComma => a(","), - Code::Lang1 => KbKey::HangulMode, - Code::Lang2 => KbKey::HanjaMode, + Code::Lang1 => Key::HangulMode, + Code::Lang2 => Key::HanjaMode, Code::IntlYen => a("¥"), - Code::MetaLeft => KbKey::Meta, - Code::MetaRight => KbKey::Meta, - Code::ContextMenu => KbKey::ContextMenu, - Code::BrowserStop => KbKey::BrowserStop, - Code::Again => KbKey::Again, - Code::Props => KbKey::Props, - Code::Undo => KbKey::Undo, - Code::Select => KbKey::Select, - Code::Copy => KbKey::Copy, - Code::Open => KbKey::Open, - Code::Paste => KbKey::Paste, - Code::Find => KbKey::Find, - Code::Cut => KbKey::Cut, - Code::Help => KbKey::Help, - Code::LaunchApp2 => KbKey::LaunchApplication2, - Code::WakeUp => KbKey::WakeUp, - Code::LaunchApp1 => KbKey::LaunchApplication1, - Code::LaunchMail => KbKey::LaunchMail, - Code::BrowserFavorites => KbKey::BrowserFavorites, - Code::BrowserBack => KbKey::BrowserBack, - Code::BrowserForward => KbKey::BrowserForward, - Code::Eject => KbKey::Eject, - Code::MediaTrackNext => KbKey::MediaTrackNext, - Code::MediaPlayPause => KbKey::MediaPlayPause, - Code::MediaTrackPrevious => KbKey::MediaTrackPrevious, - Code::MediaStop => KbKey::MediaStop, - Code::MediaSelect => KbKey::LaunchMediaPlayer, - Code::BrowserHome => KbKey::BrowserHome, - Code::BrowserRefresh => KbKey::BrowserRefresh, - Code::BrowserSearch => KbKey::BrowserSearch, + Code::MetaLeft => Key::Meta, + Code::MetaRight => Key::Meta, + Code::ContextMenu => Key::ContextMenu, + Code::BrowserStop => Key::BrowserStop, + Code::Again => Key::Again, + Code::Props => Key::Props, + Code::Undo => Key::Undo, + Code::Select => Key::Select, + Code::Copy => Key::Copy, + Code::Open => Key::Open, + Code::Paste => Key::Paste, + Code::Find => Key::Find, + Code::Cut => Key::Cut, + Code::Help => Key::Help, + Code::LaunchApp2 => Key::LaunchApplication2, + Code::WakeUp => Key::WakeUp, + Code::LaunchApp1 => Key::LaunchApplication1, + Code::LaunchMail => Key::LaunchMail, + Code::BrowserFavorites => Key::BrowserFavorites, + Code::BrowserBack => Key::BrowserBack, + Code::BrowserForward => Key::BrowserForward, + Code::Eject => Key::Eject, + Code::MediaTrackNext => Key::MediaTrackNext, + Code::MediaPlayPause => Key::MediaPlayPause, + Code::MediaTrackPrevious => Key::MediaTrackPrevious, + Code::MediaStop => Key::MediaStop, + Code::MediaSelect => Key::LaunchMediaPlayer, + Code::BrowserHome => Key::BrowserHome, + Code::BrowserRefresh => Key::BrowserRefresh, + Code::BrowserSearch => Key::BrowserSearch, - _ => KbKey::Unidentified, + _ => Key::Unidentified, } } @@ -386,18 +388,18 @@ fn key_mods(mods: u16) -> Modifiers { pub(super) fn convert_key_press_event( key_press: &xcb::KeyPressEvent, -) -> KeyEvent { +) -> KeyboardEvent { let hw_keycode = key_press.detail(); let code = hardware_keycode_to_code(hw_keycode.into()); - let mods = key_mods(key_press.state()); - let key = code_to_key(code, mods); + let modifiers = key_mods(key_press.state()); + let key = code_to_key(code, modifiers); let location = code_to_location(code); let state = KeyState::Down; - KeyEvent { + KeyboardEvent { code, key, - mods, + modifiers, location, state, repeat: false, @@ -408,18 +410,18 @@ pub(super) fn convert_key_press_event( pub(super) fn convert_key_release_event( key_release: &xcb::KeyReleaseEvent -) -> KeyEvent { +) -> KeyboardEvent { let hw_keycode = key_release.detail(); let code = hardware_keycode_to_code(hw_keycode.into()); - let mods = key_mods(key_release.state()); - let key = code_to_key(code, mods); + let modifiers = key_mods(key_release.state()); + let key = code_to_key(code, modifiers); let location = code_to_location(code); let state = KeyState::Up; - KeyEvent { + KeyboardEvent { code, key, - mods, + modifiers, location, state, repeat: false, From 0a6a0bdffdb4d126908da54dd1f7ce1e5ebf0e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Fri, 13 Nov 2020 23:17:46 +0100 Subject: [PATCH 4/8] Use keyboard_types::KeyboardEvent directly instead of wrapping it --- src/event.rs | 8 ++------ src/macos/view.rs | 10 +++------- src/win/window.rs | 2 +- src/x11/window.rs | 16 ++++------------ 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/src/event.rs b/src/event.rs index 443f939..64dd4ed 100644 --- a/src/event.rs +++ b/src/event.rs @@ -1,10 +1,6 @@ -use crate::{WindowInfo, Point}; +use keyboard_types::KeyboardEvent; -#[derive(Debug, Clone, PartialEq)] -pub enum KeyboardEvent { - KeyPressed(keyboard_types::KeyboardEvent), - KeyReleased(keyboard_types::KeyboardEvent), -} +use crate::{WindowInfo, Point}; #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/src/macos/view.rs b/src/macos/view.rs index 0de1dc2..8d958c3 100644 --- a/src/macos/view.rs +++ b/src/macos/view.rs @@ -15,7 +15,7 @@ use objc::{ use uuid::Uuid; use crate::{ - Event, KeyboardEvent, MouseButton, MouseEvent, Point, WindowHandler, + Event, MouseButton, MouseEvent, Point, WindowHandler, WindowOpenOptions }; use crate::MouseEvent::{ButtonPressed, ButtonReleased}; @@ -351,9 +351,7 @@ extern "C" fn key_down(this: &Object, _: Sel, event: id){ }; if let Some(key_event) = state.process_native_key_event(event){ - let event = Event::Keyboard(KeyboardEvent::KeyPressed(key_event)); - - state.trigger_event(event); + state.trigger_event(Event::Keyboard(key_event)); } } @@ -364,8 +362,6 @@ extern "C" fn key_up(this: &Object, _: Sel, event: id){ }; if let Some(key_event) = state.process_native_key_event(event){ - let event = Event::Keyboard(KeyboardEvent::KeyReleased(key_event)); - - state.trigger_event(event); + state.trigger_event(Event::Keyboard(key_event)); } } \ No newline at end of file diff --git a/src/win/window.rs b/src/win/window.rs index 2e017a2..ae952be 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -23,7 +23,7 @@ use raw_window_handle::{ }; use crate::{ - Event, KeyboardEvent, MouseButton, MouseEvent, Parent::WithParent, ScrollDelta, WindowEvent, + Event, MouseButton, MouseEvent, Parent::WithParent, ScrollDelta, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, Size, Point, PhySize, PhyPoint, }; diff --git a/src/x11/window.rs b/src/x11/window.rs index 2448117..9d3a5e5 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -11,8 +11,8 @@ use raw_window_handle::{ use super::XcbConnection; use crate::{ - Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, - ScrollDelta, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, + Event, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, + WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint, PhySize, }; @@ -396,26 +396,18 @@ impl Window { xcb::KEY_PRESS => { let event = unsafe { xcb::cast_event::(&event) }; - let event = Event::Keyboard(KeyboardEvent::KeyPressed( - convert_key_press_event(&event) - )); - handler.on_event( self, - event + Event::Keyboard(convert_key_press_event(&event)) ); } xcb::KEY_RELEASE => { let event = unsafe { xcb::cast_event::(&event) }; - let event = Event::Keyboard(KeyboardEvent::KeyReleased( - convert_key_release_event(&event) - )); - handler.on_event( self, - event + Event::Keyboard(convert_key_release_event(&event)) ); } From dd3f85b34730b1fe4c23eed7b563a10826512a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Fri, 13 Nov 2020 23:33:34 +0100 Subject: [PATCH 5/8] Cargo.toml: set default-features = false for keyboard-types --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8f4c5d8..1f3ba27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0" [dependencies] log = "0.4.11" -keyboard-types = "0.5.0" +keyboard-types = { version = "0.5.0", default-features = false } raw-window-handle = "0.3.3" [target.'cfg(target_os="linux")'.dependencies] From f8c994a23c85c0ec87aabe681e25e944c8a27277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Sat, 14 Nov 2020 16:44:55 +0100 Subject: [PATCH 6/8] README: add Linux installation prerequisites --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 33951e0..e879929 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,16 @@ Below is a proposed list of milestones (roughly in-order) and their status. Subj | Parent window support | | | | | *(Converge on a common API for all platforms?)* | | | | +## Prerequisites + +### Linux + +Install dependencies, e.g., + +```sh +sudo apt-get install libx11-dev libxcursor-dev libxcb-dri2-0-dev libxcb-icccm4-dev libx11-xcb-dev +``` + ## License Licensed under either of Apache License, Version @@ -27,4 +37,4 @@ Licensed under either of Apache License, Version Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Baseview by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +dual licensed as above, without any additional terms or conditions. \ No newline at end of file From d36f289663131b39c03b184103aafb2fb08b2db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Sat, 14 Nov 2020 16:51:13 +0100 Subject: [PATCH 7/8] README: mark events and parenting as solved for macOS and Linux --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e879929..5a19c4d 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ Below is a proposed list of milestones (roughly in-order) and their status. Subj | Cross-platform API for window spawning | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | Window uses an OpenGL surface | :heavy_check_mark: | | :heavy_check_mark: | | Can find DPI scale factor | | | :heavy_check_mark: | -| Basic event handling (mouse, keyboard) | | | :heavy_check_mark: | -| Parent window support | | | | +| Basic event handling (mouse, keyboard) | | :heavy_check_mark: | :heavy_check_mark: | +| Parent window support | | :heavy_check_mark: | :heavy_check_mark: | | *(Converge on a common API for all platforms?)* | | | | ## Prerequisites From 71b27cf4735a792274d803b8a2e406006f75abf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Frosteg=C3=A5rd?= Date: Sat, 14 Nov 2020 17:11:37 +0100 Subject: [PATCH 8/8] README: fix error in milestone checkmarks --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a19c4d..448a754 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Below is a proposed list of milestones (roughly in-order) and their status. Subj | Window uses an OpenGL surface | :heavy_check_mark: | | :heavy_check_mark: | | Can find DPI scale factor | | | :heavy_check_mark: | | Basic event handling (mouse, keyboard) | | :heavy_check_mark: | :heavy_check_mark: | -| Parent window support | | :heavy_check_mark: | :heavy_check_mark: | +| Parent window support | :heavy_check_mark: | :heavy_check_mark: | | | *(Converge on a common API for all platforms?)* | | | | ## Prerequisites