uefi_input2/
input.rs

1use core::ffi::c_void;
2use core::ptr::addr_of_mut;
3use uefi::{Char16, Event, Result, StatusExt};
4use uefi::proto::console::text::{Key};
5use uefi::proto::unsafe_protocol;
6use uefi_raw::Status;
7use crate::simple_text_input_ex::{KeyNotifyFunction, KeyState, KeyToggleState,
8                                  RawKeyData, SimpleTextInputExProtocol, Boolean, InputKey};
9
10/// height-level key data wrapper
11#[derive(Debug, Copy, Clone)]
12pub struct KeyData {
13    pub key: Key,
14    pub key_state: KeyState,
15}
16
17/// reverse conversion to C struct
18impl From<KeyData> for RawKeyData {
19    fn from(value: KeyData) -> Self {
20        let input_key = match value.key {
21            Key::Printable(c) => InputKey {
22                scan_code: 0,
23                unicode_char: u16::from(c),
24            },
25            Key::Special(code) => InputKey {
26                scan_code: code.0,
27                unicode_char: 0,
28            },
29        };
30
31        Self {
32            key: input_key,
33            key_state: value.key_state,
34        }
35    }
36}
37
38/// forward conversion to Rust struct
39impl From<RawKeyData> for KeyData {
40    fn from(raw: RawKeyData) -> Self {
41        Self {
42            key: Key::from(raw.key),
43            // TODO: add new type enum for key state
44            key_state: raw.key_state,
45        }
46    }
47}
48
49impl KeyData {
50    /// Create key data from char
51    pub fn new(c: char) -> Result<Self> {
52        let c = Char16::try_from(c).map_err(|_| Status::INVALID_PARAMETER)?;
53        
54        Ok(Self {
55            key: Key::Printable(c),
56            key_state: KeyState::default(),
57        })
58    }
59}
60
61/// safety wrapper for SimpleTextInputExProtocol
62#[derive(Debug)]
63#[repr(transparent)]
64#[unsafe_protocol(SimpleTextInputExProtocol::GUID)]
65pub struct Input(SimpleTextInputExProtocol);
66
67impl Input {
68    /// clear keyboard cache
69    pub fn reset(&mut self, extended_verification: bool) -> Result {
70        let this = addr_of_mut!(self.0);
71        unsafe {
72            (self.0.reset)(this, Boolean::from(extended_verification)).to_result()
73        }
74    }
75
76    /// non-blocking keyboard read
77    pub fn read_key_stroke_ex(&mut self) -> Option<KeyData> {
78        let mut raw = RawKeyData::default();
79        let this = addr_of_mut!(self.0);
80
81        let status = unsafe {
82            // FFI binding
83            (self.0.read_key_stroke_ex)(this, &mut raw)
84        };
85
86        // Convert to Rust high-level type
87        status.is_success().then_some(KeyData::from(raw))
88    }
89
90    /// Returns an event that is signaled when a key is pressed.
91    ///
92    /// This allows the caller to wait for input without polling in a busy loop,
93    /// or to use it in `wait_for_event` along with other events (like timers).
94    pub fn wait_for_key_event(&self) -> Option<Event> {
95        unsafe { Event::from_ptr(self.0.wait_for_key_ex) }
96    }
97
98    pub fn set_state(&mut self, mut state: KeyToggleState) -> Result {
99        let this = addr_of_mut!(self.0);
100        unsafe {
101            (self.0.set_state)(this, &mut state).to_result()
102        }
103    }
104
105    /// Register a callback function to be invoked when a key is pressed.
106    /// #### Usage
107    /// 1. Prepare callback function
108    /// ```rust,no_run
109    /// extern "efiapi" fn listener(_key_data: *mut uefi_input2::simple_text_input_ex::RawKeyData) -> Status {
110    ///     if _key_data.is_null() { return Status::INVALID_PARAMETER; }
111    ///     let data = unsafe { *_key_data };
112    ///     let data = KeyData::from(data);
113    ///     uefi::println!("{:?}{:08X}", data.key, data.key_state.key_shift_state);
114    ///     Status::SUCCESS
115    /// }
116    /// ```
117    /// 2. Register callback function
118    /// ```rust,no_run
119    /// uefi_input2::with_stdin(|input| {
120    ///     let trigger_key = KeyData::new('b')?;
121    ///     let _listener = input.on_key_callback(&trigger_key, listener)?;
122    ///     loop { core::hint::spin_loop() }
123    ///     Ok(())
124    ///  }).unwrap();
125    /// ```
126    pub fn on_key_callback(
127        &mut self,
128        key_data: &KeyData,
129        notification_function: KeyNotifyFunction,
130    ) -> Result<KeyNotifyHandle<'_>> {
131        let this = addr_of_mut!(self.0);
132
133        let mut handle = core::ptr::null_mut();
134        let mut raw_data = RawKeyData::from(*key_data);
135
136        unsafe {
137            ((*this).register_key_notify)(
138                this,
139                &mut raw_data,
140                notification_function,
141                &mut handle,
142            )
143                .to_result_with_val(|| KeyNotifyHandle {
144                    // the pointer needs to be cast back to a reference and stored in the Handle.
145                    // ensure that the Handle can safety borrow the origin protocol.
146                    proto: &mut *this,
147                    handle,
148                })
149        }
150    }
151}
152
153/// Keyboard notification handle
154pub struct KeyNotifyHandle<'a> {
155    /// Referencing the original agreement ensures that the agreement remains valid upon cancellation.
156    proto: &'a mut SimpleTextInputExProtocol,
157    /// The unique handle generated by UEFI
158    handle: *mut c_void,
159}
160
161impl Drop for KeyNotifyHandle<'_> {
162    /// Automatically unregister the handle when the user no longer needs it 
163    /// (e.g. when the variable goes out of scope).
164    fn drop(&mut self) {
165        let this = addr_of_mut!(*self.proto);
166        unsafe {
167            let _ = ((*this).unregister_key_notify)(this, self.handle);
168        }
169    }
170}