1use 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 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 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}