Skip to main content

libretro_core/
input.rs

1//! Typed input devices, descriptors, polling IDs, and input-side services.
2//!
3//! Joypad, analog, mouse, pointer, and lightgun input are polled through
4//! `Runtime`; keyboard input is delivered as `KeyboardEvent` when registered
5//! through `CoreEventConfig`.
6
7use enumflags2::{BitFlags, bitflags};
8
9use crate::raw::{
10    RETRO_DEVICE_ANALOG, RETRO_DEVICE_ID_ANALOG_X, RETRO_DEVICE_ID_ANALOG_Y,
11    RETRO_DEVICE_ID_JOYPAD_A, RETRO_DEVICE_ID_JOYPAD_B, RETRO_DEVICE_ID_JOYPAD_DOWN,
12    RETRO_DEVICE_ID_JOYPAD_L, RETRO_DEVICE_ID_JOYPAD_L2, RETRO_DEVICE_ID_JOYPAD_L3,
13    RETRO_DEVICE_ID_JOYPAD_LEFT, RETRO_DEVICE_ID_JOYPAD_MASK, RETRO_DEVICE_ID_JOYPAD_R,
14    RETRO_DEVICE_ID_JOYPAD_R2, RETRO_DEVICE_ID_JOYPAD_R3, RETRO_DEVICE_ID_JOYPAD_RIGHT,
15    RETRO_DEVICE_ID_JOYPAD_SELECT, RETRO_DEVICE_ID_JOYPAD_START, RETRO_DEVICE_ID_JOYPAD_UP,
16    RETRO_DEVICE_ID_JOYPAD_X, RETRO_DEVICE_ID_JOYPAD_Y, RETRO_DEVICE_ID_LIGHTGUN_AUX_A,
17    RETRO_DEVICE_ID_LIGHTGUN_AUX_B, RETRO_DEVICE_ID_LIGHTGUN_AUX_C,
18    RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN, RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT,
19    RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT, RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP,
20    RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN, RETRO_DEVICE_ID_LIGHTGUN_RELOAD,
21    RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y,
22    RETRO_DEVICE_ID_LIGHTGUN_SELECT, RETRO_DEVICE_ID_LIGHTGUN_START,
23    RETRO_DEVICE_ID_LIGHTGUN_TRIGGER, RETRO_DEVICE_ID_LIGHTGUN_X, RETRO_DEVICE_ID_LIGHTGUN_Y,
24    RETRO_DEVICE_ID_MOUSE_BUTTON_4, RETRO_DEVICE_ID_MOUSE_BUTTON_5,
25    RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN, RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP,
26    RETRO_DEVICE_ID_MOUSE_LEFT, RETRO_DEVICE_ID_MOUSE_MIDDLE, RETRO_DEVICE_ID_MOUSE_RIGHT,
27    RETRO_DEVICE_ID_MOUSE_WHEELDOWN, RETRO_DEVICE_ID_MOUSE_WHEELUP, RETRO_DEVICE_ID_MOUSE_X,
28    RETRO_DEVICE_ID_MOUSE_Y, RETRO_DEVICE_ID_POINTER_COUNT, RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN,
29    RETRO_DEVICE_ID_POINTER_PRESSED, RETRO_DEVICE_ID_POINTER_X, RETRO_DEVICE_ID_POINTER_Y,
30    RETRO_DEVICE_INDEX_ANALOG_BUTTON, RETRO_DEVICE_INDEX_ANALOG_LEFT,
31    RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_JOYPAD, RETRO_DEVICE_KEYBOARD,
32    RETRO_DEVICE_LIGHTGUN, RETRO_DEVICE_MASK, RETRO_DEVICE_MOUSE, RETRO_DEVICE_NONE,
33    RETRO_DEVICE_POINTER, RETRO_DEVICE_TYPE_SHIFT,
34};
35
36#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
37pub struct LedIndex(i32);
38
39impl LedIndex {
40    pub const fn new(index: i32) -> Self {
41        Self(index)
42    }
43
44    pub const fn as_raw(self) -> i32 {
45        self.0
46    }
47}
48
49impl From<i32> for LedIndex {
50    fn from(index: i32) -> Self {
51        Self::new(index)
52    }
53}
54
55#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
56pub enum LedState {
57    #[default]
58    Off,
59    On,
60}
61
62impl LedState {
63    pub const fn as_raw(self) -> i32 {
64        match self {
65            Self::Off => 0,
66            Self::On => 1,
67        }
68    }
69}
70
71#[derive(Clone, Copy, Debug, Default)]
72pub struct LedInterface {
73    raw: crate::raw::retro_led_interface,
74}
75
76impl LedInterface {
77    pub(crate) const fn from_raw(raw: crate::raw::retro_led_interface) -> Self {
78        Self { raw }
79    }
80
81    pub const fn is_available(self) -> bool {
82        self.raw.set_led_state.is_some()
83    }
84
85    pub fn set_state(self, led: impl Into<LedIndex>, state: LedState) -> bool {
86        let Some(callback) = self.raw.set_led_state else {
87            return false;
88        };
89
90        // SAFETY: The callback pointer is provided by the frontend through the
91        // libretro LED interface. Arguments are plain value types.
92        unsafe { callback(led.into().as_raw(), state.as_raw()) };
93        true
94    }
95}
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
98pub enum RumbleEffect {
99    Strong,
100    Weak,
101}
102
103impl RumbleEffect {
104    pub(crate) const fn as_raw(self) -> crate::raw::retro_rumble_effect {
105        match self {
106            Self::Strong => crate::raw::retro_rumble_effect::Strong,
107            Self::Weak => crate::raw::retro_rumble_effect::Weak,
108        }
109    }
110}
111
112#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
113pub struct RumbleStrength(u16);
114
115impl RumbleStrength {
116    pub const fn new(strength: u16) -> Self {
117        Self(strength)
118    }
119
120    pub const fn off() -> Self {
121        Self(0)
122    }
123
124    pub const fn max() -> Self {
125        Self(u16::MAX)
126    }
127
128    pub const fn as_raw(self) -> u16 {
129        self.0
130    }
131}
132
133impl From<u16> for RumbleStrength {
134    fn from(strength: u16) -> Self {
135        Self::new(strength)
136    }
137}
138
139#[derive(Clone, Copy, Debug, Default)]
140pub struct RumbleInterface {
141    raw: crate::raw::retro_rumble_interface,
142}
143
144impl RumbleInterface {
145    pub(crate) const fn from_raw(raw: crate::raw::retro_rumble_interface) -> Self {
146        Self { raw }
147    }
148
149    pub const fn is_available(self) -> bool {
150        self.raw.set_rumble_state.is_some()
151    }
152
153    pub fn set_state(
154        self,
155        port: impl Into<InputPort>,
156        effect: RumbleEffect,
157        strength: impl Into<RumbleStrength>,
158    ) -> bool {
159        let Some(callback) = self.raw.set_rumble_state else {
160            return false;
161        };
162
163        // SAFETY: The callback pointer is provided by the frontend through the
164        // libretro rumble interface. Arguments are plain value types.
165        unsafe {
166            callback(
167                port.into().as_raw(),
168                effect.as_raw(),
169                strength.into().as_raw(),
170            )
171        }
172    }
173}
174
175#[bitflags]
176#[repr(u64)]
177#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
178pub enum InputDeviceCapability {
179    Joypad = 1u64 << RETRO_DEVICE_JOYPAD,
180    Mouse = 1u64 << RETRO_DEVICE_MOUSE,
181    Keyboard = 1u64 << RETRO_DEVICE_KEYBOARD,
182    Lightgun = 1u64 << RETRO_DEVICE_LIGHTGUN,
183    Analog = 1u64 << RETRO_DEVICE_ANALOG,
184    Pointer = 1u64 << RETRO_DEVICE_POINTER,
185}
186
187pub type InputDeviceCapabilities = BitFlags<InputDeviceCapability>;
188
189impl InputDeviceCapability {
190    pub const fn device(self) -> ControllerDevice {
191        match self {
192            Self::Joypad => ControllerDevice::Joypad,
193            Self::Mouse => ControllerDevice::Mouse,
194            Self::Keyboard => ControllerDevice::Keyboard,
195            Self::Lightgun => ControllerDevice::Lightgun,
196            Self::Analog => ControllerDevice::Analog,
197            Self::Pointer => ControllerDevice::Pointer,
198        }
199    }
200}
201
202#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
203pub enum KeyboardKey {
204    #[default]
205    Unknown,
206    Backspace,
207    Tab,
208    Clear,
209    Return,
210    Pause,
211    Escape,
212    Space,
213    Exclaim,
214    QuoteDbl,
215    Hash,
216    Dollar,
217    Ampersand,
218    Quote,
219    LeftParen,
220    RightParen,
221    Asterisk,
222    Plus,
223    Comma,
224    Minus,
225    Period,
226    Slash,
227    Num0,
228    Num1,
229    Num2,
230    Num3,
231    Num4,
232    Num5,
233    Num6,
234    Num7,
235    Num8,
236    Num9,
237    Colon,
238    Semicolon,
239    Less,
240    Equals,
241    Greater,
242    Question,
243    At,
244    LeftBracket,
245    Backslash,
246    RightBracket,
247    Caret,
248    Underscore,
249    Backquote,
250    A,
251    B,
252    C,
253    D,
254    E,
255    F,
256    G,
257    H,
258    I,
259    J,
260    K,
261    L,
262    M,
263    N,
264    O,
265    P,
266    Q,
267    R,
268    S,
269    T,
270    U,
271    V,
272    W,
273    X,
274    Y,
275    Z,
276    LeftBrace,
277    Bar,
278    RightBrace,
279    Tilde,
280    Delete,
281    Kp0,
282    Kp1,
283    Kp2,
284    Kp3,
285    Kp4,
286    Kp5,
287    Kp6,
288    Kp7,
289    Kp8,
290    Kp9,
291    KpPeriod,
292    KpDivide,
293    KpMultiply,
294    KpMinus,
295    KpPlus,
296    KpEnter,
297    KpEquals,
298    Up,
299    Down,
300    Right,
301    Left,
302    Insert,
303    Home,
304    End,
305    PageUp,
306    PageDown,
307    F1,
308    F2,
309    F3,
310    F4,
311    F5,
312    F6,
313    F7,
314    F8,
315    F9,
316    F10,
317    F11,
318    F12,
319    F13,
320    F14,
321    F15,
322    NumLock,
323    CapsLock,
324    ScrollLock,
325    RightShift,
326    LeftShift,
327    RightCtrl,
328    LeftCtrl,
329    RightAlt,
330    LeftAlt,
331    RightMeta,
332    LeftMeta,
333    LeftSuper,
334    RightSuper,
335    Mode,
336    Compose,
337    Help,
338    Print,
339    SysReq,
340    Break,
341    Menu,
342    Power,
343    Euro,
344    Undo,
345    Oem102,
346    BrowserBack,
347    BrowserForward,
348    BrowserRefresh,
349    BrowserStop,
350    BrowserSearch,
351    BrowserFavorites,
352    BrowserHome,
353    VolumeMute,
354    VolumeDown,
355    VolumeUp,
356    MediaNext,
357    MediaPrev,
358    MediaStop,
359    MediaPlayPause,
360    LaunchMail,
361    LaunchMedia,
362    LaunchApp1,
363    LaunchApp2,
364    Last,
365    UnknownKeycode(u32),
366}
367
368impl KeyboardKey {
369    pub const fn from_raw(keycode: u32) -> Self {
370        match keycode {
371            0 => Self::Unknown,
372            8 => Self::Backspace,
373            9 => Self::Tab,
374            12 => Self::Clear,
375            13 => Self::Return,
376            19 => Self::Pause,
377            27 => Self::Escape,
378            32 => Self::Space,
379            33 => Self::Exclaim,
380            34 => Self::QuoteDbl,
381            35 => Self::Hash,
382            36 => Self::Dollar,
383            38 => Self::Ampersand,
384            39 => Self::Quote,
385            40 => Self::LeftParen,
386            41 => Self::RightParen,
387            42 => Self::Asterisk,
388            43 => Self::Plus,
389            44 => Self::Comma,
390            45 => Self::Minus,
391            46 => Self::Period,
392            47 => Self::Slash,
393            48 => Self::Num0,
394            49 => Self::Num1,
395            50 => Self::Num2,
396            51 => Self::Num3,
397            52 => Self::Num4,
398            53 => Self::Num5,
399            54 => Self::Num6,
400            55 => Self::Num7,
401            56 => Self::Num8,
402            57 => Self::Num9,
403            58 => Self::Colon,
404            59 => Self::Semicolon,
405            60 => Self::Less,
406            61 => Self::Equals,
407            62 => Self::Greater,
408            63 => Self::Question,
409            64 => Self::At,
410            91 => Self::LeftBracket,
411            92 => Self::Backslash,
412            93 => Self::RightBracket,
413            94 => Self::Caret,
414            95 => Self::Underscore,
415            96 => Self::Backquote,
416            97 => Self::A,
417            98 => Self::B,
418            99 => Self::C,
419            100 => Self::D,
420            101 => Self::E,
421            102 => Self::F,
422            103 => Self::G,
423            104 => Self::H,
424            105 => Self::I,
425            106 => Self::J,
426            107 => Self::K,
427            108 => Self::L,
428            109 => Self::M,
429            110 => Self::N,
430            111 => Self::O,
431            112 => Self::P,
432            113 => Self::Q,
433            114 => Self::R,
434            115 => Self::S,
435            116 => Self::T,
436            117 => Self::U,
437            118 => Self::V,
438            119 => Self::W,
439            120 => Self::X,
440            121 => Self::Y,
441            122 => Self::Z,
442            123 => Self::LeftBrace,
443            124 => Self::Bar,
444            125 => Self::RightBrace,
445            126 => Self::Tilde,
446            127 => Self::Delete,
447            256 => Self::Kp0,
448            257 => Self::Kp1,
449            258 => Self::Kp2,
450            259 => Self::Kp3,
451            260 => Self::Kp4,
452            261 => Self::Kp5,
453            262 => Self::Kp6,
454            263 => Self::Kp7,
455            264 => Self::Kp8,
456            265 => Self::Kp9,
457            266 => Self::KpPeriod,
458            267 => Self::KpDivide,
459            268 => Self::KpMultiply,
460            269 => Self::KpMinus,
461            270 => Self::KpPlus,
462            271 => Self::KpEnter,
463            272 => Self::KpEquals,
464            273 => Self::Up,
465            274 => Self::Down,
466            275 => Self::Right,
467            276 => Self::Left,
468            277 => Self::Insert,
469            278 => Self::Home,
470            279 => Self::End,
471            280 => Self::PageUp,
472            281 => Self::PageDown,
473            282 => Self::F1,
474            283 => Self::F2,
475            284 => Self::F3,
476            285 => Self::F4,
477            286 => Self::F5,
478            287 => Self::F6,
479            288 => Self::F7,
480            289 => Self::F8,
481            290 => Self::F9,
482            291 => Self::F10,
483            292 => Self::F11,
484            293 => Self::F12,
485            294 => Self::F13,
486            295 => Self::F14,
487            296 => Self::F15,
488            300 => Self::NumLock,
489            301 => Self::CapsLock,
490            302 => Self::ScrollLock,
491            303 => Self::RightShift,
492            304 => Self::LeftShift,
493            305 => Self::RightCtrl,
494            306 => Self::LeftCtrl,
495            307 => Self::RightAlt,
496            308 => Self::LeftAlt,
497            309 => Self::RightMeta,
498            310 => Self::LeftMeta,
499            311 => Self::LeftSuper,
500            312 => Self::RightSuper,
501            313 => Self::Mode,
502            314 => Self::Compose,
503            315 => Self::Help,
504            316 => Self::Print,
505            317 => Self::SysReq,
506            318 => Self::Break,
507            319 => Self::Menu,
508            320 => Self::Power,
509            321 => Self::Euro,
510            322 => Self::Undo,
511            323 => Self::Oem102,
512            324 => Self::BrowserBack,
513            325 => Self::BrowserForward,
514            326 => Self::BrowserRefresh,
515            327 => Self::BrowserStop,
516            328 => Self::BrowserSearch,
517            329 => Self::BrowserFavorites,
518            330 => Self::BrowserHome,
519            331 => Self::VolumeMute,
520            332 => Self::VolumeDown,
521            333 => Self::VolumeUp,
522            334 => Self::MediaNext,
523            335 => Self::MediaPrev,
524            336 => Self::MediaStop,
525            337 => Self::MediaPlayPause,
526            338 => Self::LaunchMail,
527            339 => Self::LaunchMedia,
528            340 => Self::LaunchApp1,
529            341 => Self::LaunchApp2,
530            342 => Self::Last,
531            other => Self::UnknownKeycode(other),
532        }
533    }
534
535    pub const fn as_raw(self) -> u32 {
536        match self {
537            Self::Unknown => 0,
538            Self::Backspace => 8,
539            Self::Tab => 9,
540            Self::Clear => 12,
541            Self::Return => 13,
542            Self::Pause => 19,
543            Self::Escape => 27,
544            Self::Space => 32,
545            Self::Exclaim => 33,
546            Self::QuoteDbl => 34,
547            Self::Hash => 35,
548            Self::Dollar => 36,
549            Self::Ampersand => 38,
550            Self::Quote => 39,
551            Self::LeftParen => 40,
552            Self::RightParen => 41,
553            Self::Asterisk => 42,
554            Self::Plus => 43,
555            Self::Comma => 44,
556            Self::Minus => 45,
557            Self::Period => 46,
558            Self::Slash => 47,
559            Self::Num0 => 48,
560            Self::Num1 => 49,
561            Self::Num2 => 50,
562            Self::Num3 => 51,
563            Self::Num4 => 52,
564            Self::Num5 => 53,
565            Self::Num6 => 54,
566            Self::Num7 => 55,
567            Self::Num8 => 56,
568            Self::Num9 => 57,
569            Self::Colon => 58,
570            Self::Semicolon => 59,
571            Self::Less => 60,
572            Self::Equals => 61,
573            Self::Greater => 62,
574            Self::Question => 63,
575            Self::At => 64,
576            Self::LeftBracket => 91,
577            Self::Backslash => 92,
578            Self::RightBracket => 93,
579            Self::Caret => 94,
580            Self::Underscore => 95,
581            Self::Backquote => 96,
582            Self::A => 97,
583            Self::B => 98,
584            Self::C => 99,
585            Self::D => 100,
586            Self::E => 101,
587            Self::F => 102,
588            Self::G => 103,
589            Self::H => 104,
590            Self::I => 105,
591            Self::J => 106,
592            Self::K => 107,
593            Self::L => 108,
594            Self::M => 109,
595            Self::N => 110,
596            Self::O => 111,
597            Self::P => 112,
598            Self::Q => 113,
599            Self::R => 114,
600            Self::S => 115,
601            Self::T => 116,
602            Self::U => 117,
603            Self::V => 118,
604            Self::W => 119,
605            Self::X => 120,
606            Self::Y => 121,
607            Self::Z => 122,
608            Self::LeftBrace => 123,
609            Self::Bar => 124,
610            Self::RightBrace => 125,
611            Self::Tilde => 126,
612            Self::Delete => 127,
613            Self::Kp0 => 256,
614            Self::Kp1 => 257,
615            Self::Kp2 => 258,
616            Self::Kp3 => 259,
617            Self::Kp4 => 260,
618            Self::Kp5 => 261,
619            Self::Kp6 => 262,
620            Self::Kp7 => 263,
621            Self::Kp8 => 264,
622            Self::Kp9 => 265,
623            Self::KpPeriod => 266,
624            Self::KpDivide => 267,
625            Self::KpMultiply => 268,
626            Self::KpMinus => 269,
627            Self::KpPlus => 270,
628            Self::KpEnter => 271,
629            Self::KpEquals => 272,
630            Self::Up => 273,
631            Self::Down => 274,
632            Self::Right => 275,
633            Self::Left => 276,
634            Self::Insert => 277,
635            Self::Home => 278,
636            Self::End => 279,
637            Self::PageUp => 280,
638            Self::PageDown => 281,
639            Self::F1 => 282,
640            Self::F2 => 283,
641            Self::F3 => 284,
642            Self::F4 => 285,
643            Self::F5 => 286,
644            Self::F6 => 287,
645            Self::F7 => 288,
646            Self::F8 => 289,
647            Self::F9 => 290,
648            Self::F10 => 291,
649            Self::F11 => 292,
650            Self::F12 => 293,
651            Self::F13 => 294,
652            Self::F14 => 295,
653            Self::F15 => 296,
654            Self::NumLock => 300,
655            Self::CapsLock => 301,
656            Self::ScrollLock => 302,
657            Self::RightShift => 303,
658            Self::LeftShift => 304,
659            Self::RightCtrl => 305,
660            Self::LeftCtrl => 306,
661            Self::RightAlt => 307,
662            Self::LeftAlt => 308,
663            Self::RightMeta => 309,
664            Self::LeftMeta => 310,
665            Self::LeftSuper => 311,
666            Self::RightSuper => 312,
667            Self::Mode => 313,
668            Self::Compose => 314,
669            Self::Help => 315,
670            Self::Print => 316,
671            Self::SysReq => 317,
672            Self::Break => 318,
673            Self::Menu => 319,
674            Self::Power => 320,
675            Self::Euro => 321,
676            Self::Undo => 322,
677            Self::Oem102 => 323,
678            Self::BrowserBack => 324,
679            Self::BrowserForward => 325,
680            Self::BrowserRefresh => 326,
681            Self::BrowserStop => 327,
682            Self::BrowserSearch => 328,
683            Self::BrowserFavorites => 329,
684            Self::BrowserHome => 330,
685            Self::VolumeMute => 331,
686            Self::VolumeDown => 332,
687            Self::VolumeUp => 333,
688            Self::MediaNext => 334,
689            Self::MediaPrev => 335,
690            Self::MediaStop => 336,
691            Self::MediaPlayPause => 337,
692            Self::LaunchMail => 338,
693            Self::LaunchMedia => 339,
694            Self::LaunchApp1 => 340,
695            Self::LaunchApp2 => 341,
696            Self::Last => 342,
697            Self::UnknownKeycode(keycode) => keycode,
698        }
699    }
700}
701
702#[bitflags]
703#[repr(u16)]
704#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
705pub enum KeyboardModifier {
706    Shift = 0x01,
707    Ctrl = 0x02,
708    Alt = 0x04,
709    Meta = 0x08,
710    NumLock = 0x10,
711    CapsLock = 0x20,
712    ScrollLock = 0x40,
713}
714
715pub type KeyboardModifiers = BitFlags<KeyboardModifier>;
716
717#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
718pub struct KeyboardCharacter(u32);
719
720impl KeyboardCharacter {
721    pub const fn from_utf32(character: u32) -> Self {
722        Self(character)
723    }
724
725    pub const fn as_raw(self) -> u32 {
726        self.0
727    }
728
729    pub fn as_char(self) -> Option<char> {
730        char::from_u32(self.0)
731    }
732}
733
734#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
735pub struct KeyboardEvent {
736    pub down: bool,
737    pub key: KeyboardKey,
738    pub character: KeyboardCharacter,
739    pub modifiers: KeyboardModifiers,
740}
741
742impl KeyboardEvent {
743    pub const fn new(
744        down: bool,
745        key: KeyboardKey,
746        character: KeyboardCharacter,
747        modifiers: KeyboardModifiers,
748    ) -> Self {
749        Self {
750            down,
751            key,
752            character,
753            modifiers,
754        }
755    }
756
757    pub fn from_raw(down: bool, keycode: u32, character: u32, modifiers: u16) -> Self {
758        Self::new(
759            down,
760            KeyboardKey::from_raw(keycode),
761            KeyboardCharacter::from_utf32(character),
762            KeyboardModifiers::from_bits_truncate(modifiers),
763        )
764    }
765}
766
767#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
768pub struct InputPort(u32);
769
770impl InputPort {
771    pub const fn new(port: u32) -> Self {
772        Self(port)
773    }
774
775    pub const fn as_raw(self) -> u32 {
776        self.0
777    }
778}
779
780impl From<u32> for InputPort {
781    fn from(port: u32) -> Self {
782        Self::new(port)
783    }
784}
785
786#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
787pub struct InputDescriptorIndex(u32);
788
789impl InputDescriptorIndex {
790    pub const fn new(index: u32) -> Self {
791        Self(index)
792    }
793
794    pub const fn zero() -> Self {
795        Self(0)
796    }
797
798    pub const fn as_raw(self) -> u32 {
799        self.0
800    }
801}
802
803impl From<u32> for InputDescriptorIndex {
804    fn from(index: u32) -> Self {
805        Self::new(index)
806    }
807}
808
809impl From<AnalogStick> for InputDescriptorIndex {
810    fn from(stick: AnalogStick) -> Self {
811        Self::new(stick.as_raw())
812    }
813}
814
815impl From<PointerIndex> for InputDescriptorIndex {
816    fn from(index: PointerIndex) -> Self {
817        Self::new(index.as_raw())
818    }
819}
820
821#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
822pub struct InputDescriptorId(u32);
823
824impl InputDescriptorId {
825    pub const fn new(id: u32) -> Self {
826        Self(id)
827    }
828
829    pub const fn as_raw(self) -> u32 {
830        self.0
831    }
832}
833
834impl From<u32> for InputDescriptorId {
835    fn from(id: u32) -> Self {
836        Self::new(id)
837    }
838}
839
840impl From<JoypadButton> for InputDescriptorId {
841    fn from(button: JoypadButton) -> Self {
842        Self::new(button.as_raw())
843    }
844}
845
846impl From<AnalogAxis> for InputDescriptorId {
847    fn from(axis: AnalogAxis) -> Self {
848        Self::new(axis.as_raw())
849    }
850}
851
852impl From<MouseAxis> for InputDescriptorId {
853    fn from(axis: MouseAxis) -> Self {
854        Self::new(axis.as_raw())
855    }
856}
857
858impl From<MouseButton> for InputDescriptorId {
859    fn from(button: MouseButton) -> Self {
860        Self::new(button.as_raw())
861    }
862}
863
864impl From<MouseWheel> for InputDescriptorId {
865    fn from(wheel: MouseWheel) -> Self {
866        Self::new(wheel.as_raw())
867    }
868}
869
870impl From<PointerAxis> for InputDescriptorId {
871    fn from(axis: PointerAxis) -> Self {
872        Self::new(axis.as_raw())
873    }
874}
875
876impl From<LightgunAxis> for InputDescriptorId {
877    fn from(axis: LightgunAxis) -> Self {
878        Self::new(axis.as_raw())
879    }
880}
881
882impl From<LightgunButton> for InputDescriptorId {
883    fn from(button: LightgunButton) -> Self {
884        Self::new(button.as_raw())
885    }
886}
887
888#[derive(Clone, Debug, PartialEq, Eq)]
889pub struct InputDescriptor {
890    pub port: InputPort,
891    pub device: ControllerDevice,
892    pub index: InputDescriptorIndex,
893    pub id: InputDescriptorId,
894    pub description: String,
895}
896
897impl InputDescriptor {
898    pub fn new(
899        port: impl Into<InputPort>,
900        device: ControllerDevice,
901        index: impl Into<InputDescriptorIndex>,
902        id: impl Into<InputDescriptorId>,
903        description: impl Into<String>,
904    ) -> Self {
905        Self {
906            port: port.into(),
907            device,
908            index: index.into(),
909            id: id.into(),
910            description: description.into(),
911        }
912    }
913
914    pub fn joypad(
915        port: impl Into<InputPort>,
916        button: JoypadButton,
917        description: impl Into<String>,
918    ) -> Self {
919        Self::new(
920            port,
921            ControllerDevice::Joypad,
922            InputDescriptorIndex::zero(),
923            button,
924            description,
925        )
926    }
927
928    pub fn analog(
929        port: impl Into<InputPort>,
930        stick: AnalogStick,
931        axis: AnalogAxis,
932        description: impl Into<String>,
933    ) -> Self {
934        Self::new(port, ControllerDevice::Analog, stick, axis, description)
935    }
936
937    pub fn mouse(
938        port: impl Into<InputPort>,
939        id: impl Into<InputDescriptorId>,
940        description: impl Into<String>,
941    ) -> Self {
942        Self::new(
943            port,
944            ControllerDevice::Mouse,
945            InputDescriptorIndex::zero(),
946            id,
947            description,
948        )
949    }
950
951    pub fn pointer(
952        port: impl Into<InputPort>,
953        index: impl Into<InputDescriptorIndex>,
954        id: impl Into<InputDescriptorId>,
955        description: impl Into<String>,
956    ) -> Self {
957        Self::new(port, ControllerDevice::Pointer, index, id, description)
958    }
959
960    pub fn lightgun(
961        port: impl Into<InputPort>,
962        id: impl Into<InputDescriptorId>,
963        description: impl Into<String>,
964    ) -> Self {
965        Self::new(
966            port,
967            ControllerDevice::Lightgun,
968            InputDescriptorIndex::zero(),
969            id,
970            description,
971        )
972    }
973}
974
975#[derive(Clone, Debug, PartialEq, Eq)]
976pub struct ControllerDescription {
977    pub description: String,
978    pub device: ControllerDevice,
979}
980
981impl ControllerDescription {
982    pub fn new(description: impl Into<String>, device: ControllerDevice) -> Self {
983        Self {
984            description: description.into(),
985            device,
986        }
987    }
988}
989
990#[derive(Clone, Debug, Default, PartialEq, Eq)]
991pub struct ControllerInfo {
992    pub types: Vec<ControllerDescription>,
993}
994
995impl ControllerInfo {
996    pub fn new(types: impl Into<Vec<ControllerDescription>>) -> Self {
997        Self {
998            types: types.into(),
999        }
1000    }
1001}
1002
1003#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
1004pub struct PointerIndex(u32);
1005
1006impl PointerIndex {
1007    pub const fn new(index: u32) -> Self {
1008        Self(index)
1009    }
1010
1011    pub const fn as_raw(self) -> u32 {
1012        self.0
1013    }
1014}
1015
1016impl From<u32> for PointerIndex {
1017    fn from(index: u32) -> Self {
1018        Self::new(index)
1019    }
1020}
1021
1022#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1023pub enum ControllerDevice {
1024    None,
1025    Joypad,
1026    Mouse,
1027    Keyboard,
1028    Lightgun,
1029    Analog,
1030    Pointer,
1031    Subclass(ControllerDeviceSubclass),
1032    Unknown(u32),
1033}
1034
1035impl ControllerDevice {
1036    pub const fn from_raw(device: u32) -> Self {
1037        let base = device & RETRO_DEVICE_MASK;
1038        if device != base {
1039            return match Self::from_base_raw(base) {
1040                Some(_) => Self::Subclass(ControllerDeviceSubclass(device)),
1041                None => Self::Unknown(device),
1042            };
1043        }
1044        match Self::from_base_raw(device) {
1045            Some(device) => device,
1046            None => Self::Unknown(device),
1047        }
1048    }
1049
1050    const fn from_base_raw(device: u32) -> Option<Self> {
1051        match device {
1052            RETRO_DEVICE_NONE => Some(Self::None),
1053            RETRO_DEVICE_JOYPAD => Some(Self::Joypad),
1054            RETRO_DEVICE_MOUSE => Some(Self::Mouse),
1055            RETRO_DEVICE_KEYBOARD => Some(Self::Keyboard),
1056            RETRO_DEVICE_LIGHTGUN => Some(Self::Lightgun),
1057            RETRO_DEVICE_ANALOG => Some(Self::Analog),
1058            RETRO_DEVICE_POINTER => Some(Self::Pointer),
1059            _ => None,
1060        }
1061    }
1062
1063    pub const fn as_raw(self) -> u32 {
1064        match self {
1065            Self::None => RETRO_DEVICE_NONE,
1066            Self::Joypad => RETRO_DEVICE_JOYPAD,
1067            Self::Mouse => RETRO_DEVICE_MOUSE,
1068            Self::Keyboard => RETRO_DEVICE_KEYBOARD,
1069            Self::Lightgun => RETRO_DEVICE_LIGHTGUN,
1070            Self::Analog => RETRO_DEVICE_ANALOG,
1071            Self::Pointer => RETRO_DEVICE_POINTER,
1072            Self::Subclass(device) => device.as_raw(),
1073            Self::Unknown(device) => device,
1074        }
1075    }
1076
1077    pub const fn base_device(self) -> Self {
1078        match self {
1079            Self::Subclass(device) => device.base_device(),
1080            Self::Unknown(device) => match Self::from_base_raw(device & RETRO_DEVICE_MASK) {
1081                Some(base) => base,
1082                None => Self::Unknown(device & RETRO_DEVICE_MASK),
1083            },
1084            base => base,
1085        }
1086    }
1087}
1088
1089#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1090pub struct ControllerDeviceSubclass(u32);
1091
1092impl ControllerDeviceSubclass {
1093    pub const fn new(base: ControllerDevice, id: u32) -> Option<Self> {
1094        let base = base.base_device().as_raw();
1095        if base > RETRO_DEVICE_MASK {
1096            return None;
1097        }
1098        match id.checked_add(1) {
1099            Some(id) if id <= (u32::MAX >> RETRO_DEVICE_TYPE_SHIFT) => {
1100                Some(Self((id << RETRO_DEVICE_TYPE_SHIFT) | base))
1101            }
1102            None => None,
1103            _ => None,
1104        }
1105    }
1106
1107    pub const fn from_raw(device: u32) -> Option<Self> {
1108        let base = device & RETRO_DEVICE_MASK;
1109        if device == base || ControllerDevice::from_base_raw(base).is_none() {
1110            return None;
1111        }
1112        Some(Self(device))
1113    }
1114
1115    pub const fn as_raw(self) -> u32 {
1116        self.0
1117    }
1118
1119    pub const fn base_device(self) -> ControllerDevice {
1120        match ControllerDevice::from_base_raw(self.0 & RETRO_DEVICE_MASK) {
1121            Some(device) => device,
1122            None => ControllerDevice::Unknown(self.0 & RETRO_DEVICE_MASK),
1123        }
1124    }
1125
1126    pub const fn id(self) -> u32 {
1127        (self.0 >> RETRO_DEVICE_TYPE_SHIFT) - 1
1128    }
1129}
1130
1131#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1132pub enum JoypadButton {
1133    B,
1134    Y,
1135    Select,
1136    Start,
1137    Up,
1138    Down,
1139    Left,
1140    Right,
1141    A,
1142    X,
1143    L,
1144    R,
1145    L2,
1146    R2,
1147    L3,
1148    R3,
1149}
1150
1151impl JoypadButton {
1152    pub(crate) const fn as_raw(self) -> u32 {
1153        match self {
1154            Self::B => RETRO_DEVICE_ID_JOYPAD_B,
1155            Self::Y => RETRO_DEVICE_ID_JOYPAD_Y,
1156            Self::Select => RETRO_DEVICE_ID_JOYPAD_SELECT,
1157            Self::Start => RETRO_DEVICE_ID_JOYPAD_START,
1158            Self::Up => RETRO_DEVICE_ID_JOYPAD_UP,
1159            Self::Down => RETRO_DEVICE_ID_JOYPAD_DOWN,
1160            Self::Left => RETRO_DEVICE_ID_JOYPAD_LEFT,
1161            Self::Right => RETRO_DEVICE_ID_JOYPAD_RIGHT,
1162            Self::A => RETRO_DEVICE_ID_JOYPAD_A,
1163            Self::X => RETRO_DEVICE_ID_JOYPAD_X,
1164            Self::L => RETRO_DEVICE_ID_JOYPAD_L,
1165            Self::R => RETRO_DEVICE_ID_JOYPAD_R,
1166            Self::L2 => RETRO_DEVICE_ID_JOYPAD_L2,
1167            Self::R2 => RETRO_DEVICE_ID_JOYPAD_R2,
1168            Self::L3 => RETRO_DEVICE_ID_JOYPAD_L3,
1169            Self::R3 => RETRO_DEVICE_ID_JOYPAD_R3,
1170        }
1171    }
1172
1173    const fn mask(self) -> u16 {
1174        1u16 << self.as_raw()
1175    }
1176}
1177
1178#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
1179pub struct JoypadButtonSet(u16);
1180
1181impl JoypadButtonSet {
1182    pub const fn from_raw_bits(bits: u16) -> Self {
1183        Self(bits)
1184    }
1185
1186    pub const fn raw_bits(self) -> u16 {
1187        self.0
1188    }
1189
1190    pub const fn contains(self, button: JoypadButton) -> bool {
1191        self.0 & button.mask() != 0
1192    }
1193}
1194
1195#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1196pub enum AnalogStick {
1197    Left,
1198    Right,
1199}
1200
1201impl AnalogStick {
1202    pub(crate) const fn as_raw(self) -> u32 {
1203        match self {
1204            Self::Left => RETRO_DEVICE_INDEX_ANALOG_LEFT,
1205            Self::Right => RETRO_DEVICE_INDEX_ANALOG_RIGHT,
1206        }
1207    }
1208}
1209
1210#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1211pub enum AnalogAxis {
1212    X,
1213    Y,
1214}
1215
1216impl AnalogAxis {
1217    pub(crate) const fn as_raw(self) -> u32 {
1218        match self {
1219            Self::X => RETRO_DEVICE_ID_ANALOG_X,
1220            Self::Y => RETRO_DEVICE_ID_ANALOG_Y,
1221        }
1222    }
1223}
1224
1225#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1226pub enum MouseAxis {
1227    X,
1228    Y,
1229}
1230
1231impl MouseAxis {
1232    pub(crate) const fn as_raw(self) -> u32 {
1233        match self {
1234            Self::X => RETRO_DEVICE_ID_MOUSE_X,
1235            Self::Y => RETRO_DEVICE_ID_MOUSE_Y,
1236        }
1237    }
1238}
1239
1240#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1241pub enum MouseButton {
1242    Left,
1243    Right,
1244    Middle,
1245    Button4,
1246    Button5,
1247}
1248
1249impl MouseButton {
1250    pub(crate) const fn as_raw(self) -> u32 {
1251        match self {
1252            Self::Left => RETRO_DEVICE_ID_MOUSE_LEFT,
1253            Self::Right => RETRO_DEVICE_ID_MOUSE_RIGHT,
1254            Self::Middle => RETRO_DEVICE_ID_MOUSE_MIDDLE,
1255            Self::Button4 => RETRO_DEVICE_ID_MOUSE_BUTTON_4,
1256            Self::Button5 => RETRO_DEVICE_ID_MOUSE_BUTTON_5,
1257        }
1258    }
1259}
1260
1261#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1262pub enum MouseWheel {
1263    Up,
1264    Down,
1265    Left,
1266    Right,
1267}
1268
1269impl MouseWheel {
1270    pub(crate) const fn as_raw(self) -> u32 {
1271        match self {
1272            Self::Up => RETRO_DEVICE_ID_MOUSE_WHEELUP,
1273            Self::Down => RETRO_DEVICE_ID_MOUSE_WHEELDOWN,
1274            Self::Left => RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN,
1275            Self::Right => RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP,
1276        }
1277    }
1278}
1279
1280#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1281pub enum PointerAxis {
1282    X,
1283    Y,
1284}
1285
1286impl PointerAxis {
1287    pub(crate) const fn as_raw(self) -> u32 {
1288        match self {
1289            Self::X => RETRO_DEVICE_ID_POINTER_X,
1290            Self::Y => RETRO_DEVICE_ID_POINTER_Y,
1291        }
1292    }
1293}
1294
1295#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1296pub enum LightgunAxis {
1297    #[deprecated(note = "use ScreenX for the modern absolute lightgun X coordinate")]
1298    RelativeX,
1299    #[deprecated(note = "use ScreenY for the modern absolute lightgun Y coordinate")]
1300    RelativeY,
1301    ScreenX,
1302    ScreenY,
1303}
1304
1305impl LightgunAxis {
1306    #[allow(deprecated)]
1307    pub(crate) const fn as_raw(self) -> u32 {
1308        match self {
1309            Self::RelativeX => RETRO_DEVICE_ID_LIGHTGUN_X,
1310            Self::RelativeY => RETRO_DEVICE_ID_LIGHTGUN_Y,
1311            Self::ScreenX => RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X,
1312            Self::ScreenY => RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y,
1313        }
1314    }
1315}
1316
1317#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1318pub enum LightgunButton {
1319    Trigger,
1320    Reload,
1321    AuxA,
1322    AuxB,
1323    AuxC,
1324    Start,
1325    Select,
1326    DpadUp,
1327    DpadDown,
1328    DpadLeft,
1329    DpadRight,
1330    #[deprecated(note = "use AuxA instead")]
1331    Cursor,
1332    #[deprecated(note = "use AuxB instead")]
1333    Turbo,
1334    #[deprecated(note = "use Start instead")]
1335    Pause,
1336}
1337
1338impl LightgunButton {
1339    #[allow(deprecated)]
1340    pub(crate) const fn as_raw(self) -> u32 {
1341        match self {
1342            Self::Trigger => RETRO_DEVICE_ID_LIGHTGUN_TRIGGER,
1343            Self::Reload => RETRO_DEVICE_ID_LIGHTGUN_RELOAD,
1344            Self::AuxA => RETRO_DEVICE_ID_LIGHTGUN_AUX_A,
1345            Self::AuxB => RETRO_DEVICE_ID_LIGHTGUN_AUX_B,
1346            Self::AuxC => RETRO_DEVICE_ID_LIGHTGUN_AUX_C,
1347            Self::Start => RETRO_DEVICE_ID_LIGHTGUN_START,
1348            Self::Select => RETRO_DEVICE_ID_LIGHTGUN_SELECT,
1349            Self::DpadUp => RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP,
1350            Self::DpadDown => RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN,
1351            Self::DpadLeft => RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT,
1352            Self::DpadRight => RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT,
1353            Self::Cursor => crate::raw::RETRO_DEVICE_ID_LIGHTGUN_CURSOR,
1354            Self::Turbo => crate::raw::RETRO_DEVICE_ID_LIGHTGUN_TURBO,
1355            Self::Pause => crate::raw::RETRO_DEVICE_ID_LIGHTGUN_PAUSE,
1356        }
1357    }
1358}
1359
1360pub(crate) const fn joypad_mask_query_id() -> u32 {
1361    RETRO_DEVICE_ID_JOYPAD_MASK
1362}
1363
1364pub(crate) const fn analog_button_index() -> u32 {
1365    RETRO_DEVICE_INDEX_ANALOG_BUTTON
1366}
1367
1368pub(crate) const fn pointer_pressed_id() -> u32 {
1369    RETRO_DEVICE_ID_POINTER_PRESSED
1370}
1371
1372pub(crate) const fn pointer_count_id() -> u32 {
1373    RETRO_DEVICE_ID_POINTER_COUNT
1374}
1375
1376pub(crate) const fn pointer_is_offscreen_id() -> u32 {
1377    RETRO_DEVICE_ID_POINTER_IS_OFFSCREEN
1378}
1379
1380pub(crate) const fn lightgun_is_offscreen_id() -> u32 {
1381    RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN
1382}
1383
1384#[cfg(test)]
1385mod tests {
1386    use super::*;
1387
1388    #[test]
1389    fn controller_devices_round_trip_to_libretro_ids() {
1390        let devices = [
1391            ControllerDevice::None,
1392            ControllerDevice::Joypad,
1393            ControllerDevice::Mouse,
1394            ControllerDevice::Keyboard,
1395            ControllerDevice::Lightgun,
1396            ControllerDevice::Analog,
1397            ControllerDevice::Pointer,
1398        ];
1399
1400        for device in devices {
1401            assert_eq!(ControllerDevice::from_raw(device.as_raw()), device);
1402        }
1403    }
1404
1405    #[test]
1406    fn led_state_encodes_bool_like_values() {
1407        assert_eq!(LedState::Off.as_raw(), 0);
1408        assert_eq!(LedState::On.as_raw(), 1);
1409        assert_eq!(LedIndex::new(2).as_raw(), 2);
1410    }
1411
1412    #[test]
1413    fn empty_led_interface_reports_unavailable() {
1414        let leds = LedInterface::default();
1415
1416        assert!(!leds.is_available());
1417        assert!(!leds.set_state(0, LedState::On));
1418    }
1419
1420    #[test]
1421    fn rumble_values_encode_libretro_state() {
1422        assert_eq!(
1423            RumbleEffect::Strong.as_raw(),
1424            crate::raw::retro_rumble_effect::Strong
1425        );
1426        assert_eq!(
1427            RumbleEffect::Weak.as_raw(),
1428            crate::raw::retro_rumble_effect::Weak
1429        );
1430        assert_eq!(RumbleStrength::off().as_raw(), 0);
1431        assert_eq!(RumbleStrength::max().as_raw(), u16::MAX);
1432        assert_eq!(RumbleStrength::new(123).as_raw(), 123);
1433    }
1434
1435    #[test]
1436    fn empty_rumble_interface_reports_unavailable() {
1437        let rumble = RumbleInterface::default();
1438
1439        assert!(!rumble.is_available());
1440        assert!(!rumble.set_state(0, RumbleEffect::Strong, RumbleStrength::max()));
1441    }
1442
1443    #[test]
1444    fn unknown_controller_device_preserves_original_id() {
1445        assert_eq!(
1446            ControllerDevice::from_raw(123),
1447            ControllerDevice::Unknown(123)
1448        );
1449        assert_eq!(ControllerDevice::Unknown(123).as_raw(), 123);
1450    }
1451
1452    #[test]
1453    fn controller_device_subclasses_preserve_base_device_and_id() {
1454        let lightgun_scope = ControllerDeviceSubclass::new(ControllerDevice::Lightgun, 1)
1455            .expect("lightgun subclass should be representable");
1456
1457        assert_eq!(lightgun_scope.base_device(), ControllerDevice::Lightgun);
1458        assert_eq!(lightgun_scope.id(), 1);
1459        assert_eq!(
1460            ControllerDevice::from_raw(lightgun_scope.as_raw()),
1461            ControllerDevice::Subclass(lightgun_scope)
1462        );
1463        assert_eq!(
1464            ControllerDevice::Subclass(lightgun_scope).base_device(),
1465            ControllerDevice::Lightgun
1466        );
1467        assert_eq!(
1468            lightgun_scope.as_raw(),
1469            ((1 + 1) << crate::raw::RETRO_DEVICE_TYPE_SHIFT) | crate::raw::RETRO_DEVICE_LIGHTGUN
1470        );
1471        assert_eq!(
1472            ControllerDeviceSubclass::from_raw(ControllerDevice::Mouse.as_raw()),
1473            None
1474        );
1475    }
1476
1477    #[test]
1478    fn input_device_capabilities_map_to_controller_devices() {
1479        assert_eq!(
1480            InputDeviceCapability::Joypad.device(),
1481            ControllerDevice::Joypad
1482        );
1483        assert_eq!(
1484            InputDeviceCapability::Mouse.device(),
1485            ControllerDevice::Mouse
1486        );
1487        assert_eq!(
1488            InputDeviceCapability::Keyboard.device(),
1489            ControllerDevice::Keyboard
1490        );
1491        assert_eq!(
1492            InputDeviceCapability::Lightgun.device(),
1493            ControllerDevice::Lightgun
1494        );
1495        assert_eq!(
1496            InputDeviceCapability::Analog.device(),
1497            ControllerDevice::Analog
1498        );
1499        assert_eq!(
1500            InputDeviceCapability::Pointer.device(),
1501            ControllerDevice::Pointer
1502        );
1503    }
1504
1505    #[test]
1506    fn keyboard_keys_round_trip_and_preserve_unknown_keycodes() {
1507        assert_eq!(KeyboardKey::from_raw(97), KeyboardKey::A);
1508        assert_eq!(KeyboardKey::A.as_raw(), 97);
1509        assert_eq!(KeyboardKey::from_raw(282), KeyboardKey::F1);
1510        assert_eq!(KeyboardKey::F1.as_raw(), 282);
1511        assert_eq!(
1512            KeyboardKey::from_raw(65_535),
1513            KeyboardKey::UnknownKeycode(65_535)
1514        );
1515        assert_eq!(KeyboardKey::UnknownKeycode(65_535).as_raw(), 65_535);
1516    }
1517
1518    #[test]
1519    fn keyboard_event_preserves_text_and_modifiers() {
1520        let event = KeyboardEvent::from_raw(
1521            true,
1522            KeyboardKey::Return.as_raw(),
1523            0x00e9,
1524            (KeyboardModifiers::from(KeyboardModifier::Shift) | KeyboardModifier::Ctrl).bits(),
1525        );
1526
1527        assert!(event.down);
1528        assert_eq!(event.key, KeyboardKey::Return);
1529        assert_eq!(event.character.as_char(), char::from_u32(0x00e9));
1530        assert!(event.modifiers.contains(KeyboardModifier::Shift));
1531        assert!(event.modifiers.contains(KeyboardModifier::Ctrl));
1532        assert!(!event.modifiers.contains(KeyboardModifier::Alt));
1533    }
1534
1535    #[test]
1536    fn input_descriptor_builders_use_typed_device_ids() {
1537        let joypad = InputDescriptor::joypad(0, JoypadButton::A, "Jump");
1538        assert_eq!(joypad.port, InputPort::new(0));
1539        assert_eq!(joypad.device, ControllerDevice::Joypad);
1540        assert_eq!(joypad.index, InputDescriptorIndex::zero());
1541        assert_eq!(joypad.id, InputDescriptorId::from(JoypadButton::A));
1542
1543        let analog = InputDescriptor::analog(1, AnalogStick::Right, AnalogAxis::Y, "Look up");
1544        assert_eq!(analog.port, InputPort::new(1));
1545        assert_eq!(analog.device, ControllerDevice::Analog);
1546        assert_eq!(analog.index, InputDescriptorIndex::from(AnalogStick::Right));
1547        assert_eq!(analog.id, InputDescriptorId::from(AnalogAxis::Y));
1548    }
1549
1550    #[test]
1551    fn joypad_button_set_uses_libretro_mask_bits() {
1552        let buttons =
1553            JoypadButtonSet::from_raw_bits(JoypadButton::A.mask() | JoypadButton::L.mask());
1554
1555        assert!(buttons.contains(JoypadButton::A));
1556        assert!(buttons.contains(JoypadButton::L));
1557        assert!(!buttons.contains(JoypadButton::B));
1558    }
1559
1560    #[test]
1561    #[allow(deprecated)]
1562    fn deprecated_lightgun_aliases_map_to_their_modern_buttons() {
1563        assert_eq!(
1564            crate::raw::RETRO_DEVICE_ID_LIGHTGUN_CURSOR,
1565            crate::raw::RETRO_DEVICE_ID_LIGHTGUN_AUX_A
1566        );
1567        assert_eq!(
1568            crate::raw::RETRO_DEVICE_ID_LIGHTGUN_TURBO,
1569            crate::raw::RETRO_DEVICE_ID_LIGHTGUN_AUX_B
1570        );
1571        assert_eq!(
1572            LightgunButton::AuxA.as_raw(),
1573            crate::raw::RETRO_DEVICE_ID_LIGHTGUN_CURSOR
1574        );
1575        assert_eq!(
1576            LightgunButton::Cursor.as_raw(),
1577            LightgunButton::AuxA.as_raw()
1578        );
1579        assert_eq!(
1580            LightgunButton::Turbo.as_raw(),
1581            LightgunButton::AuxB.as_raw()
1582        );
1583        assert_eq!(
1584            LightgunButton::Pause.as_raw(),
1585            crate::raw::RETRO_DEVICE_ID_LIGHTGUN_PAUSE
1586        );
1587    }
1588
1589    #[test]
1590    #[allow(deprecated)]
1591    fn deprecated_lightgun_relative_axes_are_typed() {
1592        assert_eq!(
1593            LightgunAxis::RelativeX.as_raw(),
1594            crate::raw::RETRO_DEVICE_ID_LIGHTGUN_X
1595        );
1596        assert_eq!(
1597            LightgunAxis::RelativeY.as_raw(),
1598            crate::raw::RETRO_DEVICE_ID_LIGHTGUN_Y
1599        );
1600    }
1601}