uefi 0.38.0

This crate makes it easy to develop Rust software that leverages safe, convenient, and performant abstractions for UEFI functionality.
Documentation
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::proto::unsafe_protocol;
use crate::{Char16, Error, Event, Result, Status, StatusExt};
use core::mem::MaybeUninit;
use uefi_raw::protocol::console::{
    InputKey, KeyData as RawKeyData, KeyNotifyFn, KeyShiftState, KeyState as RawKeyState,
    KeyToggleState, SimpleTextInputExProtocol, SimpleTextInputProtocol,
};

/// Simple Text Input [`Protocol`]. Interface for text-based input devices.
///
/// [`Protocol`]: uefi::proto::Protocol
#[derive(Debug)]
#[repr(transparent)]
#[unsafe_protocol(SimpleTextInputProtocol::GUID)]
pub struct Input(SimpleTextInputProtocol);

impl Input {
    /// Resets the input device hardware.
    ///
    /// The `extended_verification` parameter is used to request that UEFI
    /// performs an extended check and reset of the input device.
    ///
    /// # Errors
    ///
    /// - `DeviceError` if the device is malfunctioning and cannot be reset.
    pub fn reset(&mut self, extended_verification: bool) -> Result {
        unsafe { (self.0.reset)(&mut self.0, extended_verification.into()) }.to_result()
    }

    /// Reads the next keystroke from the input device, if any.
    ///
    /// Use [`wait_for_key_event`] with the [`boot::wait_for_event`]
    /// interface in order to wait for a key to be pressed.
    ///
    /// [`boot::wait_for_event`]: crate::boot::wait_for_event
    /// [`wait_for_key_event`]: Self::wait_for_key_event
    ///
    /// # Errors
    ///
    /// - [`Status::DEVICE_ERROR`] if there was an issue with the input device
    ///
    /// # Examples
    ///
    /// ```
    /// use log::info;
    /// use uefi::proto::console::text::{Input, Key, ScanCode};
    /// use uefi::{boot, Char16, Result, ResultExt};
    ///
    /// fn read_keyboard_events(input: &mut Input) -> Result {
    ///     loop {
    ///         // Pause until a keyboard event occurs.
    ///         let mut events = [input.wait_for_key_event().unwrap()];
    ///         boot::wait_for_event(&mut events).discard_errdata()?;
    ///
    ///         let u_key = Char16::try_from('u').unwrap();
    ///         match input.read_key()? {
    ///             // Example of handling a printable key: print a message when
    ///             // the 'u' key is pressed.
    ///             Some(Key::Printable(key)) if key == u_key => {
    ///                 info!("the 'u' key was pressed");
    ///             }
    ///
    ///             // Example of handling a special key: exit the loop when the
    ///             // escape key is pressed.
    ///             Some(Key::Special(ScanCode::ESCAPE)) => {
    ///                 break;
    ///             }
    ///             _ => {}
    ///         }
    ///     }
    ///
    ///     Ok(())
    /// }
    /// ```
    pub fn read_key(&mut self) -> Result<Option<Key>> {
        let mut key = MaybeUninit::<InputKey>::uninit();

        match unsafe { (self.0.read_key_stroke)(&mut self.0, key.as_mut_ptr()) } {
            Status::NOT_READY => Ok(None),
            other => other.to_result_with_val(|| Some(unsafe { key.assume_init() }.into())),
        }
    }

    /// Event to be used with [`boot::wait_for_event`] in order to wait
    /// for a key to be available
    ///
    /// [`boot::wait_for_event`]: crate::boot::wait_for_event
    pub fn wait_for_key_event(&self) -> Result<Event> {
        unsafe { Event::from_ptr(self.0.wait_for_key) }.ok_or(Error::from(Status::UNSUPPORTED))
    }
}

/// A key read from the console (high-level version)
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Key {
    /// The key is associated with a printable Unicode character
    Printable(Char16),

    /// The key is special (arrow, function, multimedia...)
    Special(ScanCode),
}

impl From<InputKey> for Key {
    fn from(k: InputKey) -> Self {
        if k.scan_code == ScanCode::NULL.0 {
            Self::Printable(Char16::try_from(k.unicode_char).unwrap())
        } else {
            Self::Special(ScanCode(k.scan_code))
        }
    }
}

