uefi_input2/
lib.rs

1// Copyright (c) Bemly, January 2026
2// You may copy and distribute this file freely.  Any queries and
3// complaints should be forwarded to bemly_@petalmail.com.
4// If you make any changes to this file, please do not distribute
5// the results under the name `bemly'.
6
7//! # UEFI Simple Text Input Ex Protocol Wrapper
8//!
9//! This library provides a safe, idiomatic Rust wrapper for the `EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL`.
10//! Unlike the standard `SimpleTextInput`, this protocol allows for advanced key tracking,
11//! including shift state (Ctrl, Alt, Shift) and toggle state (Caps Lock, Num Lock).
12//!
13//! ## Purpose
14//! - **Seamless Migration**: Designed as a **drop-in, painless replacement**
15//!     for the standard `uefi::system::with_stdin`.
16//! - **Safe Resource Management**: Uses the `with_stdin` pattern to ensure the protocol is opened
17//!     exclusively and closed automatically.
18//! - **Extended Key Data**: Access to `KeyShiftState` and `KeyToggleState`.
19//! - **No-Std Compatible**: Designed specifically for UEFI environments.
20//!
21//! ## Feature List
22//! - **alloc**: Enables `Vec` support. For example, `with_stdins` requires
23//!     the `alloc` feature to more than 8 multiple input handles via `find_handles`.
24//!
25//! ## Minimal Example
26//! Simply replace your import and use the same closure-based pattern:
27//!
28//! ```rust,no_run
29//! #![no_main]
30//! #![no_std]
31//! use uefi::prelude::*;
32//! use uefi::{print, println};
33//! use uefi::proto::console::text::Key::{Printable, Special};
34//! use uefi::proto::console::text::ScanCode;
35//! use uefi::boot::check_event;
36//!
37//! #[entry]
38//! fn main() -> Status {
39//!     uefi::helpers::init().unwrap();
40//!
41//!     uefi_input2::with_stdin(|input| {
42//!         loop {
43//!             // Performance Note: Using wait_for_key_event + check_event conforms to UEFI
44//!             // best practices by reducing CPU overhead and bus traffic. However,
45//!             // for maximum loop throughput (e.g., high-frequency GOP rendering),
46//!             // consider calling read_key_stroke_ex directly to save the extra protocol call.
47//!             let Some(event) = input.wait_for_key_event() else { continue };
48//!             if !check_event(event)? { continue }
49//!
50//!             if let Some(data) = input.read_key_stroke_ex() {
51//!                 if data.shift() { println!("Shift is being held!") }
52//!                 match data.key {
53//!                    Printable(c) if u16::from(c) == 0x0D => print!("\r\n"),
54//!                    Printable(c) => print!("{}", c),
55//!                    Special(code) if code == ScanCode::ESCAPE => {
56//!                        println!("Exiting...");
57//!                        return Ok(())
58//!                    },
59//!                    _ => {},
60//!                }
61//!             }
62//!         }
63//!         Ok(())
64//!     }).unwrap();
65//!
66//!     Status::SUCCESS
67//! }
68//! ```
69
70#![no_std]
71#![cfg_attr(docsrs, feature(doc_cfg))]
72
73/// 
74pub mod config;
75/// C FFI Bindingw
76pub mod simple_text_input_ex;
77/// simple_text_input_ex wrapper
78pub mod input;
79/// height-level data wrapper
80pub mod key_data;
81/// keyboard hotplug support (Not Recommend, Unplugging is UEFI Spec Undefined Behavior)
82#[cfg(feature = "alloc")]
83pub mod hotplug;
84/// Core input state machine for handling complex keyboard events.
85#[cfg(feature = "alloc")]
86pub mod state_machine;
87/// Compatibility layer for environments lacking UEFI `Timestamp` protocol support.
88#[cfg(feature = "alloc")]
89mod state_machine_fallback;
90
91use uefi::boot::{get_handle_for_protocol, open_protocol_exclusive, ScopedProtocol};
92use uefi::Result;
93use crate::input::Input;
94
95/// it has roughly the same function as `uefi::system::with_stdin`.
96/// only support single keyboard.
97pub fn with_stdin<F, R>(mut f: F) -> Result<R>
98where F: FnMut(&mut ScopedProtocol<Input>) -> Result<R> {
99    let input = get_handle_for_protocol::<Input>()?;
100    let mut input = open_protocol_exclusive::<Input>(input)?;
101
102    f(&mut input)
103}
104
105#[cfg(feature = "alloc")]
106extern crate alloc;
107#[cfg(feature = "alloc")]
108use alloc::vec::Vec;
109#[cfg(feature = "alloc")]
110use uefi::boot::find_handles;
111
112/// support multiple keyboard.
113///
114/// Tips: if OEM UEFI impl ConSplitter driver(Virtual ConIn), keyboard hotplug may is supported.
115///
116/// #### Usage
117/// ```rust,no_run
118/// uefi_input2::with_stdins(|stdins| {
119///     loop {
120///         for keyboard in stdins.iter_mut() {
121///             let Some(event) = input.wait_for_key_event() else { continue };
122///             if !check_event(event)? { continue }
123///
124///             if let Some(key_data) = keyboard.read_key_stroke_ex() {
125///                 // just do it!
126///             }
127///         }
128///     }
129///  }).unwarp();
130/// ```
131#[cfg(feature = "alloc")]
132pub fn with_stdins<F, R>(mut f: F) -> Result<R>
133where F: FnMut(&mut Vec<ScopedProtocol<Input>>) -> Result<R> {
134
135    let inputs = find_handles::<Input>()?;
136    let mut keyboards: Vec<ScopedProtocol<Input>> = Vec::with_capacity(inputs.len());
137    for input in inputs {
138        let keyboard = open_protocol_exclusive::<Input>(input)?;
139        keyboards.push(keyboard);
140    }
141
142    f(&mut keyboards)
143}
144
145#[cfg(feature = "extend")]
146pub fn init_keyboards_protocol() -> Result<Vec<ScopedProtocol<Input>>> {
147    let inputs = find_handles::<Input>()?;
148    let mut keyboards: Vec<ScopedProtocol<Input>> = Vec::with_capacity(inputs.len());
149    for input in inputs {
150        let keyboard = open_protocol_exclusive::<Input>(input)?;
151        keyboards.push(keyboard);
152    }
153
154    Ok(keyboards)
155}
156
157/// only supports a maximum of 8 keyboards.
158#[cfg(not(feature = "alloc"))]
159pub fn with_stdins<F, R>(mut f: F) -> Result<R>
160where F: FnMut(&mut [Option<ScopedProtocol<Input>>]) -> Result<R> {
161    use uefi::boot::{locate_handle_buffer, SearchType};
162    use uefi::Identify;
163
164    let inputs = locate_handle_buffer(SearchType::ByProtocol(&Input::GUID))?;
165
166    let mut keyboards: [Option<ScopedProtocol<Input>>; 8]
167        =  [None, None, None, None, None, None, None, None];
168
169    for (i, &input) in inputs.iter().enumerate() {
170        if i >= keyboards.len() { break } // safe check
171
172        if let Ok(keyboard) = open_protocol_exclusive::<Input>(input) {
173            keyboards[i] = Some(keyboard);
174        }
175    }
176    f(&mut keyboards)
177}