The input protocol dosen`t work on real keyboards.
Closed this issue ยท 14 comments
The Input protocol only works in a vm.
Hi! Please provide more information about the code you ran, the environment (software versions etc.) you used, and so on. Without context, we can't do much about it.
The Input protocol does not get user input on a real computer.
Here is the code:
#![no_main]
#![no_std]
extern crate alloc;
use arrayvec::ArrayString;
use alloc::string::{String, ToString};
use uefi::prelude::*;
use uefi::{print, println, CStr16};
use uefi::proto::console::text::{Key, ScanCode, Input};
use uefi::proto::media::file::{File, FileAttribute, FileMode};
use uefi::proto::loaded_image::LoadedImage;
use uefi::proto::device_path::text::{DevicePathToText, DisplayOnly, AllowShortcuts};
use uefi::proto::device_path::DevicePath;
use uefi::runtime::ResetType;
#[entry]
fn main() -> Status {
let _ = uefi::helpers::init();
system::with_stdout(|stdout| stdout.reset(true).expect("Failed to reset stdout"));
let mut input: String = Default::default();
print!(">");
let mut input_protocol = boot::open_protocol_exclusive::<Input>(boot::get_handle_for_protocol::<Input>().unwrap()).unwrap();
system::with_stdin(|stdin| stdin.reset(true).expect("Failed to reset stdin"));
loop {
boot::wait_for_event(&mut [input_protocol.wait_for_key_event().unwrap()]).unwrap();
if input == "exit" {
runtime::reset(ResetType::SHUTDOWN, Status::SUCCESS, None);
}
let key = input_protocol.read_key().unwrap();
let _ = match key {
Some(k) => {
match k {
Key::Printable(k) => {
let input_string: String = input.to_string();
let k_string: String = k.to_string();
let k_str: &str = k_string.as_str();
if k_str == "\r" {
os(&mut input);
println!();
print!(">");
input = Default::default()
}else {
input = input_string + k_str;
print!("{}", k_str)
}
}
Key::Special(s) => {
if s == ScanCode::ESCAPE {
runtime::reset(ResetType::SHUTDOWN, Status::SUCCESS, None);
}
if s == ScanCode::DELETE {
input.pop();
}
}
}
}
_ => {}
};
}
}
fn os<'a>(input: &mut String) {
if input.contains("help") {
println!();
println!("Commands:");
println!(" exit: Exits the system");
println!(" remove [<target_path>]: The command removes the target.");
println!(" debug-uefi-path: A debug tool that shows the os/.efi file location.");
println!(" new-file [<target_path>]: A new file at the target directory.");
println!(" new-dir [<target_path>]: A new directory at the target directory.");
println!(" edit-file <file_contents> [<target_path>]: Edits a file and ads the file_contents to the file at the target directory.");
println!(" copy [<file1_path>] | [<file2_path>]:Creates a copy of the file.");
println!(" cat [<target_path>]:Prints the text in a file");
}
if input.contains("remove") {
let mut filesystem = boot::get_image_file_system(boot::image_handle()).unwrap();
let buf= &mut [0; 99999];
let binding = input.replace("remove", "").replace(" ", "").replace("/", "\\");
let str = binding.as_str();
let input_cstr16 = CStr16::from_str_with_buf(str, buf).unwrap();
let file = filesystem.open_volume().unwrap().open(input_cstr16, FileMode::ReadWrite, FileAttribute::default()).unwrap();
if file.is_regular_file().unwrap() {
file.into_regular_file().unwrap().delete().unwrap();
}else {
file.into_directory().unwrap().delete().unwrap();
}
}
if input.contains("new-dir") {
let mut filesystem = boot::get_image_file_system(boot::image_handle()).unwrap();
let buf= &mut [0; 99999];
let binding = input.replace("new-dir", "").replace(" ", "").replace("/", "\\");
let str = binding.as_str();
let input_cstr16 = CStr16::from_str_with_buf(str, buf).unwrap();
let _ = filesystem.open_volume().unwrap().open(input_cstr16, FileMode::CreateReadWrite, FileAttribute::DIRECTORY).unwrap().into_directory().unwrap();
}
if input.contains("new-file") {
let mut filesystem = boot::get_image_file_system(boot::image_handle()).unwrap();
let buf= &mut [0; 99999];
let binding = input.replace("new-file", "").replace(" ", "").replace("/", "\\");
let str = binding.as_str();
let input_cstr16 = CStr16::from_str_with_buf(str, buf).unwrap();
let _ = filesystem.open_volume().unwrap().open(input_cstr16, FileMode::CreateReadWrite, FileAttribute::default()).unwrap().into_regular_file().unwrap();
}
if input.contains("edit-file") {
let mut filesystem = boot::get_image_file_system(boot::image_handle()).unwrap();
let buf= &mut [0; 99999];
let binding = input.replace("edit-file", "").replace(" ", "").replace("/", "\\");
let split_point = binding.find("\\").unwrap() - 2;
let(file_contents, file_path) = binding.split_at(split_point);
let file_path_cstr16 = CStr16::from_str_with_buf(file_path, buf).unwrap();
let file = filesystem.open_volume().unwrap().open(file_path_cstr16, FileMode::ReadWrite, FileAttribute::default()).unwrap();
if file.is_regular_file().unwrap() {
let mut file = file.into_regular_file().unwrap();
file.write(file_contents.as_bytes()).unwrap();
file.close()
}
}
if input.contains("debug-uefi-path") {
let loaded_image_protocol = boot::open_protocol_exclusive::<LoadedImage>(boot::image_handle()).unwrap();
let device_path:&DevicePath = loaded_image_protocol.file_path().unwrap();
let device_path_protocol = boot::open_protocol_exclusive::<DevicePathToText>(boot::get_handle_for_protocol::<DevicePathToText>().unwrap()).unwrap();
let mut buf = ArrayString::<128>::new();
device_path_protocol.convert_device_path_to_text(device_path, DisplayOnly(true), AllowShortcuts(true)).unwrap().as_str_in_buf(&mut buf).unwrap();
let str:&str = buf.as_str();
println!();
println!("Loaded image path: {:?}", str);
}
if input.contains("copy") {
let mut filesystem = boot::get_image_file_system(boot::image_handle()).unwrap();
let path1_buf= &mut [0; 999];
let path2_buf= &mut [0; 999];
let binding = input.replace("copy", "").replace(" ", "").replace("/", "\\");
let split_location = binding.find("|").unwrap() - 1;
let (path1, path2) = binding.split_at(split_location);
let path1_cstr16 = CStr16::from_str_with_buf(path1, path1_buf).unwrap();
let path2_cstr16 = CStr16::from_str_with_buf(path2, path2_buf).unwrap();
let file1 = filesystem.open_volume().unwrap().open(path1_cstr16, FileMode::Read, FileAttribute::default()).unwrap();
let file2 = filesystem.open_volume().unwrap().open(path2_cstr16, FileMode::ReadWrite, FileAttribute::default()).unwrap();
if file1.is_regular_file().unwrap() == true {
if file2.is_regular_file().unwrap() == true {
let mut file1_contents:&mut [u8] = &mut [];
let mut file1 = file1.into_regular_file().unwrap();
let mut file2 = file2.into_regular_file().unwrap();
let _ = file1.read(&mut file1_contents).unwrap();
let _ = file2.write(&file1_contents);
file1.close();
file2.close();
}
}
}
if input.contains("move") {
let mut filesystem = boot::get_image_file_system(boot::image_handle()).unwrap();
let path1_buf= &mut [0; 999];
let path2_buf= &mut [0; 999];
let binding = input.replace("move", "").replace(" ", "").replace("/", "\\");
let split_location = binding.find("|").unwrap() - 1;
let (path1, path2) = binding.split_at(split_location);
let path1_cstr16 = CStr16::from_str_with_buf(path1, path1_buf).unwrap();
let path2_cstr16 = CStr16::from_str_with_buf(path2, path2_buf).unwrap();
let file1 = filesystem.open_volume().unwrap().open(path1_cstr16, FileMode::Read, FileAttribute::default()).unwrap();
let file2 = filesystem.open_volume().unwrap().open(path2_cstr16, FileMode::ReadWrite, FileAttribute::default()).unwrap();
if file1.is_regular_file().unwrap() == true {
if file2.is_regular_file().unwrap() == true {
let mut file1 = file1.into_regular_file().unwrap();
let mut file2 = file2.into_regular_file().unwrap();
let mut file1_contents:&mut [u8] = &mut [];
let _ = file1.read(&mut file1_contents).unwrap();
let _ = file2.write(&file1_contents);
file1.delete().unwrap();
file2.close();
}
}
}
if input.contains("cat") {
let mut filesystem = boot::get_image_file_system(boot::image_handle()).unwrap();
let buf = &mut [0; 999];
let binding = input.replace("cat", "").replace(" ", "").replace("/", "\\");
let str = binding.as_str();
let file_path = CStr16::from_str_with_buf(str, buf).unwrap();
let file = filesystem.open_volume().unwrap().open(file_path, FileMode::Read, FileAttribute::default()).unwrap();
if file.is_regular_file().unwrap() == true {
let file_contents:&mut [u8] = &mut [];
file.into_regular_file().unwrap().read(file_contents).unwrap();
println!("File contents: {:?}", String::from_utf8(file_contents.to_vec()).unwrap());
}
}
}The code writes input to the input variable that is used by the os function with contains all the commands.
- The
uefiversion is0.32.0 - The cargo and rustup versions are
1.81.0 - The target architecture is
x86_64.
Maybe the problem is in the Input protocol.
When I compile the project and test the .efi file with a vm it works.
This line doesn't look right to me:
input_protocol.wait_for_key_event().unwrap();This is just getting an Event, it's not actually waiting for a key to be pressed (see https://docs.rs/uefi/latest/uefi/proto/console/text/struct.Input.html#method.wait_for_key_event). You'll need to use boot::wait_for_event to actually wait for a key press.
It sill does not work.
Can you describe in what way? Is it panicking somewhere? Does the boot::wait_for_event call ever return? Is key ever Some?
From boot::wait_for_event I get 0 and key is not Some.
On a real laptop key is never Some, boot::wait_for_event works and the code does not panic.
The problem probably is the Input protocol not getting the pressed key.
The issue is still active.
The issue is still active.
We are all maintaining this in our free time ๐ You can be sure that @nicholasbishop and I have seen this issue. But at this time, none of us had the time to investigate this further, unfortunately.
I think I've identified the issue. I trimmed your example code down and verified what you saw: key presses worked in a VM, but not on real hardware. The device I tested on is a Lenovo X1 Carbon Gen 9. The issue is this line: system::with_stdin(|stdin| stdin.reset(true).expect("Failed to reset stdin"));. Once I removed that, it started working.
Here's the complete code I tested:
// In cargo.toml:
// uefi = { version = "0.33.0", features = ["panic_handler"] }
#![no_main]
#![no_std]
use uefi::prelude::*;
use uefi::println;
use uefi::proto::console::text::Input;
#[entry]
fn main() -> Status {
uefi::helpers::init().unwrap();
let handle = boot::get_handle_for_protocol::<Input>().unwrap();
let mut input_protocol = boot::open_protocol_exclusive::<Input>(handle).unwrap();
loop {
println!("before wait");
boot::wait_for_event(&mut [input_protocol.wait_for_key_event().unwrap()]).unwrap();
println!("after wait");
let key = input_protocol.read_key().unwrap();
println!("key: {key:?}");
}
}Thanks, I turned your code into the input processing code and pasted my own code for handling commands in.
Now it works on a laptop.