1use crate::proto::unsafe_protocol;
2use crate::{Char16, Event, Result, Status};
3use core::mem::MaybeUninit;
4
5/// Interface for text-based input devices.
6#[repr(C)]
7#[unsafe_protocol("387477c1-69c7-11d2-8e39-00a0c969723b")]
8pub struct Input {
9 reset: extern "efiapi" fn(this: &mut Input, extended: bool) -> Status,
10 read_key_stroke: extern "efiapi" fn(this: &mut Input, key: *mut RawKey) -> Status,
11 wait_for_key: Event,
12}
13
14impl Input {
15 /// Resets the input device hardware.
16 ///
17 /// The `extended_verification` parameter is used to request that UEFI
18 /// performs an extended check and reset of the input device.
19 ///
20 /// # Errors
21 ///
22 /// - `DeviceError` if the device is malfunctioning and cannot be reset.
23 pub fn reset(&mut self, extended_verification: bool) -> Result {
24 (self.reset)(self, extended_verification).into()
25 }
26
27 /// Reads the next keystroke from the input device, if any.
28 ///
29 /// Use [`wait_for_key_event`] with the [`BootServices::wait_for_event`]
30 /// interface in order to wait for a key to be pressed.
31 ///
32 /// [`BootServices::wait_for_event`]: uefi::table::boot::BootServices::wait_for_event
33 /// [`wait_for_key_event`]: Self::wait_for_key_event
34 ///
35 /// # Errors
36 ///
37 /// - [`Status::DEVICE_ERROR`] if there was an issue with the input device
38 ///
39 /// # Examples
40 ///
41 /// ```
42 /// use log::info;
43 /// use uefi::proto::console::text::{Input, Key, ScanCode};
44 /// use uefi::table::boot::BootServices;
45 /// use uefi::{Char16, Result, ResultExt};
46 ///
47 /// fn read_keyboard_events(boot_services: &BootServices, input: &mut Input) -> Result {
48 /// loop {
49 /// // Pause until a keyboard event occurs.
50 /// let mut events = unsafe { [input.wait_for_key_event().unsafe_clone()] };
51 /// boot_services
52 /// .wait_for_event(&mut events)
53 /// .discard_errdata()?;
54 ///
55 /// let u_key = Char16::try_from('u').unwrap();
56 /// match input.read_key()? {
57 /// // Example of handling a printable key: print a message when
58 /// // the 'u' key is pressed.
59 /// Some(Key::Printable(key)) if key == u_key => {
60 /// info!("the 'u' key was pressed");
61 /// }
62 ///
63 /// // Example of handling a special key: exit the loop when the
64 /// // escape key is pressed.
65 /// Some(Key::Special(ScanCode::ESCAPE)) => {
66 /// break;
67 /// }
68 /// _ => {}
69 /// }
70 /// }
71 ///
72 /// Ok(())
73 /// }
74 /// ```
75 pub fn read_key(&mut self) -> Result<Option<Key>> {
76 let mut key = MaybeUninit::<RawKey>::uninit();
77
78 match (self.read_key_stroke)(self, key.as_mut_ptr()) {
79 Status::NOT_READY => Ok(None),
80 other => other.into_with_val(|| Some(unsafe { key.assume_init() }.into())),
81 }
82 }
83
84 /// Event to be used with `BootServices::wait_for_event()` in order to wait
85 /// for a key to be available
86 #[must_use]
87 pub const fn wait_for_key_event(&self) -> &Event {
88 &self.wait_for_key
89 }
90}
91
92/// A key read from the console (high-level version)
93#[derive(Debug, Copy, Clone, Eq, PartialEq)]
94pub enum Key {
95 /// The key is associated with a printable Unicode character
96 Printable(Char16),
97
98 /// The key is special (arrow, function, multimedia...)
99 Special(ScanCode),
100}
101
102impl From<RawKey> for Key {
103 fn from(k: RawKey) -> Key {
104 if k.scan_code == ScanCode::NULL {
105 Key::Printable(k.unicode_char)
106 } else {
107 Key::Special(k.scan_code)
108 }
109 }
110}
111
112/// A key read from the console (UEFI version)
113#[repr(C)]
114pub struct RawKey {
115 /// The key's scan code.
116 /// or 0 if printable
117 pub scan_code: ScanCode,
118 /// Associated Unicode character,
119 /// or 0 if not printable.
120 pub unicode_char: Char16,
121}
122
123newtype_enum! {
124/// A keyboard scan code
125///
126/// Codes 0x8000 -> 0xFFFF are reserved for future OEM extensibility, therefore
127/// this C enum is _not_ safe to model as a Rust enum (where the compiler must
128/// know about all variants at compile time).
129pub enum ScanCode: u16 => #[allow(missing_docs)] {
130 /// Null scan code, indicates that the Unicode character should be used.
131 NULL = 0x00,
132 /// Move cursor up 1 row.
133 UP = 0x01,
134 /// Move cursor down 1 row.
135 DOWN = 0x02,
136 /// Move cursor right 1 column.
137 RIGHT = 0x03,
138 /// Move cursor left 1 column.
139 LEFT = 0x04,
140 HOME = 0x05,
141 END = 0x06,
142 INSERT = 0x07,
143 DELETE = 0x08,
144 PAGE_UP = 0x09,
145 PAGE_DOWN = 0x0A,
146 FUNCTION_1 = 0x0B,
147 FUNCTION_2 = 0x0C,
148 FUNCTION_3 = 0x0D,
149 FUNCTION_4 = 0x0E,
150 FUNCTION_5 = 0x0F,
151 FUNCTION_6 = 0x10,
152 FUNCTION_7 = 0x11,
153 FUNCTION_8 = 0x12,
154 FUNCTION_9 = 0x13,
155 FUNCTION_10 = 0x14,
156 FUNCTION_11 = 0x15,
157 FUNCTION_12 = 0x16,
158 ESCAPE = 0x17,
159
160 FUNCTION_13 = 0x68,
161 FUNCTION_14 = 0x69,
162 FUNCTION_15 = 0x6A,
163 FUNCTION_16 = 0x6B,
164 FUNCTION_17 = 0x6C,
165 FUNCTION_18 = 0x6D,
166 FUNCTION_19 = 0x6E,
167 FUNCTION_20 = 0x6F,
168 FUNCTION_21 = 0x70,
169 FUNCTION_22 = 0x71,
170 FUNCTION_23 = 0x72,
171 FUNCTION_24 = 0x73,
172
173 MUTE = 0x7F,
174 VOLUME_UP = 0x80,
175 VOLUME_DOWN = 0x81,
176
177 BRIGHTNESS_UP = 0x100,
178 BRIGHTNESS_DOWN = 0x101,
179 SUSPEND = 0x102,
180 HIBERNATE = 0x103,
181 TOGGLE_DISPLAY = 0x104,
182 RECOVERY = 0x105,
183 EJECT = 0x106,
184}}
185