diff --git a/src/keyboard.rs b/src/keyboard.rs index bed3c0c4c6..acad629793 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -176,10 +176,14 @@ pub enum NativeKeyCode { Windows(u16), MacOS(u16), XKB(u32), + + /// This is the android "key code" of the event as returned by + /// `KeyEvent.getKeyCode()` + Android(u32), } impl std::fmt::Debug for NativeKeyCode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use NativeKeyCode::{MacOS, Unidentified, Windows, XKB}; + use NativeKeyCode::{Android, MacOS, Unidentified, Windows, XKB}; let mut debug_tuple; match self { Unidentified => { @@ -197,6 +201,10 @@ impl std::fmt::Debug for NativeKeyCode { debug_tuple = f.debug_tuple(name_of!(XKB)); debug_tuple.field(v); } + Android(v) => { + debug_tuple = f.debug_tuple(name_of!(Android)); + debug_tuple.field(&format_args!("0x{:04X}", v)); + } } debug_tuple.finish() } @@ -508,10 +516,13 @@ pub enum KeyCode { /// Pause Break Pause, /// Some laptops place this key to the left of the key. + /// + /// This also the "back" button (triangle) on Android. BrowserBack, BrowserFavorites, /// Some laptops place this key to the right of the key. BrowserForward, + /// The "home" button on Android. BrowserHome, BrowserRefresh, BrowserSearch, diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 4dc3bb4aff..6f45455839 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -4,6 +4,7 @@ use crate::{ dpi::{PhysicalPosition, PhysicalSize, Position, Size}, error, event, event_loop::{self, ControlFlow}, + keyboard::{Key, KeyCode, KeyLocation, NativeKeyCode}, monitor, window, }; use ndk::{ @@ -12,8 +13,10 @@ use ndk::{ looper::{ForeignLooper, Poll, ThreadLooper}, }; use ndk_glue::{Event, Rect}; +use ndk_sys::AKeyEvent_getKeyCode; use std::{ collections::VecDeque, + convert::TryInto, sync::{Arc, Mutex, RwLock}, time::{Duration, Instant}, }; @@ -41,6 +44,9 @@ fn poll(poll: Poll) -> Option { } } +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct KeyEventExtra {} + pub struct EventLoop { window_target: event_loop::EventLoopWindowTarget, user_queue: Arc>>, @@ -245,16 +251,35 @@ impl EventLoop { KeyAction::Up => event::ElementState::Released, _ => event::ElementState::Released, }; - #[allow(deprecated)] + + // We use the unsafe function directly because + // we want to forward the keycode value even if it doesn't have a variant + // defined in the ndk crate. + let keycode_u32 = unsafe { + AKeyEvent_getKeyCode(key.ptr().as_ptr()) as u32 + }; + let keycode = keycode_u32 + .try_into() + .unwrap_or(ndk::event::Keycode::Unknown); + let physical_key = KeyCode::Unidentified( + NativeKeyCode::Android(keycode.into()), + ); + let native = NativeKeyCode::Android(keycode_u32); + let logical_key = keycode_to_logical(keycode, native); + // TODO: maybe use getUnicodeChar to get the logical key + let event = event::Event::WindowEvent { window_id, event: event::WindowEvent::KeyboardInput { device_id, - input: event::KeyboardInput { - scancode: key.scan_code() as u32, + event: event::KeyEvent { state, - virtual_keycode: None, - modifiers: event::ModifiersState::default(), + physical_key, + logical_key, + location: keycode_to_location(keycode), + repeat: key.repeat_count() > 0, + text: None, + platform_specific: KeyEventExtra {}, }, is_synthetic: false, }, @@ -581,6 +606,8 @@ impl Window { pub fn content_rect(&self) -> Rect { ndk_glue::content_rect() } + + pub fn reset_dead_keys(&self) {} } #[derive(Default, Clone, Debug)] @@ -669,3 +696,377 @@ impl VideoMode { } } } + +fn keycode_to_logical(keycode: ndk::event::Keycode, native: NativeKeyCode) -> Key<'static> { + use ndk::event::Keycode::*; + + // The android `Keycode` is sort-of layout dependent. More specifically + // if I press the Z key using a US layout, then I get KEYCODE_Z, + // but if I press the same key after switching to a HUN layout, I get + // KEYCODE_Y. + // + // To prevents us from using this value to determine the `physical_key` + // (also know as winit's `KeyCode`) + // + // Unfortunately the documentation says that the scancode values + // "are not reliable and vary from device to device". Which seems to mean + // that there's no way to reliably get the physical_key on android. + + match keycode { + Unknown => Key::Unidentified(native), + + // Can be added on demand + SoftLeft => Key::Unidentified(native), + SoftRight => Key::Unidentified(native), + + // Using `BrowserHome` instead of `GoHome` according to + // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values + Home => Key::BrowserHome, + Back => Key::BrowserBack, + Call => Key::Call, + Endcall => Key::EndCall, + + //------------------------------------------------------------------------------- + // Reporting unidentified, because the specific character is layout dependent. + // (I'm not sure though) + Keycode0 => Key::Unidentified(native), + Keycode1 => Key::Unidentified(native), + Keycode2 => Key::Unidentified(native), + Keycode3 => Key::Unidentified(native), + Keycode4 => Key::Unidentified(native), + Keycode5 => Key::Unidentified(native), + Keycode6 => Key::Unidentified(native), + Keycode7 => Key::Unidentified(native), + Keycode8 => Key::Unidentified(native), + Keycode9 => Key::Unidentified(native), + Star => Key::Unidentified(native), + Pound => Key::Unidentified(native), + A => Key::Unidentified(native), + B => Key::Unidentified(native), + C => Key::Unidentified(native), + D => Key::Unidentified(native), + E => Key::Unidentified(native), + F => Key::Unidentified(native), + G => Key::Unidentified(native), + H => Key::Unidentified(native), + I => Key::Unidentified(native), + J => Key::Unidentified(native), + K => Key::Unidentified(native), + L => Key::Unidentified(native), + M => Key::Unidentified(native), + N => Key::Unidentified(native), + O => Key::Unidentified(native), + P => Key::Unidentified(native), + Q => Key::Unidentified(native), + R => Key::Unidentified(native), + S => Key::Unidentified(native), + T => Key::Unidentified(native), + U => Key::Unidentified(native), + V => Key::Unidentified(native), + W => Key::Unidentified(native), + X => Key::Unidentified(native), + Y => Key::Unidentified(native), + Z => Key::Unidentified(native), + Comma => Key::Unidentified(native), + Period => Key::Unidentified(native), + Grave => Key::Unidentified(native), + Minus => Key::Unidentified(native), + Equals => Key::Unidentified(native), + LeftBracket => Key::Unidentified(native), + RightBracket => Key::Unidentified(native), + Backslash => Key::Unidentified(native), + Semicolon => Key::Unidentified(native), + Apostrophe => Key::Unidentified(native), + Slash => Key::Unidentified(native), + At => Key::Unidentified(native), + Plus => Key::Unidentified(native), + //------------------------------------------------------------------------------- + DpadUp => Key::ArrowUp, + DpadDown => Key::ArrowDown, + DpadLeft => Key::ArrowLeft, + DpadRight => Key::ArrowRight, + DpadCenter => Key::Enter, + + VolumeUp => Key::AudioVolumeUp, + VolumeDown => Key::AudioVolumeDown, + Power => Key::Power, + Camera => Key::Camera, + Clear => Key::Clear, + + AltLeft => Key::Alt, + AltRight => Key::Alt, + ShiftLeft => Key::Shift, + ShiftRight => Key::Shift, + Tab => Key::Tab, + Space => Key::Space, + Sym => Key::Symbol, + Explorer => Key::LaunchWebBrowser, + Envelope => Key::LaunchMail, + Enter => Key::Enter, + Del => Key::Backspace, + + // According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM + Num => Key::Alt, + + Headsethook => Key::HeadsetHook, + Focus => Key::CameraFocus, + + Menu => Key::Unidentified(native), + + Notification => Key::Notification, + Search => Key::BrowserSearch, + MediaPlayPause => Key::MediaPlayPause, + MediaStop => Key::MediaStop, + MediaNext => Key::MediaTrackNext, + MediaPrevious => Key::MediaTrackPrevious, + MediaRewind => Key::MediaRewind, + MediaFastForward => Key::MediaFastForward, + Mute => Key::MicrophoneVolumeMute, + PageUp => Key::PageUp, + PageDown => Key::PageDown, + Pictsymbols => Key::Unidentified(native), + SwitchCharset => Key::Unidentified(native), + + // ----------------------------------------------------------------- + // Gamepad events should be exposed through a separate API, not + // keyboard events + ButtonA => Key::Unidentified(native), + ButtonB => Key::Unidentified(native), + ButtonC => Key::Unidentified(native), + ButtonX => Key::Unidentified(native), + ButtonY => Key::Unidentified(native), + ButtonZ => Key::Unidentified(native), + ButtonL1 => Key::Unidentified(native), + ButtonR1 => Key::Unidentified(native), + ButtonL2 => Key::Unidentified(native), + ButtonR2 => Key::Unidentified(native), + ButtonThumbl => Key::Unidentified(native), + ButtonThumbr => Key::Unidentified(native), + ButtonStart => Key::Unidentified(native), + ButtonSelect => Key::Unidentified(native), + ButtonMode => Key::Unidentified(native), + // ----------------------------------------------------------------- + Escape => Key::Escape, + ForwardDel => Key::Delete, + CtrlLeft => Key::Control, + CtrlRight => Key::Control, + CapsLock => Key::CapsLock, + ScrollLock => Key::ScrollLock, + MetaLeft => Key::Super, + MetaRight => Key::Super, + Function => Key::Fn, + Sysrq => Key::PrintScreen, + Break => Key::Pause, + MoveHome => Key::Home, + MoveEnd => Key::End, + Insert => Key::Insert, + Forward => Key::BrowserForward, + MediaPlay => Key::MediaPlay, + MediaPause => Key::MediaPause, + MediaClose => Key::MediaClose, + MediaEject => Key::Eject, + MediaRecord => Key::MediaRecord, + F1 => Key::F1, + F2 => Key::F2, + F3 => Key::F3, + F4 => Key::F4, + F5 => Key::F5, + F6 => Key::F6, + F7 => Key::F7, + F8 => Key::F8, + F9 => Key::F9, + F10 => Key::F10, + F11 => Key::F11, + F12 => Key::F12, + NumLock => Key::NumLock, + Numpad0 => Key::Unidentified(native), + Numpad1 => Key::Unidentified(native), + Numpad2 => Key::Unidentified(native), + Numpad3 => Key::Unidentified(native), + Numpad4 => Key::Unidentified(native), + Numpad5 => Key::Unidentified(native), + Numpad6 => Key::Unidentified(native), + Numpad7 => Key::Unidentified(native), + Numpad8 => Key::Unidentified(native), + Numpad9 => Key::Unidentified(native), + NumpadDivide => Key::Unidentified(native), + NumpadMultiply => Key::Unidentified(native), + NumpadSubtract => Key::Unidentified(native), + NumpadAdd => Key::Unidentified(native), + NumpadDot => Key::Unidentified(native), + NumpadComma => Key::Unidentified(native), + NumpadEnter => Key::Unidentified(native), + NumpadEquals => Key::Unidentified(native), + NumpadLeftParen => Key::Unidentified(native), + NumpadRightParen => Key::Unidentified(native), + + VolumeMute => Key::AudioVolumeMute, + Info => Key::Info, + ChannelUp => Key::ChannelUp, + ChannelDown => Key::ChannelDown, + ZoomIn => Key::ZoomIn, + ZoomOut => Key::ZoomOut, + Tv => Key::TV, + Window => Key::Unidentified(native), + Guide => Key::Guide, + Dvr => Key::DVR, + Bookmark => Key::BrowserFavorites, + Captions => Key::ClosedCaptionToggle, + Settings => Key::Settings, + TvPower => Key::TVPower, + TvInput => Key::TVInput, + StbPower => Key::STBPower, + StbInput => Key::STBInput, + AvrPower => Key::AVRPower, + AvrInput => Key::AVRInput, + ProgRed => Key::ColorF0Red, + ProgGreen => Key::ColorF1Green, + ProgYellow => Key::ColorF2Yellow, + ProgBlue => Key::ColorF3Blue, + AppSwitch => Key::AppSwitch, + Button1 => Key::Unidentified(native), + Button2 => Key::Unidentified(native), + Button3 => Key::Unidentified(native), + Button4 => Key::Unidentified(native), + Button5 => Key::Unidentified(native), + Button6 => Key::Unidentified(native), + Button7 => Key::Unidentified(native), + Button8 => Key::Unidentified(native), + Button9 => Key::Unidentified(native), + Button10 => Key::Unidentified(native), + Button11 => Key::Unidentified(native), + Button12 => Key::Unidentified(native), + Button13 => Key::Unidentified(native), + Button14 => Key::Unidentified(native), + Button15 => Key::Unidentified(native), + Button16 => Key::Unidentified(native), + LanguageSwitch => Key::GroupNext, + MannerMode => Key::MannerMode, + Keycode3dMode => Key::TV3DMode, + Contacts => Key::LaunchContacts, + Calendar => Key::LaunchCalendar, + Music => Key::LaunchMusicPlayer, + Calculator => Key::LaunchApplication2, + ZenkakuHankaku => Key::ZenkakuHankaku, + Eisu => Key::Eisu, + Muhenkan => Key::NonConvert, + Henkan => Key::Convert, + KatakanaHiragana => Key::HiraganaKatakana, + Yen => Key::Unidentified(native), + Ro => Key::Unidentified(native), + Kana => Key::KanjiMode, + Assist => Key::Unidentified(native), + BrightnessDown => Key::BrightnessDown, + BrightnessUp => Key::BrightnessUp, + MediaAudioTrack => Key::MediaAudioTrack, + Sleep => Key::Standby, + Wakeup => Key::WakeUp, + Pairing => Key::Pairing, + MediaTopMenu => Key::MediaTopMenu, + Keycode11 => Key::Unidentified(native), + Keycode12 => Key::Unidentified(native), + LastChannel => Key::MediaLast, + TvDataService => Key::TVDataService, + VoiceAssist => Key::VoiceDial, + TvRadioService => Key::TVRadioService, + TvTeletext => Key::Teletext, + TvNumberEntry => Key::TVNumberEntry, + TvTerrestrialAnalog => Key::TVTerrestrialAnalog, + TvTerrestrialDigital => Key::TVTerrestrialDigital, + TvSatellite => Key::TVSatellite, + TvSatelliteBs => Key::TVSatelliteBS, + TvSatelliteCs => Key::TVSatelliteCS, + TvSatelliteService => Key::TVSatelliteToggle, + TvNetwork => Key::TVNetwork, + TvAntennaCable => Key::TVAntennaCable, + TvInputHdmi1 => Key::TVInputHDMI1, + TvInputHdmi2 => Key::TVInputHDMI2, + TvInputHdmi3 => Key::TVInputHDMI3, + TvInputHdmi4 => Key::TVInputHDMI4, + TvInputComposite1 => Key::TVInputComposite1, + TvInputComposite2 => Key::TVInputComposite2, + TvInputComponent1 => Key::TVInputComponent1, + TvInputComponent2 => Key::TVInputComponent2, + TvInputVga1 => Key::TVInputVGA1, + TvAudioDescription => Key::TVAudioDescription, + TvAudioDescriptionMixUp => Key::TVAudioDescriptionMixUp, + TvAudioDescriptionMixDown => Key::TVAudioDescriptionMixDown, + TvZoomMode => Key::ZoomToggle, + TvContentsMenu => Key::TVContentsMenu, + TvMediaContextMenu => Key::TVMediaContext, + TvTimerProgramming => Key::TVTimer, + Help => Key::Help, + NavigatePrevious => Key::NavigatePrevious, + NavigateNext => Key::NavigateNext, + NavigateIn => Key::NavigateIn, + NavigateOut => Key::NavigateOut, + StemPrimary => Key::Unidentified(native), + Stem1 => Key::Unidentified(native), + Stem2 => Key::Unidentified(native), + Stem3 => Key::Unidentified(native), + DpadUpLeft => Key::Unidentified(native), + DpadDownLeft => Key::Unidentified(native), + DpadUpRight => Key::Unidentified(native), + DpadDownRight => Key::Unidentified(native), + MediaSkipForward => Key::MediaSkipForward, + MediaSkipBackward => Key::MediaSkipBackward, + MediaStepForward => Key::MediaStepForward, + MediaStepBackward => Key::MediaStepBackward, + SoftSleep => Key::Unidentified(native), + Cut => Key::Cut, + Copy => Key::Copy, + Paste => Key::Paste, + SystemNavigationUp => Key::Unidentified(native), + SystemNavigationDown => Key::Unidentified(native), + SystemNavigationLeft => Key::Unidentified(native), + SystemNavigationRight => Key::Unidentified(native), + AllApps => Key::Unidentified(native), + Refresh => Key::BrowserRefresh, + ThumbsUp => Key::Unidentified(native), + ThumbsDown => Key::Unidentified(native), + ProfileSwitch => Key::Unidentified(native), + } +} + +fn keycode_to_location(keycode: ndk::event::Keycode) -> KeyLocation { + use ndk::event::Keycode::*; + + match keycode { + AltLeft => KeyLocation::Left, + AltRight => KeyLocation::Right, + ShiftLeft => KeyLocation::Left, + ShiftRight => KeyLocation::Right, + + // According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM + Num => KeyLocation::Left, + + CtrlLeft => KeyLocation::Left, + CtrlRight => KeyLocation::Right, + MetaLeft => KeyLocation::Left, + MetaRight => KeyLocation::Right, + + NumLock => KeyLocation::Numpad, + Numpad0 => KeyLocation::Numpad, + Numpad1 => KeyLocation::Numpad, + Numpad2 => KeyLocation::Numpad, + Numpad3 => KeyLocation::Numpad, + Numpad4 => KeyLocation::Numpad, + Numpad5 => KeyLocation::Numpad, + Numpad6 => KeyLocation::Numpad, + Numpad7 => KeyLocation::Numpad, + Numpad8 => KeyLocation::Numpad, + Numpad9 => KeyLocation::Numpad, + NumpadDivide => KeyLocation::Numpad, + NumpadMultiply => KeyLocation::Numpad, + NumpadSubtract => KeyLocation::Numpad, + NumpadAdd => KeyLocation::Numpad, + NumpadDot => KeyLocation::Numpad, + NumpadComma => KeyLocation::Numpad, + NumpadEnter => KeyLocation::Numpad, + NumpadEquals => KeyLocation::Numpad, + NumpadLeftParen => KeyLocation::Numpad, + NumpadRightParen => KeyLocation::Numpad, + + _ => KeyLocation::Standard, + } +}