newtype_enum! {
/// A keyboard scan code
///
/// Codes 0x8000 -> 0xFFFF are reserved for future OEM extensibility, therefore
/// this C enum is _not_ safe to model as a Rust enum (where the compiler must
/// know about all variants at compile time).
pub enum ScanCode: u16 => #[expect(missing_docs)] {
    /// Null scan code, indicates that the Unicode character should be used.
    NULL        = 0x00,
    /// Move cursor up 1 row.
    UP          = 0x01,
    /// Move cursor down 1 row.
    DOWN        = 0x02,
    /// Move cursor right 1 column.
    RIGHT       = 0x03,
    /// Move cursor left 1 column.
    LEFT        = 0x04,
    HOME        = 0x05,
    END         = 0x06,
    INSERT      = 0x07,
    DELETE      = 0x08,
    PAGE_UP     = 0x09,
    PAGE_DOWN   = 0x0A,
    FUNCTION_1  = 0x0B,
    FUNCTION_2  = 0x0C,
    FUNCTION_3  = 0x0D,
    FUNCTION_4  = 0x0E,
    FUNCTION_5  = 0x0F,
    FUNCTION_6  = 0x10,
    FUNCTION_7  = 0x11,
    FUNCTION_8  = 0x12,
    FUNCTION_9  = 0x13,
    FUNCTION_10 = 0x14,
    FUNCTION_11 = 0x15,
    FUNCTION_12 = 0x16,
    ESCAPE      = 0x17,

    FUNCTION_13 = 0x68,
    FUNCTION_14 = 0x69,
    FUNCTION_15 = 0x6A,
    FUNCTION_16 = 0x6B,
    FUNCTION_17 = 0x6C,
    FUNCTION_18 = 0x6D,
    FUNCTION_19 = 0x6E,
    FUNCTION_20 = 0x6F,
    FUNCTION_21 = 0x70,
    FUNCTION_22 = 0x71,
    FUNCTION_23 = 0x72,
    FUNCTION_24 = 0x73,

    MUTE        = 0x7F,
    VOLUME_UP   = 0x80,
    VOLUME_DOWN = 0x81,

    BRIGHTNESS_UP   = 0x100,
    BRIGHTNESS_DOWN = 0x101,
    SUSPEND         = 0x102,
    HIBERNATE       = 0x103,
    TOGGLE_DISPLAY  = 0x104,
    RECOVERY        = 0x105,
    EJECT           = 0x106,
}}

/// Simple Text Input Ex [`Protocol`]. Extended Interface for text-based input devices.
///
/// [`Protocol`]: uefi::proto::Protocol
#[derive(Debug)]
#[repr(transparent)]
#[unsafe_protocol(SimpleTextInputExProtocol::GUID)]
pub struct InputEx(SimpleTextInputExProtocol);

impl InputEx {
    /// Resets the input device hardware.
    ///
    /// If the `extended_verification` parameter is true, then the firmware may
    /// take an extended amount of time to verify the device is operating after
    /// the reset.
    ///
    /// # Errors
    ///
    /// - `DeviceError` if the device is malfunctioning and cannot be reset.
    pub fn reset(&mut self, extended_verification: bool) -> Result {
        unsafe { (self.0.reset)(&mut self.0, extended_verification.into()) }.to_result()
    }

    /// Reads the next keystroke from the input device, if any.
    ///
    /// Use [`wait_for_key_event`] with the [`boot::wait_for_event`]
    /// interface in order to wait for a key to be pressed.
    ///
    /// [`boot::wait_for_event`]: crate::boot::wait_for_event
    /// [`wait_for_key_event`]: Self::wait_for_key_event
    ///
    /// # Errors
    ///
    /// - [`Status::DEVICE_ERROR`] if there was an issue with the input device
    pub fn read_key(&mut self) -> Result<Option<KeyData>> {
        let mut key = MaybeUninit::<RawKeyData>::uninit();

        match unsafe { (self.0.read_key_stroke_ex)(&mut self.0, key.as_mut_ptr()) } {
            Status::NOT_READY => Ok(None),
            other => other.to_result_with_val(|| Some(unsafe { key.assume_init() }.into())),
        }
    }

