Skip to content

Commit

Permalink
Add VgaFont device (#653)
Browse files Browse the repository at this point in the history
* Move from_bytes to Font

* Add VgaFont device

* Allow writing to device

* Remove api::fs::append because it is not used

* Add api::fs::is_device

* Allow reading binary files

* Add deprecation warning

* Remove usage of vga set font command

* Implement TryFrom for Font

* Fix tests
  • Loading branch information
vinc authored Sep 13, 2024
1 parent 6c7e902 commit 6561184
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 63 deletions.
2 changes: 1 addition & 1 deletion doc/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ You can also set the `TZ` environment variable to use your preferred timezone:
Add `env TZ 7200` to `/ini/boot.sh` before `shell` to save the timezone:

> read /ini/boot.sh
vga set font /ini/fonts/zap-light-8x16.psf
shell /ini/palettes/gruvbox-dark.sh
read /ini/fonts/zap-light-8x16.psf => /dev/vga/font
read /ini/banner.txt
user login
env TZ 7200
Expand Down
2 changes: 1 addition & 1 deletion dsk/ini/boot.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
vga set font /ini/fonts/zap-light-8x16.psf
shell /ini/palettes/gruvbox-dark.sh
read /ini/fonts/zap-light-8x16.psf => /dev/vga/font
read /ini/banner.txt
user login
shell
57 changes: 31 additions & 26 deletions src/api/font.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use alloc::vec::Vec;
use core::convert::TryFrom;

#[derive(Clone)]
pub struct Font {
Expand All @@ -7,39 +8,43 @@ pub struct Font {
pub data: Vec<u8>,
}

// http://www.fifi.org/doc/console-tools-dev/file-formats/psf
pub fn from_bytes(buf: &[u8]) -> Result<Font, ()> {
// Header
if buf.len() < 4 || buf[0] != 0x36 || buf[1] != 0x04 {
return Err(());
}
let mode = buf[2];
let height = buf[3];
let size = match mode {
0 | 2 => 256,
1 | 3 => 512,
_ => return Err(()),
};

// Data
let n = (4 + size * height as u16) as usize;
if buf.len() < n {
return Err(());
}
let data = buf[4..n].to_vec();
impl TryFrom<&[u8]> for Font {
type Error = ();

fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
// See: http://www.fifi.org/doc/console-tools-dev/file-formats/psf
// Header
if buf.len() < 4 || buf[0] != 0x36 || buf[1] != 0x04 {
return Err(());
}
let mode = buf[2];
let height = buf[3];
let size = match mode {
0 | 2 => 256,
1 | 3 => 512,
_ => return Err(()),
};

// TODO: Unicode Table
// Data
let n = (4 + size * height as u16) as usize;
if buf.len() < n {
return Err(());
}
let data = buf[4..n].to_vec();

Ok(Font { height, size, data })
// TODO: Unicode Table

Ok(Font { height, size, data })
}
}

#[test_case]
fn parse_psf_font() {
assert!(from_bytes(include_bytes!("../../dsk/ini/boot.sh")).is_err());
let buf = include_bytes!("../../dsk/ini/boot.sh");
assert!(Font::try_from(&buf[..]).is_err());

let font = from_bytes(
include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf")
).unwrap();
let buf = include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf");
let font = Font::try_from(&buf[..]).unwrap();
assert_eq!(font.height, 16);
assert_eq!(font.size, 256);
assert_eq!(font.data.len(), 256 * 16);
Expand Down
31 changes: 13 additions & 18 deletions src/api/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ pub fn is_file(path: &str) -> bool {
}
}

pub fn is_device(path: &str) -> bool {
if let Some(info) = syscall::info(path) {
info.is_device()
} else {
false
}
}

pub fn delete(path: &str) -> Result<(), ()> {
syscall::delete(path)
}
Expand Down Expand Up @@ -144,6 +152,7 @@ fn device_type(name: &str) -> Result<DeviceType, ()> {
"rtc" => Ok(DeviceType::RTC),
"tcp" => Ok(DeviceType::TcpSocket),
"udp" => Ok(DeviceType::UdpSocket),
"font" => Ok(DeviceType::VgaFont),
"ata" => Ok(DeviceType::Drive),
_ => Err(()),
}
Expand Down Expand Up @@ -173,14 +182,14 @@ pub fn read_to_string(path: &str) -> Result<String, ()> {

pub fn read_to_bytes(path: &str) -> Result<Vec<u8>, ()> {
if let Some(info) = syscall::info(path) {
let f = if info.is_device() {
let res = if info.is_device() {
open_device(path)
} else if info.is_dir() {
open_dir(path)
} else {
open_file(path)
};
if let Some(handle) = f {
if let Some(handle) = res {
let n = info.size() as usize;
let mut buf = vec![0; n];
if let Some(bytes) = syscall::read(handle, &mut buf) {
Expand All @@ -194,22 +203,8 @@ pub fn read_to_bytes(path: &str) -> Result<Vec<u8>, ()> {
}

pub fn write(path: &str, buf: &[u8]) -> Result<usize, ()> {
if let Some(handle) = create_file(path) {
if let Some(bytes) = syscall::write(handle, buf) {
syscall::close(handle);
return Ok(bytes);
}
}
Err(())
}

pub fn append(path: &str, buf: &[u8]) -> Result<usize, ()> {
let res = if let Some(info) = syscall::info(path) {
if info.is_device() {
open_device(path)
} else {
append_file(path)
}
let res = if is_device(path) {
open_device(path)
} else {
create_file(path)
};
Expand Down
31 changes: 20 additions & 11 deletions src/sys/fs/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::sys::console::Console;
use crate::sys::net::socket::tcp::TcpSocket;
use crate::sys::net::socket::udp::UdpSocket;
use crate::sys::rng::Random;
use crate::sys::vga::VgaFont;

use alloc::vec;
use alloc::vec::Vec;
Expand All @@ -29,24 +30,26 @@ pub enum DeviceType {
TcpSocket = 7,
UdpSocket = 8,
Drive = 9,
VgaFont = 10,
}

impl TryFrom<&[u8]> for DeviceType {
type Error = ();

fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
match buf.first().ok_or(())? {
0 => Ok(DeviceType::Null),
1 => Ok(DeviceType::File),
2 => Ok(DeviceType::Console),
3 => Ok(DeviceType::Random),
4 => Ok(DeviceType::Uptime),
5 => Ok(DeviceType::Realtime),
6 => Ok(DeviceType::RTC),
7 => Ok(DeviceType::TcpSocket),
8 => Ok(DeviceType::UdpSocket),
9 => Ok(DeviceType::Drive),
_ => Err(()),
0 => Ok(DeviceType::Null),
1 => Ok(DeviceType::File),
2 => Ok(DeviceType::Console),
3 => Ok(DeviceType::Random),
4 => Ok(DeviceType::Uptime),
5 => Ok(DeviceType::Realtime),
6 => Ok(DeviceType::RTC),
7 => Ok(DeviceType::TcpSocket),
8 => Ok(DeviceType::UdpSocket),
9 => Ok(DeviceType::Drive),
10 => Ok(DeviceType::VgaFont),
_ => Err(()),
}
}
}
Expand Down Expand Up @@ -83,6 +86,7 @@ pub enum Device {
RTC(RTC),
TcpSocket(TcpSocket),
UdpSocket(UdpSocket),
VgaFont(VgaFont),
Drive(Drive),
}

Expand All @@ -100,6 +104,7 @@ impl TryFrom<&[u8]> for Device {
DeviceType::RTC => Ok(Device::RTC(RTC::new())),
DeviceType::TcpSocket => Ok(Device::TcpSocket(TcpSocket::new())),
DeviceType::UdpSocket => Ok(Device::UdpSocket(UdpSocket::new())),
DeviceType::VgaFont => Ok(Device::VgaFont(VgaFont::new())),
DeviceType::Drive if buf.len() > 2 => {
let bus = buf[1];
let dsk = buf[2];
Expand Down Expand Up @@ -158,6 +163,7 @@ impl FileIO for Device {
Device::RTC(io) => io.read(buf),
Device::TcpSocket(io) => io.read(buf),
Device::UdpSocket(io) => io.read(buf),
Device::VgaFont(io) => io.read(buf),
Device::Drive(io) => io.read(buf),
}
}
Expand All @@ -173,6 +179,7 @@ impl FileIO for Device {
Device::RTC(io) => io.write(buf),
Device::TcpSocket(io) => io.write(buf),
Device::UdpSocket(io) => io.write(buf),
Device::VgaFont(io) => io.write(buf),
Device::Drive(io) => io.write(buf),
}
}
Expand All @@ -188,6 +195,7 @@ impl FileIO for Device {
Device::RTC(io) => io.close(),
Device::TcpSocket(io) => io.close(),
Device::UdpSocket(io) => io.close(),
Device::VgaFont(io) => io.close(),
Device::Drive(io) => io.close(),
}
}
Expand All @@ -203,6 +211,7 @@ impl FileIO for Device {
Device::RTC(io) => io.poll(event),
Device::TcpSocket(io) => io.poll(event),
Device::UdpSocket(io) => io.poll(event),
Device::VgaFont(io) => io.poll(event),
Device::Drive(io) => io.poll(event),
}
}
Expand Down
36 changes: 36 additions & 0 deletions src/sys/vga.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::api::font::Font;
use crate::api::fs::{FileIO, IO};
use crate::api::vga::color;
use crate::api::vga::{Color, Palette};
use crate::sys;

use alloc::string::String;
use bit_field::BitField;
use core::convert::TryFrom;
use core::cmp;
use core::fmt;
use core::fmt::Write;
Expand Down Expand Up @@ -461,6 +463,39 @@ impl fmt::Write for Writer {
}
}

#[derive(Debug, Clone)]
pub struct VgaFont;

impl VgaFont {
pub fn new() -> Self {
Self
}
}

impl FileIO for VgaFont {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, ()> {
Err(()) // TODO
}

fn write(&mut self, buf: &[u8]) -> Result<usize, ()> {
if let Ok(font) = Font::try_from(buf) {
set_font(&font);
Ok(buf.len()) // TODO: Use font.data.len() ?
} else {
Err(())
}
}

fn close(&mut self) {}

fn poll(&mut self, event: IO) -> bool {
match event {
IO::Read => false, // TODO
IO::Write => true,
}
}
}

#[doc(hidden)]
pub fn print_fmt(args: fmt::Arguments) {
interrupts::without_interrupts(||
Expand Down Expand Up @@ -489,6 +524,7 @@ pub fn is_printable(c: u8) -> bool {
matches!(c, 0x20..=0x7E | 0x08 | 0x0A | 0x0D | 0x7F..=0xFF)
}

// TODO: Remove this
pub fn set_font(font: &Font) {
interrupts::without_interrupts(||
WRITER.lock().set_font(font)
Expand Down
2 changes: 2 additions & 0 deletions src/usr/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub fn copy_files(verbose: bool) {
create_dir("/dev/ata/1", verbose);
create_dir("/dev/clk", verbose); // Clock
create_dir("/dev/net", verbose); // Network
create_dir("/dev/vga", verbose);

create_dev("/dev/ata/0/0", "ata-0-0", verbose);
create_dev("/dev/ata/0/1", "ata-0-1", verbose);
Expand All @@ -53,6 +54,7 @@ pub fn copy_files(verbose: bool) {
create_dev("/dev/console", "console", verbose);
create_dev("/dev/net/tcp", "tcp", verbose);
create_dev("/dev/net/udp", "udp", verbose);
create_dev("/dev/vga/font", "font", verbose);

copy_file!("/ini/banner.txt", verbose);
copy_file!("/ini/boot.sh", verbose);
Expand Down
4 changes: 2 additions & 2 deletions src/usr/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
} else if let Some(info) = syscall::info(path) {
if info.is_file() {
if let Ok(contents) = api::fs::read_to_string(path) {
print!("{}", contents);
if let Ok(buf) = api::fs::read_to_bytes(path) {
syscall::write(1, &buf);
Ok(())
} else {
error!("Could not read '{}'", path);
Expand Down
10 changes: 7 additions & 3 deletions src/usr/vga.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use crate::api::console::Style;
use crate::api::fs;
use crate::api::process::ExitCode;
use crate::api::font::Font;
use crate::api::vga::palette;
use crate::{api, sys};
use crate::sys;

// TODO: Remove this command when everything can be done from userspace
use core::convert::TryFrom;

// TODO: Remove this command in the next version of MOROS
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
if args.len() == 1 {
help();
Expand All @@ -18,8 +21,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
"set" => {
if args.len() == 4 && args[2] == "font" {
warning!("Use VGA font device");
if let Ok(buf) = fs::read_to_bytes(args[3]) {
if let Ok(font) = api::font::from_bytes(&buf) {
if let Ok(font) = Font::try_from(buf.as_slice()) {
sys::vga::set_font(&font);
Ok(())
} else {
Expand Down
2 changes: 1 addition & 1 deletion www/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ <h2>Time</h2>
<p>Add <code>env TZ 7200</code> to <code>/ini/boot.sh</code> before <code>shell</code> to save the timezone:</p>

<pre><code>&gt; read /ini/boot.sh
vga set font /ini/fonts/zap-light-8x16.psf
shell /ini/palettes/gruvbox-dark.sh
read /ini/fonts/zap-light-8x16.psf =&gt; /dev/vga/font
read /ini/banner.txt
user login
env TZ 7200
Expand Down

0 comments on commit 6561184

Please sign in to comment.