efigife/efi-bin/src/main.rs

140 lines
4.5 KiB
Rust
Raw Normal View History

2024-05-05 03:37:03 +00:00
#![no_main]
#![no_std]
2024-05-07 06:06:15 +00:00
extern crate alloc;
2024-05-05 03:37:03 +00:00
use core::time::Duration;
use uefi::prelude::*;
use uefi::proto::console::gop::GraphicsOutput;
use uefi::proto::loaded_image::LoadedImage;
use uefi::proto::media::block::{BlockIO, Lba};
use uefi::proto::misc::Timestamp;
use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams};
use uefi_graphics2::embedded_graphics::{pixelcolor::Rgb888, prelude::*};
use uefi_graphics2::UefiDisplay;
const GIF_SIZE: usize = include_bytes!("bad-apple.gif").len();
#[entry]
fn main(_image_handle: Handle, mut boot_system_table: SystemTable<Boot>) -> Status {
2024-05-07 06:06:15 +00:00
uefi::helpers::init(&mut boot_system_table).expect("helper init");
log::info!("hello!");
2024-05-05 03:37:03 +00:00
// Disable the watchdog timer
boot_system_table
.boot_services()
.set_watchdog_timer(0, 0x10000, None)
2024-05-07 06:06:15 +00:00
.expect("disable watchdog timer");
2024-05-05 03:37:03 +00:00
let boot_services = boot_system_table.boot_services();
2024-05-07 06:06:15 +00:00
boot_services.stall(1_000_000);
2024-05-05 03:37:03 +00:00
let img_handle = boot_services.image_handle();
let loaded_image = boot_services
.open_protocol_exclusive::<LoadedImage>(img_handle)
2024-05-07 06:06:15 +00:00
.expect("loaded image");
2024-05-05 03:37:03 +00:00
2024-05-07 06:06:15 +00:00
let device_handle = loaded_image.device().expect("device handle");
2024-05-05 03:37:03 +00:00
let block_io = unsafe {
boot_services.open_protocol::<BlockIO>(
OpenProtocolParams {
handle: device_handle,
agent: img_handle,
controller: None,
},
OpenProtocolAttributes::GetProtocol,
)
}
2024-05-07 06:06:15 +00:00
.expect("block io");
2024-05-05 03:37:03 +00:00
let mut gif_buf = [0u8; GIF_SIZE];
block_io
.read_blocks(block_io.media().media_id(), Lba::from(0u64), &mut gif_buf)
2024-05-07 06:06:15 +00:00
.expect("read blocks");
2024-05-05 03:37:03 +00:00
2024-05-07 06:06:15 +00:00
let gif = tinygif::Gif::<Rgb888>::from_slice(&gif_buf).expect("gif from slice");
2024-05-05 03:37:03 +00:00
// Get gop
let gop_handle = boot_services
.get_handle_for_protocol::<GraphicsOutput>()
2024-05-07 06:06:15 +00:00
.expect("graphics output handle");
2024-05-05 03:37:03 +00:00
let mut gop = boot_services
.open_protocol_exclusive::<GraphicsOutput>(gop_handle)
2024-05-07 06:06:15 +00:00
.expect("graphics output open");
2024-05-05 03:37:03 +00:00
2024-05-07 06:06:15 +00:00
use alloc::vec::Vec;
2024-05-05 03:37:03 +00:00
// Create UefiDisplay
2024-05-07 06:06:15 +00:00
for mode in gop.modes(&boot_services).collect::<Vec<_>>() {
let (w, h) = mode.info().resolution();
if w >= gif.width() as usize && h >= gif.height() as usize {
match mode.info().pixel_format() {
uefi::proto::console::gop::PixelFormat::Rgb
| uefi::proto::console::gop::PixelFormat::Bgr => {
if gop.set_mode(&mode).is_ok() {
break;
}
}
uefi::proto::console::gop::PixelFormat::Bitmask
| uefi::proto::console::gop::PixelFormat::BltOnly => {}
}
}
}
2024-05-05 03:37:03 +00:00
let mode = gop.current_mode_info();
let (res_w, res_h) = mode.resolution();
let mut display = UefiDisplay::new(gop.frame_buffer(), mode);
// Tint the entire screen cyan
2024-05-07 06:06:15 +00:00
display.fill_entire(Rgb888::BLACK).expect("fill black");
2024-05-05 03:37:03 +00:00
let timer_handle = boot_services
.get_handle_for_protocol::<Timestamp>()
2024-05-07 06:06:15 +00:00
.expect("timestamp handle");
2024-05-05 03:37:03 +00:00
let timer = boot_services
.open_protocol_exclusive::<Timestamp>(timer_handle)
2024-05-07 06:06:15 +00:00
.expect("timestamp open");
2024-05-05 03:37:03 +00:00
2024-05-07 06:06:15 +00:00
let timer_hz = timer.get_properties().expect("timer props").frequency as f64;
2024-05-05 03:37:03 +00:00
let start_timestamp = timer.get_timestamp();
let mut gif_elapsed = Duration::new(0, 0);
let get_real_elapsed =
|| Duration::from_secs_f64((timer.get_timestamp() - start_timestamp) as f64 / timer_hz);
let mut skipped_flushes = 0;
let translation = Point::new(
(res_w as i32 - gif.width() as i32) / 2,
(res_h as i32 / gif.height() as i32) / 2,
);
for frame in gif.frames() {
2024-05-07 06:06:15 +00:00
frame
.draw(&mut display.translated(translation))
.expect("frame draw");
2024-05-05 03:37:03 +00:00
gif_elapsed += Duration::from_millis(frame.delay_centis as u64 * 10);
// skip flush to catch up
if gif_elapsed > get_real_elapsed() {
display.flush();
if let Some(time_diff) = get_real_elapsed().checked_sub(gif_elapsed) {
boot_services.stall(time_diff.as_micros() as usize);
}
} else {
skipped_flushes += 1;
if skipped_flushes == 5 {
// don't skip more than 5 frames at a time
// so you can actually still see anything
skipped_flushes = 0;
display.flush();
}
}
}
Status::SUCCESS
}