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}