#![no_main] #![no_std] extern crate alloc; 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) -> Status { uefi::helpers::init(&mut boot_system_table).expect("helper init"); log::info!("hello!"); // Disable the watchdog timer boot_system_table .boot_services() .set_watchdog_timer(0, 0x10000, None) .expect("disable watchdog timer"); let boot_services = boot_system_table.boot_services(); boot_services.stall(1_000_000); let img_handle = boot_services.image_handle(); let loaded_image = boot_services .open_protocol_exclusive::(img_handle) .expect("loaded image"); let device_handle = loaded_image.device().expect("device handle"); let block_io = unsafe { boot_services.open_protocol::( OpenProtocolParams { handle: device_handle, agent: img_handle, controller: None, }, OpenProtocolAttributes::GetProtocol, ) } .expect("block io"); let mut gif_buf = [0u8; GIF_SIZE]; block_io .read_blocks(block_io.media().media_id(), Lba::from(0u64), &mut gif_buf) .expect("read blocks"); let gif = tinygif::Gif::::from_slice(&gif_buf).expect("gif from slice"); // Get gop let gop_handle = boot_services .get_handle_for_protocol::() .expect("graphics output handle"); let mut gop = boot_services .open_protocol_exclusive::(gop_handle) .expect("graphics output open"); use alloc::vec::Vec; // Create UefiDisplay for mode in gop.modes(&boot_services).collect::>() { 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 => {} } } } 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).expect("fill black"); let timer_handle = boot_services .get_handle_for_protocol::() .expect("timestamp handle"); let timer = boot_services .open_protocol_exclusive::(timer_handle) .expect("timestamp open"); let timer_hz = timer.get_properties().expect("timer props").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)) .expect("frame draw"); 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 }