user-interfacerustuser-inputuefi

How do I read user input in uefi-rs (UEFI Rust wrapper)


I want to know if there is any way I can read user input in UEFI using the uefi-rs Rust wrapper. (or possibly any other way I can gather user input in UEFI with Rust)

I have tried many different approaches with read_key, the key event, and one didn't even compile and the other one only printed zeros. I have also scavenged through dozens of GitHub repositories, but seemingly none of those used these methods.

The first method (read key):

    let bs = st.boot_services();
    let read_key =  st.stdin().read_key();
    let unwrap = read_key.unwrap();
    if unwrap.is_some() {
        st.stdout().write_str(&unwrap.expect("Error").fmt(&mut fmt::Formatter).to_string()).expect("Failed to write to the console.");
    }

And the second method (wait for event):

    let mut binding = st.unsafe_clone();
    key_event = binding.stdin().wait_for_key_event();
    let result = bs.wait_for_event(&mut [key_event.unsafe_clone()]);
    let usize = result.unwrap();
    st.stdout().write_str(&usize.to_string()).expect("Failed to write to the console.");

There are probably many beginner mistakes in there because I am not extremely experienced with Rust. I did a few projects and now I found this, so I wanted to try it. User input is a thing that is comparably easy in Assembly. (from what I've experienced with Rust so far)

If anyone knows a repository I could check or knows the answer, please help. Have been working on this for hours now.

Link to the crate docs: https://docs.rs/uefi/latest/uefi/ Source code: https://github.com/rust-osdev/uefi-rs


Solution

  • Here is a short example to read input until the user presses 'ESC'.

    #![no_main]
    #![no_std]
    #![feature(abi_efiapi)]
    
    use core::fmt::{Write};
    use uefi::{prelude::*, proto::console::text::ScanCode, Char16};
    
    #[entry]
    fn main(_handle: Handle, mut st: SystemTable<Boot>) -> Status {
        uefi_services::init(&mut st).unwrap();
        
        let mut exit_flag = false;
    
        while !exit_flag {
            let key = st.stdin().read_key().expect("Expected input");
            let _ = match key {
                Some(k) =>  {
                    match k {
                        uefi::proto::console::text::Key::Printable(p) => {
                            write!(st.stdout(), "A printable key was entered: {:?}\r\n", p).expect("Write failed");
                            if p == Char16::try_from(27u16).expect("Unable to convert the ESC ascii code to Char16") {
                                exit_flag = true;
                            }
                        }
                        uefi::proto::console::text::Key::Special(s) => {
                            write!(st.stdout(), "A special key was entered: {:?}\r\n", s).expect("Write failed");
                            if s == ScanCode::ESCAPE {
                                exit_flag = true;
                            }
                        }
                    };             
                },
                None => {}
            };
        }
    
        Status::SUCCESS
    }
    

    I have never used uefi-rs before, there may be better solutions to handle input with this crate.

    Take the template as starting point and replace main.rs. Build with: cargo +nightly build --target x86_64-unknown-uefi