use crate::{ButtonInput, ButtonState};
use bevy_ecs::{
change_detection::DetectChangesMut,
entity::Entity,
message::{Message, MessageReader},
system::ResMut,
};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
#[cfg(not(feature = "smol_str"))]
use alloc::string::String as SmolStr;
#[cfg(feature = "smol_str")]
use smol_str::SmolStr;
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
#[derive(Message, Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Hash, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct KeyboardInput {
pub key_code: KeyCode,
pub logical_key: Key,
pub state: ButtonState,
pub text: Option<SmolStr>,
pub repeat: bool,
pub window: Entity,
}
#[derive(Message, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone, PartialEq))]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct KeyboardFocusLost;
pub fn keyboard_input_system(
mut keycode_input: ResMut<ButtonInput<KeyCode>>,
mut key_input: ResMut<ButtonInput<Key>>,
mut keyboard_input_reader: MessageReader<KeyboardInput>,
mut keyboard_focus_lost_reader: MessageReader<KeyboardFocusLost>,
) {
keycode_input.bypass_change_detection().clear();
key_input.bypass_change_detection().clear();
for event in keyboard_input_reader.read() {
let KeyboardInput {
key_code,
logical_key,
state,
..
} = event;
match state {
ButtonState::Pressed => {
keycode_input.press(*key_code);
key_input.press(logical_key.clone());
}
ButtonState::Released => {
keycode_input.release(*key_code);
key_input.release(logical_key.clone());
}
}
}
if !keyboard_focus_lost_reader.is_empty() {
keycode_input.release_all();
keyboard_focus_lost_reader.clear();
}
}
#[derive(Debug, Clone, Ord, PartialOrd, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Clone, PartialEq, Hash)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub enum NativeKeyCode {
Unidentified,
Android(u32),
MacOS(u16),
Windows(u16),
Xkb(u32),
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
#[expect(
clippy::doc_markdown,
reason = "We use camel-case words inside `<kbd>` tags to represent keyboard keys, which are not identifiers that we should be putting inside backticks."
)]
#[repr(u32)]
pub enum KeyCode {
Unidentified(NativeKeyCode),
Backquote,
Backslash,
BracketLeft,
BracketRight,
Comma,
Digit0,
Digit1,
Digit2,
Digit3,
Digit4,
Digit5,
Digit6,
Digit7,
Digit8,
Digit9,
Equal,
IntlBackslash,
IntlRo,
IntlYen,
KeyA,
KeyB,
KeyC,
KeyD,
KeyE,
KeyF,
KeyG,
KeyH,
KeyI,
KeyJ,
KeyK,
KeyL,
KeyM,
KeyN,
KeyO,
KeyP,
KeyQ,
KeyR,
KeyS,
KeyT,
KeyU,
KeyV,
KeyW,
KeyX,
KeyY,
KeyZ,
Minus,
Period,
Quote,
Semicolon,
Slash,
AltLeft,
AltRight,
Backspace,
CapsLock,
ContextMenu,
ControlLeft,
ControlRight,
Enter,
SuperLeft,
SuperRight,
ShiftLeft,
ShiftRight,
Space,
Tab,
Convert,
KanaMode,
Lang1,
Lang2,
Lang3,
Lang4,
Lang5,
NonConvert,
Delete,
End,
Help,
Home,
Insert,
PageDown,
PageUp,
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowUp,
NumLock,
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
NumpadAdd,
NumpadBackspace,
NumpadClear,
NumpadClearEntry,
NumpadComma,
NumpadDecimal,
NumpadDivide,
NumpadEnter,
NumpadEqual,
NumpadHash,
NumpadMemoryAdd,
NumpadMemoryClear,
NumpadMemoryRecall,
NumpadMemoryStore,
NumpadMemorySubtract,
NumpadMultiply,
NumpadParenLeft,
NumpadParenRight,
NumpadStar,
NumpadSubtract,
Escape,
Fn,
FnLock,
PrintScreen,
ScrollLock,
Pause,
BrowserBack,
BrowserFavorites,
BrowserForward,
BrowserHome,
BrowserRefresh,
BrowserSearch,
BrowserStop,
Eject,
LaunchApp1,
LaunchApp2,
LaunchMail,
MediaPlayPause,
MediaSelect,
MediaStop,
MediaTrackNext,
MediaTrackPrevious,
Power,
Sleep,
AudioVolumeDown,
AudioVolumeMute,
AudioVolumeUp,
WakeUp,
Meta,
Hyper,
Turbo,
Abort,
Resume,
Suspend,
Again,
Copy,
Cut,
Find,
Open,
Paste,
Props,
Select,
Undo,
Hiragana,
Katakana,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
F25,
F26,
F27,
F28,
F29,
F30,
F31,
F32,
F33,
F34,
F35,
}
#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Hash)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub enum NativeKey {
Unidentified,
Android(u32),
MacOS(u16),
Windows(u16),
Xkb(u32),
Web(SmolStr),
}
#[non_exhaustive]
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, Hash, PartialEq, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
#[expect(
clippy::doc_markdown,
reason = "We use camel-case words inside `<kbd>` tags to represent keyboard keys, which are not identifiers that we should be putting inside backticks."
)]
pub enum Key {
Character(SmolStr),
Unidentified(NativeKey),
Dead(Option<char>),
Alt,
AltGraph,
CapsLock,
Control,
Fn,
FnLock,
NumLock,
ScrollLock,
Shift,
Symbol,
SymbolLock,
Meta,
Hyper,
Super,
Enter,
Tab,
Space,
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowUp,
End,
Home,
PageDown,
PageUp,
Backspace,
Clear,
Copy,
CrSel,
Cut,
Delete,
EraseEof,
ExSel,
Insert,
Paste,
Redo,
Undo,
Accept,
Again,
Attn,
Cancel,
ContextMenu,
Escape,
Execute,
Find,
Help,
Pause,
Play,
Props,
Select,
ZoomIn,
ZoomOut,
BrightnessDown,
BrightnessUp,
Eject,
LogOff,
Power,
PowerOff,
PrintScreen,
Hibernate,
Standby,
WakeUp,
AllCandidates,
Alphanumeric,
CodeInput,
Compose,
Convert,
FinalMode,
GroupFirst,
GroupLast,
GroupNext,
GroupPrevious,
ModeChange,
NextCandidate,
NonConvert,
PreviousCandidate,
Process,
SingleCandidate,
HangulMode,
HanjaMode,
JunjaMode,
Eisu,
Hankaku,
Hiragana,
HiraganaKatakana,
KanaMode,
KanjiMode,
Katakana,
Romaji,
Zenkaku,
ZenkakuHankaku,
Soft1,
Soft2,
Soft3,
Soft4,
ChannelDown,
ChannelUp,
Close,
MailForward,
MailReply,
MailSend,
MediaClose,
MediaFastForward,
MediaPause,
MediaPlay,
MediaPlayPause,
MediaRecord,
MediaRewind,
MediaStop,
MediaTrackNext,
MediaTrackPrevious,
New,
Open,
Print,
Save,
SpellCheck,
Key11,
Key12,
AudioBalanceLeft,
AudioBalanceRight,
AudioBassBoostDown,
AudioBassBoostToggle,
AudioBassBoostUp,
AudioFaderFront,
AudioFaderRear,
AudioSurroundModeNext,
AudioTrebleDown,
AudioTrebleUp,
AudioVolumeDown,
AudioVolumeUp,
AudioVolumeMute,
MicrophoneToggle,
MicrophoneVolumeDown,
MicrophoneVolumeUp,
MicrophoneVolumeMute,
SpeechCorrectionList,
SpeechInputToggle,
LaunchApplication1,
LaunchApplication2,
LaunchCalendar,
LaunchContacts,
LaunchMail,
LaunchMediaPlayer,
LaunchMusicPlayer,
LaunchPhone,
LaunchScreenSaver,
LaunchSpreadsheet,
LaunchWebBrowser,
LaunchWebCam,
LaunchWordProcessor,
BrowserBack,
BrowserFavorites,
BrowserForward,
BrowserHome,
BrowserRefresh,
BrowserSearch,
BrowserStop,
AppSwitch,
Call,
Camera,
CameraFocus,
EndCall,
GoBack,
GoHome,
HeadsetHook,
LastNumberRedial,
Notification,
MannerMode,
VoiceDial,
TV,
TV3DMode,
TVAntennaCable,
TVAudioDescription,
TVAudioDescriptionMixDown,
TVAudioDescriptionMixUp,
TVContentsMenu,
TVDataService,
TVInput,
TVInputComponent1,
TVInputComponent2,
TVInputComposite1,
TVInputComposite2,
TVInputHDMI1,
TVInputHDMI2,
TVInputHDMI3,
TVInputHDMI4,
TVInputVGA1,
TVMediaContext,
TVNetwork,
TVNumberEntry,
TVPower,
TVRadioService,
TVSatellite,
TVSatelliteBS,
TVSatelliteCS,
TVSatelliteToggle,
TVTerrestrialAnalog,
TVTerrestrialDigital,
TVTimer,
AVRInput,
AVRPower,
ColorF0Red,
ColorF1Green,
ColorF2Yellow,
ColorF3Blue,
ColorF4Grey,
ColorF5Brown,
ClosedCaptionToggle,
Dimmer,
DisplaySwap,
DVR,
Exit,
FavoriteClear0,
FavoriteClear1,
FavoriteClear2,
FavoriteClear3,
FavoriteRecall0,
FavoriteRecall1,
FavoriteRecall2,
FavoriteRecall3,
FavoriteStore0,
FavoriteStore1,
FavoriteStore2,
FavoriteStore3,
Guide,
GuideNextDay,
GuidePreviousDay,
Info,
InstantReplay,
Link,
ListProgram,
LiveContent,
Lock,
MediaApps,
MediaAudioTrack,
MediaLast,
MediaSkipBackward,
MediaSkipForward,
MediaStepBackward,
MediaStepForward,
MediaTopMenu,
NavigateIn,
NavigateNext,
NavigateOut,
NavigatePrevious,
NextFavoriteChannel,
NextUserProfile,
OnDemand,
Pairing,
PinPDown,
PinPMove,
PinPToggle,
PinPUp,
PlaySpeedDown,
PlaySpeedReset,
PlaySpeedUp,
RandomToggle,
RcLowBattery,
RecordSpeedNext,
RfBypass,
ScanChannelsToggle,
ScreenModeNext,
Settings,
SplitScreenToggle,
STBInput,
STBPower,
Subtitle,
Teletext,
VideoModeNext,
Wink,
ZoomToggle,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
F16,
F17,
F18,
F19,
F20,
F21,
F22,
F23,
F24,
F25,
F26,
F27,
F28,
F29,
F30,
F31,
F32,
F33,
F34,
F35,
}