use nimbus roman, theme config, lto

license, philosophy, more shippori chars, slight code shuffling
This commit is contained in:
stjet
2025-02-21 15:09:51 +00:00
parent cb766ce8a5
commit e769bc1452
888 changed files with 5349 additions and 2876 deletions

View File

@@ -61,22 +61,22 @@ impl WindowLike for AudioPlayer {
}
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
let mut instructions = vec![DrawInstructions::Text([2, self.dimensions[1] - LINE_HEIGHT], vec!["times-new-roman".to_string()], if self.command.len() > 0 { self.command.clone() } else { self.response.clone() }, theme_info.text, theme_info.background, None, None)];
let mut instructions = vec![DrawInstructions::Text([2, self.dimensions[1] - LINE_HEIGHT], vec!["nimbus-roman".to_string()], if self.command.len() > 0 { self.command.clone() } else { self.response.clone() }, theme_info.text, theme_info.background, None, None)];
if let Some(sink) = &self.sink {
if sink.len() > 0 {
let current = &self.queue[self.queue.len() - sink.len()];
let current_name = current.0.file_name().unwrap().to_string_lossy().into_owned();
instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - current_name.len() * MONO_WIDTH as usize / 2, 2], vec!["times-new-romono".to_string(), "shippori-mincho".to_string()], current_name.clone(), theme_info.text, theme_info.background, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - current_name.len() * MONO_WIDTH as usize / 2, 2], vec!["nimbus-romono".to_string(), "shippori-mincho".to_string()], current_name.clone(), theme_info.text, theme_info.background, Some(0), Some(MONO_WIDTH)));
if let Some(artist) = &current.2 {
let artist_string = "by ".to_string() + &artist;
instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - artist_string.len() * MONO_WIDTH as usize / 2, LINE_HEIGHT + 2], vec!["times-new-romono".to_string()], artist_string, theme_info.text, theme_info.background, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - artist_string.len() * MONO_WIDTH as usize / 2, LINE_HEIGHT + 2], vec!["nimbus-romono".to_string()], artist_string, theme_info.text, theme_info.background, Some(0), Some(MONO_WIDTH)));
}
let time_string = format!("{}/{}", format_seconds(sink.get_pos().as_secs()), format_seconds(current.1));
instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - time_string.len() * MONO_WIDTH as usize / 2, LINE_HEIGHT * 2 + 2], vec!["times-new-romono".to_string()], time_string, theme_info.text, theme_info.background, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([self.dimensions[0] / 2 - time_string.len() * MONO_WIDTH as usize / 2, LINE_HEIGHT * 2 + 2], vec!["nimbus-romono".to_string()], time_string, theme_info.text, theme_info.background, Some(0), Some(MONO_WIDTH)));
}
} else {
instructions.push(DrawInstructions::Text([2, 2], vec!["times-new-roman".to_string()], "type to write commands, enter to execute.".to_string(), theme_info.text, theme_info.background, None, None));
instructions.push(DrawInstructions::Text([2, 2 + LINE_HEIGHT], vec!["times-new-roman".to_string()], "See help in start menu for commands.".to_string(), theme_info.text, theme_info.background, None, None));
instructions.push(DrawInstructions::Text([2, 2], vec!["nimbus-roman".to_string()], "type to write commands, enter to execute.".to_string(), theme_info.text, theme_info.background, None, None));
instructions.push(DrawInstructions::Text([2, 2 + LINE_HEIGHT], vec!["nimbus-roman".to_string()], "See help in start menu for commands.".to_string(), theme_info.text, theme_info.background, None, None));
}
//
instructions
@@ -157,7 +157,7 @@ impl AudioPlayer {
if line.ends_with("/*") {
queue.extend(get_all_files(concat_paths(&self.base_directory, &line[..line.len() - 2]).unwrap()));
} else if line.len() > 0 {
queue.push(concat_paths(&self.base_directory, &(line.to_owned() + ".mp3")).unwrap());
queue.push(concat_paths(&self.base_directory, &(line.to_owned() + if line.ends_with(".mp3") { "" } else { ".mp3" })).unwrap());
}
}
queue
@@ -171,7 +171,8 @@ impl AudioPlayer {
self.queue = Vec::new();
for item in &queue {
let file = BufReader::new(File::open(item).unwrap());
let decoded = Decoder::new(file).unwrap();
//slightly faster for mp3s? since it doesn't need to check if it is .wav, etc. but maybe not
let decoded = if item.ends_with(".mp3") { Decoder::new_mp3(file) } else { Decoder::new(file) }.unwrap();
self.queue.push((item.clone(), decoded.total_duration().unwrap().as_secs(), Tag::new().read_from_path(item.clone()).unwrap().artist().map(|s| s.to_string())));
sink.append(decoded);
}

View File

@@ -109,7 +109,7 @@ impl WindowLike for FileExplorer {
let mut instructions = Vec::new();
if self.state == State::List {
//top bar with path name
instructions.push(DrawInstructions::Text([5, 0], vec!["times-new-roman".to_string(), "shippori-mincho".to_string()], "Current: ".to_string() + &self.current_path.to_string_lossy().to_string(), theme_info.text, theme_info.background, None, None));
instructions.push(DrawInstructions::Text([5, 0], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], "Current: ".to_string() + &self.current_path.to_string_lossy().to_string(), theme_info.text, theme_info.background, None, None));
//the actual files and directories
let mut start_y = HEIGHT;
let mut i = self.top_position;
@@ -128,7 +128,7 @@ impl WindowLike for FileExplorer {
} else {
name.unwrap()
};
instructions.push(DrawInstructions::Text([5, start_y], vec!["times-new-roman".to_string(), "shippori-mincho".to_string()], name, if is_selected { theme_info.top_text } else { theme_info.text }, if is_selected { theme_info.top } else { theme_info.background }, None, None));
instructions.push(DrawInstructions::Text([5, start_y], vec!["nimbus-roman".to_string(), "shippori-mincho".to_string()], name, if is_selected { theme_info.top_text } else { theme_info.text }, if is_selected { theme_info.top } else { theme_info.background }, None, None));
start_y += HEIGHT;
i += 1;
}
@@ -136,7 +136,7 @@ impl WindowLike for FileExplorer {
let metadata = self.metadata.clone().unwrap();
let mut start_y = HEIGHT;
let bytes_len = metadata.len();
instructions.push(DrawInstructions::Text([5, start_y], vec!["times-new-roman".to_string()], format!("Size: {} mb ({} b)", bytes_len / (1024_u64).pow(2), bytes_len), theme_info.text, theme_info.background, None, None));
instructions.push(DrawInstructions::Text([5, start_y], vec!["nimbus-roman".to_string()], format!("Size: {} mb ({} b)", bytes_len / (1024_u64).pow(2), bytes_len), theme_info.text, theme_info.background, None, None));
start_y += HEIGHT;
//todo: other stuff
}

View File

@@ -1,7 +1,150 @@
use linux_framebuffer::Framebuffer;
use std::process::{ Command, Stdio };
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use std::io::{ stdin, stdout, BufReader, BufRead, Write };
use std::process::exit;
use std::env;
use ming_wm::framebuffer::FramebufferInfo;
use ming_wm::window_manager::init;
use linux_framebuffer::Framebuffer;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use termion::{ clear, cursor };
use ming_wm::framebuffer::{ FramebufferWriter, FramebufferInfo };
use ming_wm::window_manager::{ WindowManager, KeyChar };
use ming_wm::utils::key_to_char;
use ming_wm::messages::*;
pub enum ThreadMessage {
KeyChar(KeyChar),
Touch(usize, usize),
Clear,
Exit,
}
fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) {
let args: Vec<_> = env::args().collect();
let rotate = args.contains(&"rotate".to_string());
let framebuffer_info = if rotate {
FramebufferInfo {
byte_len: framebuffer_info.byte_len,
width: framebuffer_info.height,
height: framebuffer_info.width,
bytes_per_pixel: framebuffer_info.bytes_per_pixel,
stride: framebuffer_info.height,
old_stride: Some(framebuffer_info.stride),
}
} else {
framebuffer_info
};
let dimensions = [framebuffer_info.width, framebuffer_info.height];
let grayscale = args.contains(&"grayscale".to_string()) || args.contains(&"greyscale".to_string());
let mut writer: FramebufferWriter = FramebufferWriter::new(grayscale);
writer.init(framebuffer_info.clone());
let mut wm: WindowManager = WindowManager::new(writer, framebuffer, dimensions, rotate, grayscale);
let mut stdout = stdout().into_raw_mode().unwrap();
write!(stdout, "{}", clear::All).unwrap();
write!(stdout, "{}", cursor::Hide).unwrap();
stdout.flush().unwrap();
wm.draw(None, false);
let (tx, rx) = mpsc::channel();
let tx1 = tx.clone();
//read key presses
thread::spawn(move || {
let stdin = stdin().lock();
for c in stdin.keys() {
if let Some(kc) = key_to_char(c.unwrap()) {
//do not allow exit when locked unless debugging
//if kc == KeyChar::Alt('E') {
if kc == KeyChar::Alt('E') {
tx.send(ThreadMessage::Exit).unwrap();
} else {
tx.send(ThreadMessage::KeyChar(kc)).unwrap();
}
}
thread::sleep(Duration::from_millis(1));
}
});
let touch = args.contains(&"touch".to_string());
//read touchscreen presses (hopefully)
thread::spawn(move || {
//spawn evtest, parse it for touch coords
if touch {
let mut evtest = Command::new("evtest").arg("/dev/input/by-path/first-touchscreen").stdout(Stdio::piped()).spawn().unwrap();
let reader = BufReader::new(evtest.stdout.as_mut().unwrap());
let mut x: Option<usize> = None;
let mut y: Option<usize> = None;
for line in reader.lines() {
let line = line.unwrap();
if line.contains(&"ABS_X), value ") || line.contains(&"ABS_Y), value ") {
let value: Vec<_> = line.split("), value ").collect();
let value = value[value.len() - 1].parse::<usize>().unwrap();
if line.contains(&"ABS_X") {
x = Some(value);
} else {
y = Some(value);
}
if x.is_some() && y.is_some() {
let (x2, y2) = if rotate {
(y.unwrap(), dimensions[0] - x.unwrap())
} else {
(x.unwrap(), y.unwrap())
};
//top right, clear
//useful sometimes, I think.
if x2 > dimensions[0] - 100 && y2 < 100 {
tx1.send(ThreadMessage::Clear).unwrap();
}
tx1.send(ThreadMessage::Touch(x2, y2)).unwrap();
x = None;
y = None;
}
}
thread::sleep(Duration::from_millis(1));
}
}
});
if touch {
//opens osk
wm.handle_message(WindowManagerMessage::Touch(1, 1));
}
for message in rx {
match message {
ThreadMessage::KeyChar(kc) => wm.handle_message(WindowManagerMessage::KeyChar(kc.clone())),
ThreadMessage::Touch(x, y) => wm.handle_message(WindowManagerMessage::Touch(x, y)),
ThreadMessage::Clear => {
write!(stdout, "{}", clear::All).unwrap();
stdout.flush().unwrap();
},
ThreadMessage::Exit => {
if !wm.locked {
write!(stdout, "{}", cursor::Show).unwrap();
stdout.suspend_raw_mode().unwrap();
exit(0);
}
},
};
}
}
fn main() {
let fb = Framebuffer::new("/dev/fb0").unwrap();

View File

@@ -14,7 +14,7 @@ use ming_wm::ipc::listen;
const MONO_WIDTH: u8 = 10;
const LINE_HEIGHT: usize = 18;
const PADDING: usize = 2;
const BAND_HEIGHT: usize = 18;
const BAND_HEIGHT: usize = 19;
struct FileInfo {
pub name: String,
@@ -185,7 +185,7 @@ impl WindowLike for Malvim {
self.state = State::None;
} else if self.state == State::Maybeg {
if key_press.key == 'g' {
current_file.line_pos = self.maybe_num.unwrap_or(0);
current_file.line_pos = self.maybe_num.unwrap_or(1) - 1;
if current_file.line_pos >= current_file.content.len() {
current_file.line_pos = current_file.content.len() - 1;
}
@@ -372,7 +372,7 @@ impl WindowLike for Malvim {
};
instructions.extend(vec![
DrawInstructions::Rect([used_width, 2], [future_used_width, BAND_HEIGHT - 2], background),
DrawInstructions::Text([used_width + 2, 2], vec!["times-new-romono".to_string()], if file_info.changed { "+ ".to_string() } else { String::new() } + &file_info.name, theme_info.alt_text, background, Some(0), Some(MONO_WIDTH)),
DrawInstructions::Text([used_width + 2, 2], vec!["nimbus-romono".to_string()], if file_info.changed { "+ ".to_string() } else { String::new() } + &file_info.name, theme_info.alt_text, background, Some(0), Some(MONO_WIDTH)),
]);
used_width = future_used_width;
}
@@ -390,13 +390,13 @@ impl WindowLike for Malvim {
//write line num text (if start of line)
let y0 = BAND_HEIGHT + rel_line_num * LINE_HEIGHT + PADDING;
if line.0 {
instructions.push(DrawInstructions::Text([PADDING, y0], vec!["times-new-romono".to_string()], line.1.to_string(), theme_info.alt_secondary, theme_info.alt_background, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([PADDING, y0], vec!["nimbus-romono".to_string()], (line.1 + 1).to_string(), theme_info.alt_secondary, theme_info.alt_background, Some(0), Some(MONO_WIDTH)));
sub_line_num = 0;
}
let x1 = current.line_num_width + PADDING * 2;
//write actual line
//line.2
instructions.push(DrawInstructions::Text([x1, y0], vec!["times-new-romono".to_string()], line.2.clone(), theme_info.alt_text, theme_info.alt_background, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([x1, y0], vec!["nimbus-romono".to_string()], line.2.clone(), theme_info.alt_text, theme_info.alt_background, Some(0), Some(MONO_WIDTH)));
sub_line_num += 1;
let max = sub_line_num * current.max_chars_per_line;
let min = max - current.max_chars_per_line;
@@ -406,26 +406,26 @@ impl WindowLike for Malvim {
instructions.push(DrawInstructions::Rect(top_left, [MONO_WIDTH as usize, LINE_HEIGHT], theme_info.top));
//draw the char over it
if line.2.len() > 0 {
instructions.push(DrawInstructions::Text(top_left, vec!["times-new-romono".to_string()], line.2.chars().nth(current_file.cursor_pos - min).unwrap().to_string(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text(top_left, vec!["nimbus-romono".to_string()], line.2.chars().nth(current_file.cursor_pos - min).unwrap().to_string(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
}
}
}
}
//bottom blue band stuff
//write mode
instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT * 2 + 1], vec!["times-new-romono".to_string()], self.mode.to_string(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT * 2 + 2], vec!["nimbus-romono".to_string()], self.mode.to_string(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
let file_status;
if self.files.len() > 0 {
file_status = self.files[self.current_file_index].name.clone();
} else {
file_status = "No file open".to_string();
}
instructions.push(DrawInstructions::Text([self.dimensions[0] - file_status.len() * (MONO_WIDTH as usize), self.dimensions[1] - BAND_HEIGHT * 2 + 1], vec!["times-new-romono".to_string()], file_status, theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([self.dimensions[0] - file_status.len() * (MONO_WIDTH as usize), self.dimensions[1] - BAND_HEIGHT * 2 + 2], vec!["nimbus-romono".to_string()], file_status, theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
//write command or bottom message
if self.mode == Mode::Command {
instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT], vec!["times-new-romono".to_string()], ":".to_string() + &self.command.clone().unwrap_or("".to_string()), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT + 2], vec!["nimbus-romono".to_string()], ":".to_string() + &self.command.clone().unwrap_or("".to_string()), theme_info.top_text, theme_info.alt_background, Some(0), Some(MONO_WIDTH)));
} else if self.mode == Mode::Normal && self.bottom_message.is_some() {
instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT], vec!["times-new-romono".to_string()], self.bottom_message.clone().unwrap(), theme_info.top_text, theme_info.top, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([0, self.dimensions[1] - BAND_HEIGHT + 2], vec!["nimbus-romono".to_string()], self.bottom_message.clone().unwrap(), theme_info.top_text, theme_info.alt_background, Some(0), Some(MONO_WIDTH)));
}
instructions
}

View File

@@ -145,8 +145,8 @@ impl WindowLike for Minesweeper {
fn draw(&self, theme_info: &ThemeInfo) -> Vec<DrawInstructions> {
if self.state == State::Seed {
vec![
DrawInstructions::Text([4, 4], vec!["times-new-roman".to_string()], "Type in random characters to initalise the seed".to_string(), theme_info.text, theme_info.background, None, None),
DrawInstructions::Text([4, 4 + 16], vec!["times-new-roman".to_string()], self.random_chars.clone(), theme_info.text, theme_info.background, None, None),
DrawInstructions::Text([4, 4], vec!["nimbus-roman".to_string()], "Type in random characters to initalise the seed".to_string(), theme_info.text, theme_info.background, None, None),
DrawInstructions::Text([4, 4 + 16], vec!["nimbus-roman".to_string()], self.random_chars.clone(), theme_info.text, theme_info.background, None, None),
]
} else {
let mut instructions = vec![
@@ -183,7 +183,7 @@ impl WindowLike for Minesweeper {
let tile = &self.tiles[y][x];
if tile.revealed {
if tile.mine {
instructions.push(DrawInstructions::Text([x * tile_size + tile_size / 2 + 2, y * tile_size + tile_size / 2], vec!["times-new-roman".to_string()], "x".to_string(), [255, 0, 0], theme_info.background, None, None));
instructions.push(DrawInstructions::Text([x * tile_size + tile_size / 2 + 2, y * tile_size + tile_size / 2], vec!["nimbus-roman".to_string()], "x".to_string(), [255, 0, 0], theme_info.background, None, None));
} else {
let color = match tile.touching {
1 => [0, 0, 255],
@@ -196,7 +196,7 @@ impl WindowLike for Minesweeper {
//8
_ => [128, 128, 128],
};
instructions.push(DrawInstructions::Text([x * tile_size + tile_size / 2 + 5, y * tile_size + tile_size / 2 + 2], vec!["times-new-roman".to_string()], tile.touching.to_string(), color, theme_info.background, None, None));
instructions.push(DrawInstructions::Text([x * tile_size + tile_size / 2 + 5, y * tile_size + tile_size / 2 + 2], vec!["nimbus-roman".to_string()], tile.touching.to_string(), color, theme_info.background, None, None));
}
} else {
let top_left = [x * tile_size + 6, y * tile_size + 5];
@@ -214,15 +214,15 @@ impl WindowLike for Minesweeper {
//right bottom
DrawInstructions::Rect([top_left[0] + tile_size - 4, top_left[1] + 3], [3, tile_size - 4], [128, 128, 128]),
//
DrawInstructions::Text([x * tile_size + tile_size / 2 - 2, y * tile_size + tile_size / 2], vec!["times-new-roman".to_string()], u8_to_hex((y * 16 + x) as u8), theme_info.text, theme_info.background, None, None),
DrawInstructions::Text([x * tile_size + tile_size / 2 - 2, y * tile_size + tile_size / 2], vec!["nimbus-roman".to_string()], u8_to_hex((y * 16 + x) as u8), theme_info.text, theme_info.background, None, None),
]);
}
}
}
if self.state == State::Lost {
instructions.extend(vec![DrawInstructions::Text([4, 4], vec!["times-new-roman".to_string()], "You LOST!!! Press a key to play again.".to_string(), theme_info.text, theme_info.background, None, None)]);
instructions.extend(vec![DrawInstructions::Text([4, 4], vec!["nimbus-roman".to_string()], "You LOST!!! Press a key to play again.".to_string(), theme_info.text, theme_info.background, None, None)]);
} else if self.state == State::Won {
instructions.extend(vec![DrawInstructions::Text([4, 4], vec!["times-new-roman".to_string()], "You WON!!! Press a key to play again.".to_string(), theme_info.text, theme_info.background, None, None)]);
instructions.extend(vec![DrawInstructions::Text([4, 4], vec!["nimbus-roman".to_string()], "You WON!!! Press a key to play again.".to_string(), theme_info.text, theme_info.background, None, None)]);
}
instructions
}

View File

@@ -144,7 +144,7 @@ impl WindowLike for Reversi {
for x in 0..8 {
let tile = &self.tiles[y][x];
if tile == &Tile::Empty {
instructions.push(DrawInstructions::Text([x * square_width + square_width / 2, y * square_width + square_width / 2], vec!["times-new-roman".to_string()], format!("{}{}", y, x), theme_info.text, REVERSI_GREEN, None, None));
instructions.push(DrawInstructions::Text([x * square_width + square_width / 2, y * square_width + square_width / 2], vec!["nimbus-roman".to_string()], format!("{}{}", y, x), theme_info.text, REVERSI_GREEN, None, None));
if valid_moves_contains(&self.valid_moves, &[x, y]).is_some() {
//yellow border
instructions.extend([
@@ -161,7 +161,7 @@ impl WindowLike for Reversi {
}
if self.state != State::InProgress {
instructions.push(DrawInstructions::Rect([0, 0], [self.dimensions[0], 25], theme_info.background));
instructions.push(DrawInstructions::Text([4, 4], vec!["times-new-roman".to_string()], if self.state == State::WhiteWin {
instructions.push(DrawInstructions::Text([4, 4], vec!["nimbus-roman".to_string()], if self.state == State::WhiteWin {
"White wins, press any key to restart"
} else if self.state == State::BlackWin {
"Black wins, press any key to restart"

View File

@@ -69,7 +69,7 @@ impl WindowLike for Terminal {
self.calc_actual_lines();
self.actual_line_num = self.actual_lines.len().checked_sub(self.get_max_lines()).unwrap_or(0);
WindowMessageResponse::JustRedraw
} else if key_press.key.len_utf8() == 1 {
} else {
//update
let running_process = self.running_process.as_mut().unwrap();
if let Some(status) = running_process.try_wait().unwrap() {
@@ -90,10 +90,6 @@ impl WindowLike for Terminal {
//still running
WindowMessageResponse::DoNothing
}
} else {
//esc key (crash happens if esc key is entered and deleted, so prevent it from being entered)
//but if we want to support eg Chinese we need to properly handle multi-byte chars (todo)
WindowMessageResponse::DoNothing
}
},
WindowMessage::CtrlKeyPress(key_press) => {
@@ -133,7 +129,7 @@ impl WindowLike for Terminal {
break;
}
let line = self.actual_lines[line_num].clone();
instructions.push(DrawInstructions::Text([PADDING, text_y], vec!["times-new-romono".to_string()], line, theme_info.alt_text, theme_info.alt_background, Some(0), Some(MONO_WIDTH)));
instructions.push(DrawInstructions::Text([PADDING, text_y], vec!["nimbus-romono".to_string()], line, theme_info.alt_text, theme_info.alt_background, Some(0), Some(MONO_WIDTH)));
text_y += LINE_HEIGHT;
}
instructions