framebuffer_manager/
lib.rs

1use framebuffer::Framebuffer;
2use framebuffer::KdMode;
3use framebuffer::FramebufferError;
4use std::ops::Add;
5use std::ops::AddAssign;
6
7/// Represents a pixel on the screen
8pub struct Pixel {
9	index: usize,
10}
11
12impl Pixel {
13	/// Sets the `Pixel` to the given RGB value in the given buffer
14	pub fn set_rgb(&self, buffer: &mut [u8], r: u8, g: u8, b: u8) {
15		buffer[self.index]=b;
16		buffer[self.index+1]=g;
17		buffer[self.index+2]=r;
18	}
19	/// Gets the current color of the `Pixel`
20	pub fn get_rgb(&self, buffer: &[u8]) -> (u8,u8,u8) {
21		(buffer[self.index+2], buffer[self.index+1], buffer[self.index])
22	}
23}
24
25/// Represents a point
26#[derive(Clone,Copy)]
27pub struct Point {
28	pub x: usize,
29	pub y: usize,
30}
31
32impl Point{
33	/// Creates a new `Point` with the given x, y coordinates
34	pub fn new(x: usize, y: usize) -> Self {
35		Point{x,y}
36	}
37}
38
39impl Add<(usize,usize)> for Point {
40	type Output = Self;
41
42	/// Adds a `(u8,u8)` to the Point
43	///
44	/// #Examples
45	///
46	/// ```
47	/// use framebuffer_manager::Point;
48	///
49	/// let p = Point::new(5,6);
50	/// let new_p = p + (1,2);
51	/// assert_eq!(new_p.x, 6);
52	/// assert_eq!(new_p.y, 8);
53	/// ```
54	fn add(self, offset: (usize, usize)) -> Self {
55		Self {
56			x: self.x + offset.0,
57			y: self.y + offset.1,
58		}
59	}
60}
61
62impl AddAssign<(usize,usize)> for Point {
63	fn add_assign(&mut self, offset: (usize, usize)){
64		self.x += offset.0;
65		self.y += offset.1;
66	}
67}
68
69impl From<(usize,usize)> for Point {
70	fn from(p: (usize, usize)) -> Self {
71		Point {x: p.0, y: p.1}
72	}
73}
74
75pub struct Rectangle {
76	pub location: Point,
77	pub height: usize,
78	pub width: usize,
79	pub pixels: Vec<Vec<Pixel>>,
80}
81
82impl Rectangle {
83	/// Creates a new `Rectangle` from the given dimensions and assigns the `Pixel`s their proper indicies based on the given `Framebuffer`
84	fn from_dimensions(loc: &Point, height: usize, width: usize, fb : &Framebuffer) -> Self {
85		let line_length = fb.fix_screen_info.line_length as usize;
86		let bytespp = (fb.var_screen_info.bits_per_pixel / 8) as usize;
87		let mut rows = Vec::new();
88		for i in 0..height {
89			let mut pixel_line = Vec::new();
90			for k in 0..width {
91				let index = ((i + loc.y) * line_length + (k + loc.x) * bytespp) as usize;
92				pixel_line.push(Pixel{index});
93			}
94			rows.push(pixel_line);
95		}
96		Rectangle {
97			location: *loc,
98			height,
99			width,
100			pixels: rows,
101		}
102	}
103	/// Fills a `Rectangle` with a given color
104	fn fill(&self, buffer: &mut [u8], rgb: (u8,u8,u8)) {
105		for row in self.pixels.iter() {
106			for p in row.iter() {
107				p.set_rgb(buffer, rgb.0, rgb.1, rgb.2);
108			}
109		}
110	
111	}
112}
113
114/// Represents a border around a `Window`
115pub struct Border {
116	pub top: Rectangle,
117	pub bot: Rectangle,
118	pub left: Rectangle,
119	pub right: Rectangle,
120}
121
122/// Represents a portion of the screen
123pub struct Window {
124	pub border: Option<Border>,
125	pub width: usize,
126	pub height: usize,
127	pub main_context: Rectangle,
128}
129
130impl Window {
131	/// Fills a `Window`'s `main_context` with the given color
132	fn fill(&self, buffer: &mut [u8], rgb: (u8,u8,u8)) {
133		self.main_context.fill(buffer,rgb);
134	}
135	/// Fills a `Window`'s `border` with the given color
136	fn fill_border(&self, buffer: &mut [u8], rgb: (u8,u8,u8)) {
137		match &self.border {
138			Some(br) => {
139				br.top.fill(buffer, rgb);
140				br.left.fill(buffer, rgb);
141				br.right.fill(buffer, rgb);
142				br.bot.fill(buffer, rgb);
143			},
144			_ => {}
145		}
146	}
147}
148
149/// A template to create a `Window`
150pub struct WindowTemplate {
151	pub id: usize,
152	pub location: Point,
153	pub width: usize,
154	pub height: usize,
155	pub border_thickness: usize,
156}
157
158/// A container to manage the framebuffer. Abstracts away from the buffer that represents the screen
159pub struct FBmanager {
160	pub framebuffer: Framebuffer,
161	pub buffer: Vec<u8>,
162	pub windows: Vec<Window>,
163}
164
165impl FBmanager {
166	/// Creates a new `FBmanager` using the given template
167	pub fn new(template: &[WindowTemplate]) -> Self {
168		let framebuffer = Framebuffer::new("/dev/fb0").unwrap();
169		let height = framebuffer.var_screen_info.yres;
170		let line_length = framebuffer.fix_screen_info.line_length;
171		let buffer = vec![0u8; (line_length*height) as usize];
172		let mut window_holder = Vec::new();
173		for t in template.iter() {
174			//create border
175			let mut border = None;
176			let mut start_location = t.location;
177			let mut context_height = t.height;
178			let mut context_width = t.width;
179			if t.border_thickness > 0 {
180				//create top
181				let border_height = t.border_thickness;
182				let border_width = t.width;
183				let top = Rectangle::from_dimensions(&t.location,border_height, border_width, &framebuffer);
184				//create bottom
185				let loc = t.location + (0, t.height - t.border_thickness);
186				let bot = Rectangle::from_dimensions(&loc, border_height, border_width, &framebuffer);
187				//create right
188				let loc = t.location + (t.width - t.border_thickness, t.border_thickness); 
189				let border_height = t.height - 2*t.border_thickness;
190				let border_width = t.border_thickness;
191				let right = Rectangle::from_dimensions(&loc, border_height, border_width, &framebuffer);
192				//create left
193				let loc = t.location + (0, t.border_thickness);
194				let left = Rectangle::from_dimensions(&loc, border_height, border_width, &framebuffer);
195				border = Some(Border {
196					top,
197					bot,
198					left,
199					right,
200				});
201				
202				start_location += (t.border_thickness, t.border_thickness);
203				context_height -= 2*t.border_thickness;
204				context_width -= 2*t.border_thickness;
205			}
206			//create main_context
207			let main_context = Rectangle::from_dimensions(&start_location, context_height, context_width, &framebuffer); 
208			let window = Window {
209				border,
210				width: t.width,
211				height: t.height,
212				main_context,
213			};
214			window_holder.push(window);
215		}
216		FBmanager {
217			framebuffer,
218			buffer,
219			windows: window_holder,
220		}
221	}
222	/// Enables Framebuffer graphics. *Must be enabled to draw to the screen*
223	/// **Must call** `disable_graphics()` **before the process exits**
224	pub fn enable_graphics() -> Result<i32, FramebufferError> {
225		Framebuffer::set_kd_mode(KdMode::Graphics)
226	}
227	/// Disables Framebuffer graphics. Call to use traditional means to print to the screen.
228	pub fn disable_graphics() -> Result<i32, FramebufferError> {
229		Framebuffer::set_kd_mode(KdMode::Text)
230	}
231	/// Fills the `Window` with the given `id` to the given color
232	pub fn fill(&mut self, id: usize, rgb: (u8,u8,u8)) {
233		self.windows[id].fill(&mut self.buffer, rgb);
234	}
235	/// Fills the `Window` with the given `id`'s border to the given color
236	pub fn fill_border(&mut self, id: usize, rgb: (u8,u8,u8)) {
237		self.windows[id].fill_border(&mut self.buffer, rgb);
238	}
239	/// Draws the `FBmanager`'s internal state to the screen. Remeber to `enable_graphics()` before this
240	pub fn draw(&mut self) {
241		self.framebuffer.write_frame( &self.buffer);
242	}
243}
244
245//tests
246
247#[cfg(test)]
248mod tests {
249	use super::*;
250
251	#[test]
252	fn single_square() {
253		let square = WindowTemplate {
254			id: 0,
255			location: Point::new(0,0),
256			width: 1000,
257			height: 1000,
258			border_thickness: 0,
259		};
260		let mut fm = FBmanager::new(&[square]);
261		let mut win = &mut fm.windows[0];
262		fm.windows[0].main_context.pixels[0][0].set_rgb(&mut fm.buffer,255,0,0);
263		fm.fill(0,(255,0,0));
264		let step_size = (fm.framebuffer.var_screen_info.bits_per_pixel / 8) as usize;
265		let line_length: usize = fm.framebuffer.fix_screen_info.line_length as usize;
266		println!("{:?}", fm.buffer);
267		for i in 0..1000 {
268			for q in 0..1000 {
269				let index: usize = i*line_length + q*step_size;
270				assert_eq!(fm.buffer[index], 0);
271				assert_eq!(fm.buffer[index+1], 0);
272				assert_eq!(fm.buffer[index+2], 255);
273			}
274		}
275	}
276}