vgastream_rs/
lib.rs

1//! ## vgastream-rs
2//! - High level VGA(0xB8000) library in freestanding Rust.
3//! - Provides ```println``` ```print``` ```eprintln``` ```eprint``` macros.
4//!
5//! ## Example
6//! ```rust
7//! /*main.rs*/
8//!
9//! #![no_std]
10//! #![no_main]
11//!
12//! mod multiboot;
13//! mod panic;
14//!
15//! extern crate vgastream_rs;
16//! use vgastream_rs::prelude::*;
17//!
18//! #[no_mangle]
19//! pub extern "C" fn _start() -> ! {
20//!     use vga_rs::Color;
21//!     VgaOutStream::new().reset_with((Color::BLACK, Color::WHITE));
22//!
23//!     let _ = main();
24//!     unsafe { panic::halt() }
25//! }
26//!
27//! fn main() {
28//!     println!("Hello, World!");
29//! }
30//!
31//! ```
32//!
33
34#![no_std]
35
36extern crate vga_rs;
37use vga_rs::*;
38
39extern crate cursor_rs;
40use cursor_rs::*;
41
42/// Controls vga screen.
43
44pub mod vga_screen {
45
46    use super::*;
47
48    /// Resets screen with foreground color and background color.
49    pub fn reset_with((fgcolor, bgcolor): (Color, Color)) {
50        for vchar in VgaBuffer::new().as_mut_slice().iter_mut() {
51            let attribute = Attribute::new(fgcolor, bgcolor);
52            vchar.set_volatile(VgaChar::new(0u8, attribute));
53        }
54        VgaCursor::new().set(0, 0);
55    }
56
57    /// Resets screen.
58    pub fn reset() {
59        for vchar_ref in VgaBuffer::new().as_mut_slice().iter_mut() {
60            let mut vchar = vchar_ref.get_volatile();
61            vchar.codepoint = 0u8;
62            vchar_ref.set_volatile(vchar);
63        }
64        VgaCursor::new().set(0, 0);
65    }
66
67    /// Sets foreground color.
68    pub fn set_fgcolor(fgcolor: Color) {
69        for vchar_ref in VgaBuffer::new().as_mut_slice().iter_mut() {
70            let mut vchar = vchar_ref.get_volatile();
71            let mut attribute = Attribute::from_u8(vchar.attribute);
72            attribute.fgcolor = fgcolor;
73            vchar.attribute = attribute.as_u8();
74            vchar_ref.set_volatile(vchar);
75        }
76    }
77
78    /// Sets background color.
79    pub fn set_bgcolor(bgcolor: Color) {
80        for vchar_ref in VgaBuffer::new().as_mut_slice().iter_mut() {
81            let mut vchar = vchar_ref.get_volatile();
82            let mut attribute = Attribute::from_u8(vchar.attribute);
83            attribute.bgcolor = bgcolor;
84            vchar.attribute = attribute.as_u8();
85            vchar_ref.set_volatile(vchar);
86        }
87    }
88}
89
90/// Provides vga stream.
91#[derive(Debug)]
92pub struct VgaOutStream {
93    vgabuffer: VgaBuffer,
94}
95
96impl VgaOutStream {
97    pub const HIGH: usize = VgaBuffer::HIGH;
98    pub const WIDTH: usize = VgaBuffer::WIDTH;
99
100    pub const TABSIZE: usize = 4;
101    pub const TABCHAR: u8 = b' ';
102
103    /// Constructs a VgaOutStream that wraps VgaBuffer.
104    #[inline]
105    pub fn new() -> Self {
106        Self {
107            vgabuffer: VgaBuffer::new(),
108        }
109    }
110
111    /// Shifts screen.
112    fn scroll(&mut self) {
113        for y in 0usize..(Self::HIGH - 1) {
114            for x in 0usize..(Self::WIDTH) {
115                let vchar = self.vgabuffer.get(y + 1, x);
116                self.vgabuffer.set(vchar, y, x);
117            }
118        }
119
120        for x in 0..Self::WIDTH {
121            let y = Self::HIGH - 1;
122            let attribute = Attribute::from_u8(self.vgabuffer.get(y, x).attribute);
123            let vchar = VgaChar::new(0u8, attribute);
124            self.vgabuffer.set(vchar, y, x);
125        }
126    }
127}
128
129/// Error for VgaRead and VgaWrite.
130pub enum VgaStreamError {
131    OutOfRange,
132    OverBuffer,
133}
134
135/// Read vga strean.
136pub trait VgaRead {
137    fn read(&self, y: usize, x: usize) -> Result<u8, VgaStreamError>;
138
139    fn read_buffer(&self, buffer: &mut [u8], y: usize, x: usize) -> Result<(), VgaStreamError>;
140}
141
142/// Write vga strean.
143pub trait VgaWrite {
144    /// Writes one charector on screen. Never return ```None```.
145    /// ```rust
146    /// let mut stream = VgaOutStream::new();
147    /// stream.write(b'A')?;
148    fn write(&mut self, ch: u8) -> Result<(), VgaStreamError>;
149
150    /// Writes buffer of charector on screen. Never return ```None```.
151    /// ```rust
152    /// let mut stream = VgaOutStream::new();
153    /// stream.write_buffer(b"Hello,World")?;
154    fn write_buffer(&mut self, text: &[u8]) -> Result<(), VgaStreamError> {
155        for &ch in text.iter() {
156            self.write(ch)?;
157        }
158
159        Ok(())
160    }
161}
162
163impl VgaWrite for VgaOutStream {
164    fn write(&mut self, ch: u8) -> Result<(), VgaStreamError> {
165        let (y, x) = VgaCursor::new().get();
166        let mut vchar = self.vgabuffer.get(y, x);
167
168        vchar.codepoint = ch;
169
170        let (y, x) = if ch == b'\n' {
171            (y + 1, 0usize)
172        } else if ch == b'\t' {
173            let new_x = if x + Self::TABSIZE < Self::WIDTH {
174                x + Self::TABSIZE
175            } else {
176                Self::WIDTH - 1
177            };
178
179            vchar.codepoint = Self::TABCHAR;
180            for i in x..new_x {
181                self.vgabuffer.set(vchar, y, i);
182            }
183
184            (y, new_x)
185        } else {
186            self.vgabuffer.set(vchar, y, x);
187
188            let y = y + ((x + 1) / Self::WIDTH);
189            let x = (x + 1) % Self::WIDTH;
190
191            (y, x)
192        };
193
194        let y = if y >= Self::HIGH {
195            self.scroll();
196            Self::HIGH - 1
197        } else {
198            y
199        };
200
201        VgaCursor::new().set(y, x);
202
203        Ok(())
204    }
205}
206
207use core::fmt::{Arguments, Error, Write};
208
209impl Write for VgaOutStream {
210    fn write_str(&mut self, s: &str) -> Result<(), Error> {
211        match self.write_buffer(s.as_bytes()) {
212            Ok(_) => Ok(()),
213            _ => Err(Error),
214        }
215    }
216}
217
218/// print macro for VGA.
219#[macro_export]
220macro_rules! print {
221		($($arg:tt)*) => {{
222			let _ = core::fmt::write(&mut vgastream_rs::VgaOutStream::new(),format_args!($($arg)*));
223		}};
224	}
225
226/// println macro for VGA.
227/// ```rust
228/// fn main() {
229///     println!("Hello, World!");
230/// }
231///
232/// ```
233#[macro_export]
234macro_rules! println {
235		($($arg:tt)*) => {{
236			print!($($arg)*);
237			print!("\n");
238		}};
239	}
240
241/// eprint macro for VGA.
242#[macro_export]
243macro_rules! eprint {
244		($($arg:tt)*) => {{
245			print!($($arg)*);
246		}};
247	}
248
249/// eprintln macro for VGA.
250/// ```rust
251/// #[panic_handler]
252/// fn panic(info: &PanicInfo) -> ! {
253///     eprintln!("{}", info);
254/// 	loop
255/// 	{
256/// 		unsafe { halt() }
257/// 	}
258/// }
259/// ```
260#[macro_export]
261macro_rules! eprintln {
262		($($arg:tt)*) => {{
263			println!($($arg)*);
264		}};
265	}
266
267/// Provides vga stream color.
268#[derive(Debug)]
269pub struct VgaOutStreamColor {
270    vgabuffer: VgaBuffer,
271    fgcolor: Color,
272    bgcolor: Color,
273    base_fgcolor: Color,
274    base_bgcolor: Color,
275}
276
277impl VgaOutStreamColor {
278    pub const HIGH: usize = VgaBuffer::HIGH;
279    pub const WIDTH: usize = VgaBuffer::WIDTH;
280
281    pub const TABSIZE: usize = 4;
282    pub const TABCHAR: u8 = b' ';
283
284    /// Constructs a VgaOutStream that wraps VgaBuffer.
285    #[inline]
286    pub fn new(
287        (fgcolor, bgcolor): (Color, Color),
288        (base_fgcolor, base_bgcolor): (Color, Color),
289    ) -> Self {
290        Self {
291            vgabuffer: VgaBuffer::new(),
292            fgcolor: fgcolor,
293            bgcolor: bgcolor,
294            base_fgcolor: base_fgcolor,
295            base_bgcolor: base_bgcolor,
296        }
297    }
298
299    /// Shifts screen.
300    fn scroll(&mut self) {
301        for y in 0usize..(Self::HIGH - 1) {
302            for x in 0usize..(Self::WIDTH) {
303                let vchar = self.vgabuffer.get(y + 1, x);
304                self.vgabuffer.set(vchar, y, x);
305            }
306        }
307
308        for x in 0..Self::WIDTH {
309            let y = Self::HIGH - 1;
310            let attribute = Attribute::new(self.base_fgcolor, self.base_bgcolor);
311            let vchar = VgaChar::new(0u8, attribute);
312            self.vgabuffer.set(vchar, y, x);
313        }
314    }
315
316    /// Sets foreground and background color.
317    #[inline]
318    pub fn set_color(&mut self, (fgcolor, bgcolor): (Color, Color)) {
319        self.fgcolor = fgcolor;
320        self.bgcolor = bgcolor;
321    }
322
323    /// Gets foreground and background color.
324    #[inline]
325    pub fn get_color(&self) -> (Color, Color) {
326        (self.fgcolor, self.bgcolor)
327    }
328
329    /// Gets base foreground and base background color.
330    #[inline]
331    pub fn get_base_color(&self) -> (Color, Color) {
332        (self.base_fgcolor, self.base_bgcolor)
333    }
334}
335
336impl VgaWrite for VgaOutStreamColor {
337    fn write(&mut self, ch: u8) -> Result<(), VgaStreamError> {
338        let (y, x) = VgaCursor::new().get();
339
340        let attribute = Attribute::new(self.fgcolor, self.bgcolor);
341        let mut vchar = VgaChar::new(ch, attribute);
342
343        let (y, x) = if ch == b'\n' {
344            (y + 1, 0usize)
345        } else if ch == b'\t' {
346            let new_x = if x + Self::TABSIZE < Self::WIDTH {
347                x + Self::TABSIZE
348            } else {
349                Self::WIDTH - 1
350            };
351
352            vchar.codepoint = Self::TABCHAR;
353            for i in x..new_x {
354                self.vgabuffer.set(vchar, y, i);
355            }
356
357            (y, new_x)
358        } else {
359            self.vgabuffer.set(vchar, y, x);
360
361            let y = y + ((x + 1) / Self::WIDTH);
362            let x = (x + 1) % Self::WIDTH;
363
364            (y, x)
365        };
366
367        let y = if y >= Self::HIGH {
368            self.scroll();
369            Self::HIGH - 1
370        } else {
371            y
372        };
373
374        VgaCursor::new().set(y, x);
375
376        Ok(())
377    }
378}
379
380impl Write for VgaOutStreamColor {
381    fn write_str(&mut self, s: &str) -> Result<(), Error> {
382        match self.write_buffer(s.as_bytes()) {
383            Ok(_) => Ok(()),
384            _ => Err(Error),
385        }
386    }
387}
388
389/// Print format. No return ```Result<(), Error>```.
390pub trait PrintFmt {
391    /// ## Example
392    /// ```rust
393    /// extern crate vgastream_rs;
394    /// use vgastream_rs::prelude::*;
395    ///
396    /// const DEFAULT_COLORS: (Color, Color) = (Color::BLACK, Color::WHITE);
397    /// const FGBG_COLORS: (Color, Color) = (Color::RED, Color::WHITE);
398    ///
399    /// fn main() {
400    ///		vga_screen::reset_with(DEFAULT_COLORS);
401    ///
402    ///     let mut streamcolor = VgaOutStreamColor::new(FGBG_COLORS, DEFAULT_COLORS);
403    ///     streamcolor.print_fmt(format_args!("streamcolor: Hello, World!\n"));
404    /// }
405    /// ```
406    fn print_fmt(&mut self, fmt: Arguments<'_>);
407}
408
409impl<T> PrintFmt for T
410where
411    T: Write,
412{
413    #[inline]
414    fn print_fmt(&mut self, fmt: Arguments<'_>) {
415        let _ = self.write_fmt(fmt);
416    }
417}
418
419pub mod prelude {
420    pub use crate::*;
421    pub extern crate cursor_rs;
422    pub extern crate vga_rs;
423}