Merge pull request #56 from greatest-ape/macos-keyboard
Add support for macOS and X11 key events with code from druid
This commit is contained in:
commit
808094db22
|
@ -15,6 +15,7 @@ license = "MIT OR Apache-2.0"
|
|||
|
||||
[dependencies]
|
||||
log = "0.4.11"
|
||||
keyboard-types = { version = "0.5.0", default-features = false }
|
||||
raw-window-handle = "0.3.3"
|
||||
|
||||
[target.'cfg(target_os="linux")'.dependencies]
|
||||
|
|
16
README.md
16
README.md
|
@ -16,10 +16,20 @@ 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
|
||||
|
||||
### 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 <a href="LICENSE-APACHE">Apache License, Version
|
||||
|
@ -27,4 +37,4 @@ Licensed under either of <a href="LICENSE-APACHE">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.
|
|
@ -1,11 +1,7 @@
|
|||
use keyboard_types::KeyboardEvent;
|
||||
|
||||
use crate::{WindowInfo, Point};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum KeyboardEvent {
|
||||
KeyPressed(u32),
|
||||
KeyReleased(u32),
|
||||
CharacterInput(char),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum MouseButton {
|
||||
|
|
|
@ -1,26 +1,55 @@
|
|||
// 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:
|
||||
// - only keep code_to_location function
|
||||
|
||||
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;
|
||||
//! Keyboard types.
|
||||
|
||||
shift && control && alt && logo
|
||||
use keyboard_types::{Code, Location};
|
||||
|
||||
|
||||
#[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
|
||||
/// 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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ mod mouse_cursor;
|
|||
mod window_info;
|
||||
mod window_open_options;
|
||||
pub use event::*;
|
||||
pub use keyboard::*;
|
||||
pub use mouse_cursor::MouseCursor;
|
||||
pub use window_info::*;
|
||||
pub use window_open_options::*;
|
||||
|
|
367
src/macos/keyboard.rs
Normal file
367
src/macos/keyboard.rs
Normal file
|
@ -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, Key, KeyboardEvent, Modifiers};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
|
||||
use crate::keyboard::code_to_location;
|
||||
|
||||
|
||||
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<Key> {
|
||||
Some(match code {
|
||||
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 => 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 => Key::KanjiMode,
|
||||
// kVK_JIS_Eisu
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
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<KeyboardEvent> {
|
||||
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 modifiers = 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) {
|
||||
Key::Character(characters)
|
||||
} else {
|
||||
let chars_ignoring = from_nsstring(event.charactersIgnoringModifiers());
|
||||
if is_valid_key(&chars_ignoring) {
|
||||
Key::Character(chars_ignoring)
|
||||
} else {
|
||||
// There may be more heroic things we can do here.
|
||||
Key::Unidentified
|
||||
}
|
||||
}
|
||||
};
|
||||
let event = KeyboardEvent {
|
||||
code,
|
||||
key,
|
||||
location,
|
||||
modifiers,
|
||||
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
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
mod keyboard;
|
||||
mod window;
|
||||
mod view;
|
||||
|
||||
|
|
|
@ -134,6 +134,15 @@ unsafe fn create_view_class<H: WindowHandler>() -> &'static Class {
|
|||
middle_mouse_up::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
class.add_method(
|
||||
sel!(keyDown:),
|
||||
key_down::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
class.add_method(
|
||||
sel!(keyUp:),
|
||||
key_up::<H> as extern "C" fn(&Object, Sel, id),
|
||||
);
|
||||
|
||||
class.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR_NAME);
|
||||
|
||||
class.register()
|
||||
|
@ -333,4 +342,26 @@ 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);
|
||||
mouse_simple_extern_fn!(mouse_exited, MouseEvent::CursorLeft);
|
||||
|
||||
|
||||
extern "C" fn key_down<H: WindowHandler>(this: &Object, _: Sel, event: id){
|
||||
let state: &mut WindowState<H> = unsafe {
|
||||
WindowState::from_field(this)
|
||||
};
|
||||
|
||||
if let Some(key_event) = state.process_native_key_event(event){
|
||||
state.trigger_event(Event::Keyboard(key_event));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" fn key_up<H: WindowHandler>(this: &Object, _: Sel, event: id){
|
||||
let state: &mut WindowState<H> = unsafe {
|
||||
WindowState::from_field(this)
|
||||
};
|
||||
|
||||
if let Some(key_event) = state.process_native_key_event(event){
|
||||
state.trigger_event(Event::Keyboard(key_event));
|
||||
}
|
||||
}
|
|
@ -11,17 +11,19 @@ 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, Parent, WindowHandler, WindowOpenOptions, WindowScalePolicy,
|
||||
WindowInfo
|
||||
Event, 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 +158,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 +180,7 @@ impl Window {
|
|||
pub(super) struct WindowState<H: WindowHandler> {
|
||||
window: Window,
|
||||
window_handler: H,
|
||||
keyboard_state: KeyboardState,
|
||||
}
|
||||
|
||||
|
||||
|
@ -195,6 +199,13 @@ impl <H: WindowHandler>WindowState<H> {
|
|||
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<KeyboardEvent> {
|
||||
self.keyboard_state.process_native_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
430
src/x11/keyboard.rs
Normal file
430
src/x11/keyboard.rs
Normal file
|
@ -0,0 +1,430 @@
|
|||
// 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 xcb::xproto;
|
||||
|
||||
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) -> Key {
|
||||
fn a(s: &str) -> Key {
|
||||
Key::Character(s.into())
|
||||
}
|
||||
fn s(mods: Modifiers, base: &str, shifted: &str) -> Key {
|
||||
if mods.contains(Modifiers::SHIFT) {
|
||||
Key::Character(shifted.into())
|
||||
} else {
|
||||
Key::Character(base.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
|
||||
}
|
||||
}
|
||||
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 => 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 => 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, Key::Delete, "."),
|
||||
Code::IntlBackslash => s(m, "\\", "|"),
|
||||
Code::F11 => Key::F11,
|
||||
Code::F12 => Key::F12,
|
||||
// This mapping is based on the picture in the w3c spec.
|
||||
Code::IntlRo => a("\\"),
|
||||
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 => 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 => Key::Pause,
|
||||
Code::NumpadComma => a(","),
|
||||
Code::Lang1 => Key::HangulMode,
|
||||
Code::Lang2 => Key::HanjaMode,
|
||||
Code::IntlYen => a("¥"),
|
||||
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,
|
||||
|
||||
_ => Key::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::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::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 {
|
||||
ret |= *modifiers;
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn convert_key_press_event(
|
||||
key_press: &xcb::KeyPressEvent,
|
||||
) -> KeyboardEvent {
|
||||
let hw_keycode = key_press.detail();
|
||||
let code = hardware_keycode_to_code(hw_keycode.into());
|
||||
let modifiers = key_mods(key_press.state());
|
||||
let key = code_to_key(code, modifiers);
|
||||
let location = code_to_location(code);
|
||||
let state = KeyState::Down;
|
||||
|
||||
KeyboardEvent {
|
||||
code,
|
||||
key,
|
||||
modifiers,
|
||||
location,
|
||||
state,
|
||||
repeat: false,
|
||||
is_composing: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub(super) fn convert_key_release_event(
|
||||
key_release: &xcb::KeyReleaseEvent
|
||||
) -> KeyboardEvent {
|
||||
let hw_keycode = key_release.detail();
|
||||
let code = hardware_keycode_to_code(hw_keycode.into());
|
||||
let modifiers = key_mods(key_release.state());
|
||||
let key = code_to_key(code, modifiers);
|
||||
let location = code_to_location(code);
|
||||
let state = KeyState::Up;
|
||||
|
||||
KeyboardEvent {
|
||||
code,
|
||||
key,
|
||||
modifiers,
|
||||
location,
|
||||
state,
|
||||
repeat: false,
|
||||
is_composing: false,
|
||||
}
|
||||
}
|
|
@ -4,4 +4,5 @@ use xcb_connection::XcbConnection;
|
|||
mod window;
|
||||
pub use window::*;
|
||||
|
||||
mod cursor;
|
||||
mod cursor;
|
||||
mod keyboard;
|
|
@ -11,10 +11,13 @@ use raw_window_handle::{
|
|||
|
||||
use super::XcbConnection;
|
||||
use crate::{
|
||||
Event, KeyboardEvent, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, WindowEvent,
|
||||
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint, PhySize,
|
||||
Event, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta,
|
||||
WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
|
||||
WindowScalePolicy, PhyPoint, PhySize,
|
||||
};
|
||||
|
||||
use super::keyboard::{convert_key_press_event, convert_key_release_event};
|
||||
|
||||
pub struct Window {
|
||||
xcb_connection: XcbConnection,
|
||||
window_id: u32,
|
||||
|
@ -392,21 +395,19 @@ impl Window {
|
|||
////
|
||||
xcb::KEY_PRESS => {
|
||||
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
|
||||
let detail = event.detail();
|
||||
|
||||
handler.on_event(
|
||||
self,
|
||||
Event::Keyboard(KeyboardEvent::KeyPressed(detail as u32)),
|
||||
Event::Keyboard(convert_key_press_event(&event))
|
||||
);
|
||||
}
|
||||
|
||||
xcb::KEY_RELEASE => {
|
||||
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
|
||||
let detail = event.detail();
|
||||
|
||||
handler.on_event(
|
||||
self,
|
||||
Event::Keyboard(KeyboardEvent::KeyReleased(detail as u32)),
|
||||
Event::Keyboard(convert_key_release_event(&event))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue