vgastream-rs 0.1.0

High level VGA(0xB8000) library in freestanding Rust.
Documentation
//! ## vgastream-rs
//! - High level VGA(0xB8000) library in freestanding Rust.
//! - provides ```println``` ```print``` ```eprintln``` ```eprint``` macros.
//!
//! ## Example
//! ```rust
//! /*main.rs*/
//!
//! #![no_std]
//! #![no_main]
//!
//! mod multiboot;
//! mod panic;
//!
//! extern crate vgastream_rs;
//! use vgastream_rs::prelude::*;
//!
//! #[no_mangle]
//! pub extern "C" fn _start() -> ! {
//!     use vga_rs::Color;
//!     VgaOutStream::new().reset_with(Color::BLACK, Color::WHITE);
//!
//!     let _ = main();
//!     unsafe { panic::halt() }
//! }
//!
//! fn main() {
//!     println!("Hello, World!");
//! }
//!
//! ```
//!

#![no_std]

extern crate vga_rs;
use vga_rs::*;

extern crate cursor_rs;
use cursor_rs::*;

/// Provides vga stream.
#[derive(Debug)]
pub struct VgaOutStream {
    vgabuffer: VgaBuffer,
}

impl VgaOutStream {
    pub const HIGH: usize = VgaBuffer::HIGH;
    pub const WIDTH: usize = VgaBuffer::WIDTH;

    pub const TABSIZE: usize = 4;
    pub const TABCHAR: u8 = b' ';

    /// Constructs a VgaOutStream from VgaBuffer.
    #[inline]
    pub fn new_from(vgabuffer: VgaBuffer) -> Self {
        Self {
            vgabuffer: vgabuffer,
        }
    }

    /// Constructs a VgaOutStream that wraps VgaBuffer.
    #[inline]
    pub fn new() -> Self {
        Self {
            vgabuffer: VgaBuffer::new(),
        }
    }

    /// Resets screen with foreground color and background color.
    pub fn reset_with(&mut self, fgcolor: vga_rs::Color, bgcolor: vga_rs::Color) {
        for vchar in self.vgabuffer.as_mut_slice().iter_mut() {
            let attribute = Attribute::new(fgcolor, bgcolor);
            vchar.set_volatile(VgaChar::new(0u8, attribute));
        }

        VgaCursor::new().set(0, 0);
    }

    /// Resets screen.
    pub fn reset(&mut self) {
        for vchar_ref in self.vgabuffer.as_mut_slice().iter_mut() {
            let mut vchar = vchar_ref.get_volatile();
            vchar.codepoint = 0u8;
            vchar_ref.set_volatile(vchar);
        }

        VgaCursor::new().set(0, 0);
    }

    /// Shifts screen.
    pub fn scroll(&mut self) {
        for y in 0usize..(Self::HIGH - 1) {
            for x in 0usize..(Self::WIDTH) {
                let vchar = self.vgabuffer.get(y + 1, x);
                self.vgabuffer.set(vchar, y, x);
            }
        }

        for x in 0..Self::WIDTH {
            let y = Self::HIGH - 1;
            let attribute = Attribute::from_u8(self.vgabuffer.get(y, x).attribute);
            let vchar = VgaChar::new(0u8, attribute);
            self.vgabuffer.set(vchar, y, x);
        }
    }

    /// Gets VgaBuffer.
    pub fn vgabuffer(&mut self) -> &mut VgaBuffer {
        &mut self.vgabuffer
    }
}

/// Error for VgaRead and VgaWrite.
pub enum VgaStreamError {
    OutOfRange,
    OverBuffer,
}

/// Read vga strean.
pub trait VgaRead {
    fn read(&self, y: usize, x: usize) -> Result<u8, VgaStreamError>;

    fn read_buffer(&self, buffer: &mut [u8], y: usize, x: usize) -> Result<(), VgaStreamError>;
}

/// Write vga strean.
pub trait VgaWrite {
    /// Writes one charector on screen. Never return ```None```.
    /// ```rust
    /// let mut stream = VgaOutStream::new();
    /// stream.write(b'A')?;
    fn write(&mut self, ch: u8) -> Result<(), VgaStreamError>;

    /// Writes buffer of charector on screen. Never return ```None```.
    /// ```rust
    /// let mut stream = VgaOutStream::new();
    /// stream.write_buffer(b"Hello,World")?;
    fn write_buffer(&mut self, text: &[u8]) -> Result<(), VgaStreamError> {
        for &ch in text.iter() {
            self.write(ch)?;
        }

        Ok(())
    }
}

impl VgaWrite for VgaOutStream {
    fn write(&mut self, ch: u8) -> Result<(), VgaStreamError> {
        let (y, x) = VgaCursor::new().get();
        let mut vchar = self.vgabuffer.get(y, x);

        vchar.codepoint = ch;

        let (y, x) = if ch == b'\n' {
            (y + 1, 0usize)
        } else if ch == b'\t' {
            let new_x = if x + Self::TABSIZE < Self::WIDTH {
                x + Self::TABSIZE
            } else {
                Self::WIDTH - 1
            };

            vchar.codepoint = Self::TABCHAR;
            for i in x..new_x {
                self.vgabuffer.set(vchar, y, i);
            }

            (y, new_x)
        } else {
            self.vgabuffer.set(vchar, y, x);

            let y = y + ((x + 1) / Self::WIDTH);
            let x = (x + 1) % Self::WIDTH;

            (y, x)
        };

        let y = if y >= Self::HIGH {
            self.scroll();
            Self::HIGH - 1
        } else {
            y
        };

        VgaCursor::new().set(y, x);

        Ok(())
    }
}

use core::fmt::{Error, Write};

impl Write for VgaOutStream {
    fn write_str(&mut self, s: &str) -> Result<(), Error> {
        match self.write_buffer(s.as_bytes()) {
            Ok(_) => Ok(()),
            _ => Err(Error),
        }
    }
}

/// print macro for VGA.
#[macro_export]
macro_rules! print {
		($($arg:tt)*) => {{
			let _ = core::fmt::write(&mut vgastream_rs::VgaOutStream::new(),format_args!($($arg)*));
		}};
	}

/// println macro for VGA.
/// ```rust
/// fn main() {
///     println!("Hello, World!");
/// }
///
/// ```
#[macro_export]
macro_rules! println {
		($($arg:tt)*) => {{
			print!($($arg)*);
			print!("\n");
		}};
	}

/// eprint macro for VGA.
#[macro_export]
macro_rules! eprint {
		($($arg:tt)*) => {{
			print!($($arg)*);
		}};
	}

/// eprintln macro for VGA.
/// ```rust
/// #[panic_handler]
/// fn panic(info: &PanicInfo) -> ! {
///     eprintln!("{}", info);
/// 	loop
/// 	{
/// 		unsafe { halt() }
/// 	}
/// }
/// ```
#[macro_export]
macro_rules! eprintln {
		($($arg:tt)*) => {{
			println!($($arg)*);
		}};
	}

pub mod prelude {
    pub use crate::*;
    pub extern crate cursor_rs;
    pub extern crate vga_rs;
}