115 lines
3.5 KiB
Rust
115 lines
3.5 KiB
Rust
|
#![no_main]
|
||
|
#![no_std]
|
||
|
|
||
|
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 {
|
||
|
uefi::helpers::init(&mut boot_system_table).unwrap();
|
||
|
|
||
|
// Disable the watchdog timer
|
||
|
boot_system_table
|
||
|
.boot_services()
|
||
|
.set_watchdog_timer(0, 0x10000, None)
|
||
|
.unwrap();
|
||
|
|
||
|
let boot_services = boot_system_table.boot_services();
|
||
|
|
||
|
let img_handle = boot_services.image_handle();
|
||
|
let loaded_image = boot_services
|
||
|
.open_protocol_exclusive::<LoadedImage>(img_handle)
|
||
|
.unwrap();
|
||
|
|
||
|
let device_handle = loaded_image.device().unwrap();
|
||
|
|
||
|
let block_io = unsafe {
|
||
|
boot_services.open_protocol::<BlockIO>(
|
||
|
OpenProtocolParams {
|
||
|
handle: device_handle,
|
||
|
agent: img_handle,
|
||
|
controller: None,
|
||
|
},
|
||
|
OpenProtocolAttributes::GetProtocol,
|
||
|
)
|
||
|
}
|
||
|
.unwrap();
|
||
|
|
||
|
let mut gif_buf = [0u8; GIF_SIZE];
|
||
|
block_io
|
||
|
.read_blocks(block_io.media().media_id(), Lba::from(0u64), &mut gif_buf)
|
||
|
.unwrap();
|
||
|
|
||
|
let gif = tinygif::Gif::<Rgb888>::from_slice(&gif_buf).unwrap();
|
||
|
|
||
|
// Get gop
|
||
|
let gop_handle = boot_services
|
||
|
.get_handle_for_protocol::<GraphicsOutput>()
|
||
|
.unwrap();
|
||
|
let mut gop = boot_services
|
||
|
.open_protocol_exclusive::<GraphicsOutput>(gop_handle)
|
||
|
.unwrap();
|
||
|
|
||
|
// Create UefiDisplay
|
||
|
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
|
||
|
display.fill_entire(Rgb888::BLACK).unwrap();
|
||
|
|
||
|
let timer_handle = boot_services
|
||
|
.get_handle_for_protocol::<Timestamp>()
|
||
|
.unwrap();
|
||
|
let timer = boot_services
|
||
|
.open_protocol_exclusive::<Timestamp>(timer_handle)
|
||
|
.unwrap();
|
||
|
|
||
|
let timer_hz = timer.get_properties().unwrap().frequency as f64;
|
||
|
|
||
|
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() {
|
||
|
frame.draw(&mut display.translated(translation)).unwrap();
|
||
|
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
|
||
|
}
|