usbd_human_interface_device/device/
keyboard.rs

1//!HID keyboards
2
3use crate::page::Keyboard;
4use crate::usb_class::prelude::*;
5use fugit::ExtU32;
6use packed_struct::prelude::*;
7#[allow(clippy::wildcard_imports)]
8use usb_device::class_prelude::*;
9use usb_device::UsbError;
10
11/// Interface implementing the HID boot keyboard specification
12///
13/// **Note:** This is a managed interfaces that support HID idle, [`UsbHidClass::tick()`] must be called every 1ms.
14pub struct BootKeyboard<'a, B: UsbBus> {
15    interface: ManagedIdleInterface<'a, B, BootKeyboardReport, InBytes8, OutBytes8>,
16}
17
18impl<B> BootKeyboard<'_, B>
19where
20    B: UsbBus,
21{
22    pub fn write_report<K: IntoIterator<Item = Keyboard>>(
23        &mut self,
24        keys: K,
25    ) -> Result<(), UsbHidError> {
26        self.interface.write_report(&BootKeyboardReport::new(keys))
27    }
28
29    pub fn read_report(&mut self) -> usb_device::Result<KeyboardLedsReport> {
30        let data = &mut [0];
31        match self.interface.read_report(data) {
32            Err(e) => Err(e),
33            Ok(_) => match KeyboardLedsReport::unpack(data) {
34                Ok(r) => Ok(r),
35                Err(_) => Err(UsbError::ParseError),
36            },
37        }
38    }
39}
40
41impl<'a, B> DeviceClass<'a> for BootKeyboard<'a, B>
42where
43    B: UsbBus,
44{
45    type I = Interface<'a, B, InBytes8, OutBytes8, ReportSingle>;
46
47    fn interface(&mut self) -> &mut Self::I {
48        self.interface.interface()
49    }
50
51    fn reset(&mut self) {
52        self.interface.reset();
53    }
54
55    fn tick(&mut self) -> Result<(), UsbHidError> {
56        self.interface.tick()
57    }
58}
59
60pub struct BootKeyboardConfig<'a> {
61    interface: ManagedIdleInterfaceConfig<'a, BootKeyboardReport, InBytes8, OutBytes8>,
62}
63
64impl Default for BootKeyboardConfig<'_> {
65    fn default() -> Self {
66        Self::new(ManagedIdleInterfaceConfig::new(
67            unwrap!(unwrap!(unwrap!(unwrap!(InterfaceBuilder::new(
68                BOOT_KEYBOARD_REPORT_DESCRIPTOR
69            ))
70            .boot_device(InterfaceProtocol::Keyboard)
71            .description("Keyboard")
72            .idle_default(500.millis()))
73            .in_endpoint(10.millis()))
74            //.without_out_endpoint()
75            //Shouldn't require a dedicated out endpoint, but leds are flaky without it
76            .with_out_endpoint(100.millis()))
77            .build(),
78        ))
79    }
80}
81
82impl<'a> BootKeyboardConfig<'a> {
83    #[must_use]
84    pub fn new(
85        interface: ManagedIdleInterfaceConfig<'a, BootKeyboardReport, InBytes8, OutBytes8>,
86    ) -> Self {
87        Self { interface }
88    }
89}
90
91impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for BootKeyboardConfig<'a> {
92    type Allocated = BootKeyboard<'a, B>;
93
94    fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
95        Self::Allocated {
96            interface: self.interface.allocate(usb_alloc),
97        }
98    }
99}
100
101/// Report indicating the currently lit keyboard LEDs
102#[cfg_attr(feature = "defmt", derive(defmt::Format))]
103#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, PackedStruct)]
104#[packed_struct(endian = "lsb", bit_numbering = "lsb0", size_bytes = "1")]
105pub struct KeyboardLedsReport {
106    #[packed_field(bits = "0")]
107    pub num_lock: bool,
108    #[packed_field(bits = "1")]
109    pub caps_lock: bool,
110    #[packed_field(bits = "2")]
111    pub scroll_lock: bool,
112    #[packed_field(bits = "3")]
113    pub compose: bool,
114    #[packed_field(bits = "4")]
115    pub kana: bool,
116}
117
118/// Report implementing the HID boot keyboard specification
119#[cfg_attr(feature = "defmt", derive(defmt::Format))]
120#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)]
121#[packed_struct(endian = "lsb", bit_numbering = "msb0", size_bytes = "8")]
122pub struct BootKeyboardReport {
123    #[packed_field(bits = "0")]
124    pub right_gui: bool,
125    #[packed_field(bits = "1")]
126    pub right_alt: bool,
127    #[packed_field(bits = "2")]
128    pub right_shift: bool,
129    #[packed_field(bits = "3")]
130    pub right_ctrl: bool,
131    #[packed_field(bits = "4")]
132    pub left_gui: bool,
133    #[packed_field(bits = "5")]
134    pub left_alt: bool,
135    #[packed_field(bits = "6")]
136    pub left_shift: bool,
137    #[packed_field(bits = "7")]
138    pub left_ctrl: bool,
139    #[packed_field(bytes = "2..8", ty = "enum", element_size_bytes = "1")]
140    pub keys: [Keyboard; 6],
141}
142
143impl BootKeyboardReport {
144    pub fn new<K: IntoIterator<Item = Keyboard>>(keys: K) -> Self {
145        let mut report = Self::default();
146
147        let mut error = false;
148        let mut i = 0;
149        for k in keys {
150            match k {
151                Keyboard::LeftControl => {
152                    report.left_ctrl = true;
153                }
154                Keyboard::LeftShift => {
155                    report.left_shift = true;
156                }
157                Keyboard::LeftAlt => {
158                    report.left_alt = true;
159                }
160                Keyboard::LeftGUI => {
161                    report.left_gui = true;
162                }
163                Keyboard::RightControl => {
164                    report.right_ctrl = true;
165                }
166                Keyboard::RightShift => {
167                    report.right_shift = true;
168                }
169                Keyboard::RightAlt => {
170                    report.right_alt = true;
171                }
172                Keyboard::RightGUI => {
173                    report.right_gui = true;
174                }
175                Keyboard::NoEventIndicated => {}
176                Keyboard::ErrorRollOver | Keyboard::POSTFail | Keyboard::ErrorUndefine => {
177                    if !error {
178                        error = true;
179                        i = report.keys.len();
180                        report.keys.fill(k);
181                    }
182                }
183                _ => {
184                    if error {
185                        continue;
186                    }
187
188                    if i < report.keys.len() {
189                        report.keys[i] = k;
190                        i += 1;
191                    } else {
192                        error = true;
193                        i = report.keys.len();
194                        report.keys.fill(Keyboard::ErrorRollOver);
195                    }
196                }
197            }
198        }
199        report
200    }
201}
202
203/// HID Keyboard report descriptor conforming to the Boot specification
204///
205/// This aims to be compatible with BIOS and other reduced functionality USB hosts
206///
207/// This is defined in Appendix B.1 & E.6 of [Device Class Definition for Human
208/// Interface Devices (Hid) Version 1.11](<https://www.usb.org/sites/default/files/hid1_11.pdf>)
209#[rustfmt::skip]
210pub const BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[
211    0x05, 0x01, // Usage Page (Generic Desktop),
212    0x09, 0x06, // Usage (Keyboard),
213    0xA1, 0x01, // Collection (Application),
214    0x75, 0x01, //     Report Size (1),
215    0x95, 0x08, //     Report Count (8),
216    0x05, 0x07, //     Usage Page (Key Codes),
217    0x19, 0xE0, //     Usage Minimum (224),
218    0x29, 0xE7, //     Usage Maximum (231),
219    0x15, 0x00, //     Logical Minimum (0),
220    0x25, 0x01, //     Logical Maximum (1),
221    0x81, 0x02, //     Input (Data, Variable, Absolute), ;Modifier byte
222    0x95, 0x01, //     Report Count (1),
223    0x75, 0x08, //     Report Size (8),
224    0x81, 0x01, //     Input (Constant), ;Reserved byte
225    0x95, 0x05, //     Report Count (5),
226    0x75, 0x01, //     Report Size (1),
227    0x05, 0x08, //     Usage Page (LEDs),
228    0x19, 0x01, //     Usage Minimum (1),
229    0x29, 0x05, //     Usage Maximum (5),
230    0x91, 0x02, //     Output (Data, Variable, Absolute), ;LED report
231    0x95, 0x01, //     Report Count (1),
232    0x75, 0x03, //     Report Size (3),
233    0x91, 0x01, //     Output (Constant), ;LED report padding
234    0x95, 0x06, //     Report Count (6),
235    0x75, 0x08, //     Report Size (8),
236    0x15, 0x00, //     Logical Minimum (0),
237    0x26, 0xFF, 0x00, //     Logical Maximum(255),
238    0x05, 0x07, //     Usage Page (Key Codes),
239    0x19, 0x00, //     Usage Minimum (0),
240    0x2A, 0xFF, 0x00, //     Usage Maximum (255),
241    0x81, 0x00, //     Input (Data, Array),
242    0xC0, // End Collection
243];
244
245/// HID Keyboard report descriptor implementing an NKRO keyboard as a bitmap appended to the boot
246/// keyboard report format.
247///
248/// This is compatible with the HID boot specification but key data must be duplicated across both
249/// the array and bitmap sections of the report
250//25 bytes
251//byte 0 - modifiers
252//byte 1 - reserved 0s
253//byte 2-7 - array of key codes - used for boot support
254//byte 9-24 - bit array of pressed keys
255#[rustfmt::skip]
256pub const NKRO_BOOT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[
257    0x05, 0x01,                     // Usage Page (Generic Desktop),
258    0x09, 0x06,                     // Usage (Keyboard),
259    0xA1, 0x01,                     // Collection (Application),
260    // bitmap of modifiers
261    0x75, 0x01,                     //   Report Size (1),
262    0x95, 0x08,                     //   Report Count (8),
263    0x05, 0x07,                     //   Usage Page (Key Codes),
264    0x19, 0xE0,                     //   Usage Minimum (224),
265    0x29, 0xE7,                     //   Usage Maximum (231),
266    0x15, 0x00,                     //   Logical Minimum (0),
267    0x25, 0x01,                     //   Logical Maximum (1),
268    0x81, 0x02,                     //   Input (Data, Variable, Absolute), ;Modifier byte
269    // 7 bytes of padding
270    0x75, 0x38,                     //   Report Size (0x38),
271    0x95, 0x01,                     //   Report Count (1),
272    0x81, 0x01,                     //   Input (Constant), ;Reserved byte
273    // LED output report
274    0x95, 0x05,                     //   Report Count (5),
275    0x75, 0x01,                     //   Report Size (1),
276    0x05, 0x08,                     //   Usage Page (LEDs),
277    0x19, 0x01,                     //   Usage Minimum (1),
278    0x29, 0x05,                     //   Usage Maximum (5),
279    0x91, 0x02,                     //   Output (Data, Variable, Absolute),
280    0x95, 0x01,                     //   Report Count (1),
281    0x75, 0x03,                     //   Report Size (3),
282    0x91, 0x03,                     //   Output (Constant),
283    // bitmap of keys
284    0x95, 0x88,                     //   Report Count () - (REPORT_BYTES-1)*8
285    0x75, 0x01,                     //   Report Size (1),
286    0x15, 0x00,                     //   Logical Minimum (0),
287    0x25, 0x01,                     //   Logical Maximum(1),
288    0x05, 0x07,                     //   Usage Page (Key Codes),
289    0x19, 0x00,                     //   Usage Minimum (0),
290    0x29, 0x87,                     //   Usage Maximum (), - (REPORT_BYTES-1)*8-1
291    0x81, 0x02,                     //   Input (Data, Variable, Absolute),
292    0xc0                            // End Collection
293];
294
295/// Report implementing an NKRO keyboard as a bitmap appended to the boot
296/// keyboard report format
297///
298/// This is compatible with the HID boot specification but key data must be duplicated across both
299/// the [`NKROBootKeyboardReport::boot_keys`] and [`NKROBootKeyboardReport::nkro_keys`] fields
300#[cfg_attr(feature = "defmt", derive(defmt::Format))]
301#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)]
302#[packed_struct(endian = "lsb", bit_numbering = "msb0", size_bytes = "25")]
303pub struct NKROBootKeyboardReport {
304    #[packed_field(bits = "0")]
305    pub right_gui: bool,
306    #[packed_field(bits = "1")]
307    pub right_alt: bool,
308    #[packed_field(bits = "2")]
309    pub right_shift: bool,
310    #[packed_field(bits = "3")]
311    pub right_ctrl: bool,
312    #[packed_field(bits = "4")]
313    pub left_gui: bool,
314    #[packed_field(bits = "5")]
315    pub left_alt: bool,
316    #[packed_field(bits = "6")]
317    pub left_shift: bool,
318    #[packed_field(bits = "7")]
319    pub left_ctrl: bool,
320    #[packed_field(bytes = "2..8", ty = "enum", element_size_bytes = "1")]
321    pub boot_keys: [Keyboard; 6],
322    //The usb lsb/lsb0 expected ordering isn't compatible with pact structs
323    #[packed_field(bytes = "8..25", element_size_bits = "8")]
324    pub nkro_keys: [u8; 17],
325}
326
327impl NKROBootKeyboardReport {
328    pub fn new<K: IntoIterator<Item = Keyboard>>(keys: K) -> Self {
329        let mut report = Self::default();
330
331        let mut boot_keys_error = false;
332        let mut i = 0;
333        for k in keys {
334            match k {
335                Keyboard::LeftControl => {
336                    report.left_ctrl = true;
337                }
338                Keyboard::LeftShift => {
339                    report.left_shift = true;
340                }
341                Keyboard::LeftAlt => {
342                    report.left_alt = true;
343                }
344                Keyboard::LeftGUI => {
345                    report.left_gui = true;
346                }
347                Keyboard::RightControl => {
348                    report.right_ctrl = true;
349                }
350                Keyboard::RightShift => {
351                    report.right_shift = true;
352                }
353                Keyboard::RightAlt => {
354                    report.right_alt = true;
355                }
356                Keyboard::RightGUI => {
357                    report.right_gui = true;
358                }
359                Keyboard::NoEventIndicated => {}
360                Keyboard::ErrorRollOver | Keyboard::POSTFail | Keyboard::ErrorUndefine => {
361                    report.nkro_keys[0] |= 1 << u8::from(k);
362
363                    if !boot_keys_error {
364                        boot_keys_error = true;
365                        i = report.boot_keys.len();
366                        report.boot_keys.fill(k);
367                    }
368                }
369                _ => {
370                    if report.nkro_keys.len() * 8 > u8::from(k).into() {
371                        let byte = u8::from(k) / 8;
372                        let bit = u8::from(k) % 8;
373                        report.nkro_keys[usize::from(byte)] |= 1 << bit;
374                    }
375
376                    if boot_keys_error {
377                        continue;
378                    }
379
380                    if i < report.boot_keys.len() {
381                        report.boot_keys[i] = k;
382                        i += 1;
383                    } else {
384                        boot_keys_error = true;
385                        i = report.boot_keys.len();
386                        report.boot_keys.fill(Keyboard::ErrorRollOver);
387                    }
388                }
389            }
390        }
391        report
392    }
393}
394
395/// Interface implementing a NKRO keyboard compatible with the HID boot keyboard specification
396///
397/// **Note:** This is a managed interfaces that support HID idle, [`UsbHidClass::tick()`] must be called every 1ms/ at 1kHz.
398pub struct NKROBootKeyboard<'a, B: UsbBus> {
399    interface: ManagedIdleInterface<'a, B, NKROBootKeyboardReport, InBytes32, OutBytes8>,
400}
401
402impl<B> NKROBootKeyboard<'_, B>
403where
404    B: UsbBus,
405{
406    pub fn write_report<K: IntoIterator<Item = Keyboard>>(
407        &mut self,
408        keys: K,
409    ) -> Result<(), UsbHidError> {
410        self.interface
411            .write_report(&NKROBootKeyboardReport::new(keys))
412    }
413
414    pub fn read_report(&mut self) -> usb_device::Result<KeyboardLedsReport> {
415        let data = &mut [0];
416        match self.interface.read_report(data) {
417            Err(e) => Err(e),
418            Ok(_) => match KeyboardLedsReport::unpack(data) {
419                Ok(r) => Ok(r),
420                Err(_) => Err(UsbError::ParseError),
421            },
422        }
423    }
424}
425
426pub struct NKROBootKeyboardConfig<'a> {
427    interface: ManagedIdleInterfaceConfig<'a, NKROBootKeyboardReport, InBytes32, OutBytes8>,
428}
429
430impl Default for NKROBootKeyboardConfig<'_> {
431    fn default() -> Self {
432        Self::new(ManagedIdleInterfaceConfig::new(
433            unwrap!(unwrap!(unwrap!(unwrap!(InterfaceBuilder::new(
434                NKRO_BOOT_KEYBOARD_REPORT_DESCRIPTOR
435            ))
436            .description("NKRO Keyboard")
437            .boot_device(InterfaceProtocol::Keyboard)
438            .idle_default(500.millis()))
439            .in_endpoint(10.millis()))
440            .with_out_endpoint(100.millis()))
441            .build(),
442        ))
443    }
444}
445
446impl<'a> NKROBootKeyboardConfig<'a> {
447    #[must_use]
448    pub fn new(
449        interface: ManagedIdleInterfaceConfig<'a, NKROBootKeyboardReport, InBytes32, OutBytes8>,
450    ) -> Self {
451        Self { interface }
452    }
453}
454
455impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for NKROBootKeyboardConfig<'a> {
456    type Allocated = NKROBootKeyboard<'a, B>;
457
458    fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
459        Self::Allocated {
460            interface: self.interface.allocate(usb_alloc),
461        }
462    }
463}
464
465impl<'a, B> DeviceClass<'a> for NKROBootKeyboard<'a, B>
466where
467    B: UsbBus,
468{
469    type I = Interface<'a, B, InBytes32, OutBytes8, ReportSingle>;
470
471    fn interface(&mut self) -> &mut Self::I {
472        self.interface.interface()
473    }
474
475    fn reset(&mut self) {
476        self.interface.reset();
477    }
478
479    fn tick(&mut self) -> core::result::Result<(), UsbHidError> {
480        self.interface.tick()
481    }
482}
483
484/// HID Keyboard report descriptor implementing an NKRO keyboard as a bitmap.
485///
486/// N.B. This is not compatible with the HID boot specification
487//18 bytes - derived from https://learn.adafruit.com/custom-hid-devices-in-circuitpython/n-key-rollover-nkro-hid-device
488//First byte modifiers, 17 byte key bit array
489#[rustfmt::skip]
490pub const NKRO_COMPACT_KEYBOARD_REPORT_DESCRIPTOR: &[u8] = &[
491    0x05, 0x01,                     // Usage Page (Generic Desktop),
492    0x09, 0x06,                     // Usage (Keyboard),
493    0xA1, 0x01,                     // Collection (Application),
494    // bitmap of modifiers
495    0x75, 0x01,                     //   Report Size (1),
496    0x95, 0x08,                     //   Report Count (8),
497    0x05, 0x07,                     //   Usage Page (Key Codes),
498    0x19, 0xE0,                     //   Usage Minimum (224),
499    0x29, 0xE7,                     //   Usage Maximum (231),
500    0x15, 0x00,                     //   Logical Minimum (0),
501    0x25, 0x01,                     //   Logical Maximum (1),
502    0x81, 0x02,                     //   Input (Data, Variable, Absolute), ;Modifier byte
503    // LED output report
504    0x95, 0x05,                     //   Report Count (5),
505    0x75, 0x01,                     //   Report Size (1),
506    0x05, 0x08,                     //   Usage Page (LEDs),
507    0x19, 0x01,                     //   Usage Minimum (1),
508    0x29, 0x05,                     //   Usage Maximum (5),
509    0x91, 0x02,                     //   Output (Data, Variable, Absolute),
510    0x95, 0x01,                     //   Report Count (1),
511    0x75, 0x03,                     //   Report Size (3),
512    0x91, 0x03,                     //   Output (Constant),
513    // bitmap of keys
514    0x95, 0x88,                     //   Report Count () - (REPORT_BYTES-1)*8
515    0x75, 0x01,                     //   Report Size (1),
516    0x15, 0x00,                     //   Logical Minimum (0),
517    0x25, 0x01,                     //   Logical Maximum(1),
518    0x05, 0x07,                     //   Usage Page (Key Codes),
519    0x19, 0x00,                     //   Usage Minimum (0),
520    0x29, 0x87,                     //   Usage Maximum (), - (REPORT_BYTES-1)*8-1
521    0x81, 0x02,                     //   Input (Data, Variable, Absolute),
522    0xc0                            // End Collection
523];
524
525#[cfg(test)]
526mod test {
527    #![allow(clippy::unwrap_used)]
528    #![allow(clippy::expect_used)]
529
530    use packed_struct::prelude::*;
531
532    use crate::device::keyboard::{BootKeyboardReport, KeyboardLedsReport};
533    use crate::page::Keyboard;
534
535    #[test]
536    fn leds_num_lock() {
537        assert_eq!(
538            KeyboardLedsReport::unpack(&[1]),
539            Ok(KeyboardLedsReport {
540                num_lock: true,
541                caps_lock: false,
542                scroll_lock: false,
543                compose: false,
544                kana: false,
545            })
546        );
547    }
548
549    #[test]
550    fn leds_caps_lock() {
551        assert_eq!(
552            KeyboardLedsReport::unpack(&[2]),
553            Ok(KeyboardLedsReport {
554                num_lock: false,
555                caps_lock: true,
556                scroll_lock: false,
557                compose: false,
558                kana: false,
559            })
560        );
561
562        assert_eq!(
563            KeyboardLedsReport {
564                num_lock: false,
565                caps_lock: true,
566                scroll_lock: false,
567                compose: false,
568                kana: false,
569            }
570            .pack(),
571            Ok([2])
572        );
573    }
574
575    #[test]
576    fn boot_keyboard_report_mixed() {
577        let bytes = BootKeyboardReport::new([
578            Keyboard::LeftAlt,
579            Keyboard::A,
580            Keyboard::B,
581            Keyboard::C,
582            Keyboard::RightGUI,
583        ])
584        .pack()
585        .unwrap();
586
587        let key_mod: u8 = (0x1_u8
588            << (u8::from(Keyboard::LeftAlt) - u8::from(Keyboard::LeftControl)))
589            | (0x1_u8 << (u8::from(Keyboard::RightGUI) - u8::from(Keyboard::LeftControl)));
590
591        assert_eq!(
592            bytes,
593            [
594                key_mod,
595                0,
596                Keyboard::A.into(),
597                Keyboard::B.into(),
598                Keyboard::C.into(),
599                0,
600                0,
601                0
602            ]
603        );
604    }
605
606    #[test]
607    fn boot_keyboard_report_keys() {
608        let bytes = BootKeyboardReport::new([
609            Keyboard::A,
610            Keyboard::B,
611            Keyboard::C,
612            Keyboard::D,
613            Keyboard::E,
614            Keyboard::F,
615        ])
616        .pack()
617        .unwrap();
618
619        assert_eq!(
620            bytes,
621            [
622                0,
623                0,
624                Keyboard::A.into(),
625                Keyboard::B.into(),
626                Keyboard::C.into(),
627                Keyboard::D.into(),
628                Keyboard::E.into(),
629                Keyboard::F.into()
630            ]
631        );
632    }
633
634    #[test]
635    fn boot_keyboard_report_rollover() {
636        let bytes = BootKeyboardReport::new([
637            Keyboard::LeftAlt,
638            Keyboard::A,
639            Keyboard::B,
640            Keyboard::C,
641            Keyboard::D,
642            Keyboard::E,
643            Keyboard::F,
644            Keyboard::G,
645            Keyboard::RightGUI,
646        ])
647        .pack()
648        .unwrap();
649
650        let key_mod: u8 = (0x1_u8
651            << (u8::from(Keyboard::LeftAlt) - u8::from(Keyboard::LeftControl)))
652            | (0x1_u8 << (u8::from(Keyboard::RightGUI) - u8::from(Keyboard::LeftControl)));
653        assert_eq!(
654            bytes,
655            [
656                key_mod,
657                0,
658                Keyboard::ErrorRollOver.into(),
659                Keyboard::ErrorRollOver.into(),
660                Keyboard::ErrorRollOver.into(),
661                Keyboard::ErrorRollOver.into(),
662                Keyboard::ErrorRollOver.into(),
663                Keyboard::ErrorRollOver.into(),
664            ]
665        );
666    }
667}