ball/
ball.rs

1// Copyright (c) 2025 nytpu <alex [at] nytpu.com>
2// SPDX-License-Identifier: MPL-2.0
3// For more license details, see LICENSE or <https://www.mozilla.org/en-US/MPL/2.0/>.
4
5//! Example showing usage of a double-buffered `BltDrawTarget`, by animating a ball bouncing around
6//! the screen.
7
8#![no_main]
9#![no_std]
10
11use core::time::Duration;
12use embedded_graphics::{
13	pixelcolor::Rgb888,
14	prelude::*,
15	primitives::{Circle, PrimitiveStyle, Rectangle},
16};
17use embedded_graphics_gop::BltDrawTarget;
18use uefi::{helpers, prelude::*, proto::console::gop::GraphicsOutput, runtime::ResetType};
19
20#[entry]
21fn main() -> Status {
22	helpers::init().unwrap();
23
24	let handle =
25		boot::get_handle_for_protocol::<GraphicsOutput>().expect("Unable to get GOP handle");
26	let mut gop =
27		boot::open_protocol_exclusive::<GraphicsOutput>(handle).expect("Unable to open GOP handle");
28
29	let mode_800x600 = gop
30		.modes()
31		// one of the standard modes, although some systems may only provide 640×480
32		.find(|m| m.info().resolution() == (800, 600))
33		.expect("Unable to find 800x600 video mode");
34	gop.set_mode(&mode_800x600)
35		.expect("Couldn't switch to 800x600 video mode");
36
37	let mut target = BltDrawTarget::new(&mut gop).expect("unable to open BLTDrawTarget");
38
39	target
40		.double_buffer(true)
41		.expect("failed to set double buffer");
42
43	let red_fill = PrimitiveStyle::with_fill(Rgb888::RED);
44	let blue_fill = PrimitiveStyle::with_fill(Rgb888::BLUE);
45
46	let mut ball_pos = Point::new(200, 300);
47	let mut ball_dir = Point::new(5, 5);
48	let ball_size = 10;
49	for _ in 0..1000 {
50		// Drawing over old position with blue instead of black to leave trail behind and
51		// demonstrate more colors
52		Rectangle::new(ball_pos, Size::new(ball_size, ball_size))
53			.into_styled(blue_fill)
54			.draw(&mut target)
55			.expect("infallible");
56
57		ball_pos += ball_dir;
58
59		if ball_pos.x <= 0 {
60			ball_pos.x = 0;
61			ball_dir.x = -ball_dir.x;
62		} else if ball_pos.x >= 790 {
63			ball_pos.x = 790;
64			ball_dir.x = -ball_dir.x;
65		}
66		if ball_pos.y <= 0 {
67			ball_pos.y = 0;
68			ball_dir.y = -ball_dir.y;
69		} else if ball_pos.y >= 590 {
70			ball_pos.y = 590;
71			ball_dir.y = -ball_dir.y;
72		}
73
74		Circle::new(ball_pos, ball_size)
75			.into_styled(red_fill)
76			.draw(&mut target)
77			.expect("infallible");
78
79		// In real code would probably want to set up UEFI timer and commit in there?
80		target
81			.commit()
82			.expect("unable to commit changes to framebuffer");
83		boot::stall(Duration::from_millis(10));
84	}
85
86	runtime::reset(ResetType::SHUTDOWN, Status::SUCCESS, None);
87}