    /// Sets certain state for the input device.
    ///
    /// By calling this function with [`KeyToggleState::EXPOSED`] set, this function
    /// enables [`read_key`] to return incomplete keystrokes such as
    /// the holding down of certain keys which are expressed as part of
    /// [`KeyState`] when there is no [`KeyData`].
    ///
    /// [`read_key`]: Self::read_key
    ///
    /// # Errors
    ///
    /// - `Unsupported` if the device does not support the ability to have its
    ///   state set or the requested state change was not supported.
    pub fn set_state(&mut self, state: KeyToggleState) -> Result {
        unsafe { (self.0.set_state)(&mut self.0, &state) }.to_result()
    }

    /// Event to be used with [`boot::wait_for_event`] in order to wait
    /// for a key to be available
    ///
    /// [`boot::wait_for_event`]: crate::boot::wait_for_event
    pub fn wait_for_key_event(&self) -> Result<Event> {
        unsafe { Event::from_ptr(self.0.wait_for_key_ex) }.ok_or(Error::from(Status::UNSUPPORTED))
    }

    /// Registers a function to be called when a specified key sequence is typed.
    ///
    /// The `key_data` defines the key combination (including shift/toggle states)
    /// that will trigger the `notify_function`.
    ///
    /// # Errors
    ///
    /// - [`Status::OUT_OF_RESOURCES`] if the notification could not be registered.
    /// - [`Status::INVALID_PARAMETER`] if the key sequence is invalid.
    pub fn register_key_notify(
        &mut self,
        key_data: KeyData,
        notify_function: KeyNotifyFn,
    ) -> Result<KeyNotifyHandle> {
        let mut handle = core::ptr::null_mut();

        // We must convert our high-level KeyData back to the raw format the firmware expects
        let raw_key_data = RawKeyData {
            key: InputKey {
                scan_code: match key_data.key {
                    Key::Special(s) => s.0,
                    Key::Printable(_) => ScanCode::NULL.0,
                },
                unicode_char: match key_data.key {
                    Key::Printable(c) => c.into(),
                    Key::Special(_) => 0,
                },
            },
            key_state: RawKeyState {
                key_shift_state: key_data
                    .key_state
                    .key_shift_state
                    .unwrap_or(KeyShiftState::empty()),
                key_toggle_state: key_data
                    .key_state
                    .key_toggle_state
                    .unwrap_or(KeyToggleState::empty()),
            },
        };

        unsafe {
            (self.0.register_key_notify)(&mut self.0, &raw_key_data, notify_function, &mut handle)
                .to_result_with_val(|| KeyNotifyHandle(handle))
        }
    }

    /// Unregisters a key notification previously registered with [`register_key_notify`].
    ///
    /// [`register_key_notify`]: Self::register_key_notify
    ///
    /// # Errors
    ///
    /// - [`Status::INVALID_PARAMETER`] if the handle is not valid.
    pub fn unregister_key_notify(&mut self, handle: KeyNotifyHandle) -> Result {
        unsafe { (self.0.unregister_key_notify)(&mut self.0, handle.0) }.to_result()
    }
}

/// A handle to a registered key notification.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct KeyNotifyHandle(*mut core::ffi::c_void);

/// A key read from the console and associated keyboard state.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct KeyData {
    /// The high-level representation of the key read from the console.
    pub key: Key,
    /// The associated keyboard state.
    pub key_state: KeyState,
}

impl From<RawKeyData> for KeyData {
    fn from(k: RawKeyData) -> Self {
        Self {
            key: k.key.into(),
            key_state: k.key_state.into(),
        }
    }
}

/// The state of various toggled attributes as well as input modifier values.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct KeyState {
    /// The currently pressed shift modifiers for the input device.
    pub key_shift_state: Option<KeyShiftState>,
    /// The current internal state of various toggled attributes.
    pub key_toggle_state: Option<KeyToggleState>,
}

impl From<RawKeyState> for KeyState {
    fn from(key_state: RawKeyState) -> Self {
        Self {
            key_shift_state: if key_state.key_shift_state.contains(KeyShiftState::VALID) {
                Some(key_state.key_shift_state)
            } else {
                None
            },
            key_toggle_state: if key_state.key_toggle_state.contains(KeyToggleState::VALID) {
                Some(key_state.key_toggle_state)
            } else {
                None
            },
        }
    }
}