rename libraries

fix

remove
This commit is contained in:
2025-11-17 16:37:02 -07:00
parent 55347be232
commit d77320d445
34 changed files with 129 additions and 128 deletions

View File

@@ -0,0 +1,9 @@
[package]
name = "calculator"
version = "0.1.0"
edition = "2024"
[dependencies]
userlib = { path = "../../userlib" }
embedded-graphics = "0.8.1"
embedded-layout = "0.4.2"

View File

@@ -0,0 +1,28 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("../memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg-bins=-Tmemory.x");
}

View File

@@ -0,0 +1,200 @@
#![no_std]
#![no_main]
extern crate alloc;
use alloc::{format, string::String, vec, vec::Vec};
use core::panic::PanicInfo;
use embedded_graphics::{
Drawable,
geometry::{Dimensions, Point},
mono_font::{MonoTextStyle, ascii::FONT_7X14, iso_8859_1::FONT_10X20},
pixelcolor::Rgb565,
prelude::{Primitive, RgbColor},
primitives::{PrimitiveStyle, Rectangle},
text::Text,
};
use embedded_layout::{
View,
align::{Align, horizontal, vertical},
layout::linear::LinearLayout,
prelude::Chain,
};
use userlib::{
display::Display,
get_key,
keyboard::{KeyCode, KeyState},
println,
};
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()
}
pub fn main() {
println!("Starting Calculator app");
let mut display = Display::take().unwrap();
let mut input = vec!['e', 'x', 'p', 'r', ':', ' '];
let input_min = input.len();
let mut dirty = true;
let mut last_area: Option<(Rectangle, Rectangle)> = None;
LinearLayout::vertical(Chain::new(Text::new(
"Calculator!",
Point::zero(),
MonoTextStyle::new(&FONT_10X20, Rgb565::WHITE),
)))
.arrange()
.align_to(&display.bounding_box(), horizontal::Center, vertical::Top)
.draw(&mut display)
.expect("Failed to draw title");
loop {
if dirty {
let style = PrimitiveStyle::with_fill(Rgb565::BLACK);
if let Some(area) = last_area {
Rectangle::new(area.0.top_left, area.0.size)
.into_styled(style)
.draw(&mut display)
.unwrap();
Rectangle::new(area.1.top_left, area.1.size)
.into_styled(style)
.draw(&mut display)
.unwrap();
}
let text = input.iter().cloned().collect::<String>();
let style = MonoTextStyle::new(&FONT_7X14, Rgb565::WHITE);
let expr_layout = LinearLayout::vertical(Chain::new(Text::new(
&text,
display.bounding_box().center(),
style,
)))
.arrange()
.align_to(&display.bounding_box(), horizontal::Left, vertical::Center);
let result = if let Ok(result) = evaluate(&input[input_min..]) {
&format!(" = {}", result)
} else {
" = Error"
};
let eq_layout = LinearLayout::vertical(Chain::new(Text::new(
result,
display.bounding_box().center(),
style,
)))
.arrange()
.align_to(&display.bounding_box(), horizontal::Right, vertical::Center);
last_area = Some((expr_layout.bounds(), eq_layout.bounds()));
expr_layout.draw(&mut display).unwrap();
eq_layout.draw(&mut display).unwrap();
dirty = false;
}
let event = get_key();
if event.state == KeyState::Released {
match event.key {
KeyCode::Char(ch) => {
input.push(ch);
}
KeyCode::Del => {
input.truncate(input_min);
}
KeyCode::Backspace => {
if input.len() > input_min {
input.pop();
}
}
KeyCode::Esc => return,
_ => (),
}
dirty = true;
}
}
}
fn get_int(int: &[char]) -> Result<i32, ()> {
let mut output: i32 = 0;
for &c in int {
let digit = c.to_digit(10).ok_or(())? as i32;
output = output
.checked_mul(10)
.and_then(|v| v.checked_add(digit))
.ok_or(())?;
}
Ok(output)
}
fn primary(input: &[char], pos: &mut usize) -> Result<i32, ()> {
let mut digits = Vec::new();
while *pos < input.len() && input[*pos].is_ascii_digit() {
digits.push(input[*pos]);
*pos += 1;
}
if digits.is_empty() {
return Err(());
}
get_int(&digits)
}
fn mul_div(input: &[char], pos: &mut usize) -> Result<i32, ()> {
let mut value = primary(input, pos)?;
while *pos < input.len() {
let op = input[*pos];
if op != '*' && op != '/' {
break;
}
*pos += 1;
let rhs = primary(input, pos)?;
value = match op {
'*' => value.checked_mul(rhs).ok_or(())?,
'/' => {
if rhs == 0 {
return Err(());
}
value.checked_div(rhs).ok_or(())?
}
_ => unreachable!(),
};
}
Ok(value)
}
fn add_sub(input: &[char], pos: &mut usize) -> Result<i32, ()> {
let mut value = mul_div(input, pos)?;
while *pos < input.len() {
let op = input[*pos];
if op != '+' && op != '-' {
break;
}
*pos += 1;
let rhs = mul_div(input, pos)?;
value = match op {
'+' => value.checked_add(rhs).ok_or(())?,
'-' => value.checked_sub(rhs).ok_or(())?,
_ => unreachable!(),
};
}
Ok(value)
}
fn evaluate(input: &[char]) -> Result<i32, ()> {
let mut pos = 0;
let result = add_sub(input, &mut pos)?;
if pos != input.len() {
return Err(());
}
Ok(result)
}

View File

@@ -0,0 +1,9 @@
[package]
name = "gallery"
version = "0.1.0"
edition = "2024"
[dependencies]
userlib = { path = "../../userlib" }
embedded-graphics = "0.8.1"
tinybmp = "0.6.0"

View File

@@ -0,0 +1,28 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("../memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg-bins=-Tmemory.x");
}

View File

@@ -0,0 +1,95 @@
#![no_std]
#![no_main]
#![allow(static_mut_refs)]
extern crate alloc;
use alloc::{format, vec};
use core::panic::PanicInfo;
use embedded_graphics::{
Drawable, image::Image, mono_font::MonoTextStyle, mono_font::ascii::FONT_6X10,
pixelcolor::Rgb565, prelude::*, text::Text,
};
use tinybmp::Bmp;
use userlib::{
display::{Display, SCREEN_HEIGHT, SCREEN_WIDTH},
fs::{Entries, list_dir, read_file},
get_key,
keyboard::{KeyCode, KeyState},
println,
};
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("user panic: {} @ {:?}", info.message(), info.location());
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()
}
pub fn main() {
println!("Starting Gallery app");
let mut bmp_buf = vec![0_u8; 100_000];
let mut display = Display::take().unwrap();
let grid_cols = 3;
let grid_rows = 3;
let cell_width = SCREEN_WIDTH as i32 / grid_cols;
let cell_height = SCREEN_HEIGHT as i32 / grid_rows;
let mut images_drawn = 0;
let mut entries = Entries::new();
let files_num = list_dir("/images", &mut entries);
for file in &entries.entries()[2..files_num] {
if images_drawn >= grid_cols * grid_rows {
break; // only draw 3x3
}
println!("file: {}", file);
if file.extension().unwrap_or("") == "bmp" || file.extension().unwrap_or("") == "BMP" {
let file_path = format!("/images/{}", file);
let read = read_file(&file_path, 0, &mut &mut bmp_buf[..]);
if read > 0 {
let bmp = Bmp::from_slice(&bmp_buf).expect("failed to parse bmp");
let row = images_drawn / grid_cols;
let col = images_drawn % grid_cols;
let cell_x = col * cell_width;
let cell_y = row * cell_height;
// Center image inside cell
let bmp_w = bmp.size().width as i32;
let bmp_h = bmp.size().height as i32;
let x = cell_x + (cell_width - bmp_w) / 2;
let y = cell_y + 5; // 5px top margin
Image::new(&bmp, Point::new(x, y))
.draw(&mut display)
.unwrap();
let text_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE);
let text_y = y + bmp_h + 2; // 2px gap under image
Text::new(&file.base(), Point::new(cell_x + 2, text_y), text_style)
.draw(&mut display)
.unwrap();
images_drawn += 1;
}
}
}
loop {
let event = get_key();
if event.state != KeyState::Idle {
match event.key {
KeyCode::Esc => return,
_ => (),
}
};
}
}

10
user_apps/gif/Cargo.toml Normal file
View File

@@ -0,0 +1,10 @@
[package]
name = "gif"
version = "0.1.0"
edition = "2024"
[dependencies]
userlib = { path = "../../userlib" }
selection_ui = { path = "../../selection_ui" }
embedded-graphics = "0.8.1"
tinygif = { git = "https://github.com/LegitCamper/tinygif" }

28
user_apps/gif/build.rs Normal file
View File

@@ -0,0 +1,28 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("../memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg-bins=-Tmemory.x");
}

103
user_apps/gif/src/main.rs Normal file
View File

@@ -0,0 +1,103 @@
#![no_std]
#![no_main]
extern crate alloc;
use alloc::{format, vec, vec::Vec};
use core::panic::PanicInfo;
use embedded_graphics::{
image::ImageDrawable,
mono_font::{ascii::FONT_6X10, MonoTextStyle},
pixelcolor::Rgb565,
prelude::{Point, RgbColor},
transform::Transform,
};
use selection_ui::{draw_text_center, SelectionUi, SelectionUiError};
use tinygif::Gif;
use userlib::{
display::{Display, SCREEN_HEIGHT, SCREEN_WIDTH},
fs::{file_len, list_dir, read_file, Entries},
get_key, get_ms,
keyboard::{KeyCode, KeyState},
println, sleep,
};
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()
}
pub fn main() {
println!("Starting Gif app");
let mut display = Display::take().unwrap();
let mut entries = Entries::new();
list_dir("/gifs", &mut entries);
let mut files = entries.entries();
files.retain(|e| e.extension().unwrap_or("") == "gif");
let mut gifs = files.iter().map(|e| e.full_name()).collect::<Vec<&str>>();
gifs.sort();
let mut selection_ui = SelectionUi::new(&mut gifs);
let selection = match selection_ui.run_selection_ui(&mut display) {
Ok(maybe_sel) => maybe_sel,
Err(e) => match e {
SelectionUiError::SelectionListEmpty => {
draw_text_center(
&mut display,
"No Gifs were found in /gifs",
MonoTextStyle::new(&FONT_6X10, Rgb565::RED),
)
.expect("Display Error");
None
}
SelectionUiError::DisplayError(_) => panic!("Display Error"),
},
};
assert!(selection.is_some());
let file_name = format!("/gifs/{}", gifs[selection.unwrap()]);
let size = file_len(&file_name);
let mut buf = vec![0_u8; size];
let read = read_file(&file_name, 0, &mut buf);
println!("read: {}, file size: {}", read, size);
assert!(read == size);
let gif = Gif::<Rgb565>::from_slice(&buf).expect("Failed to parse gif");
let translation = Point::new(
(SCREEN_WIDTH as i32 - gif.width() as i32) / 2,
(SCREEN_HEIGHT as i32 - gif.height() as i32) / 2,
);
let mut frame_num = 0;
loop {
for mut frame in gif.frames() {
let start = get_ms();
frame.translate_mut(translation).draw(&mut display).unwrap();
frame_num += 1;
if frame_num % 5 == 0 {
let event = get_key();
if event.state != KeyState::Idle {
match event.key {
KeyCode::Esc => {
drop(buf);
return;
}
_ => (),
};
};
}
sleep(((frame.delay_centis as u64) * 10).saturating_sub(start));
}
}
}

31
user_apps/memory.x Normal file
View File

@@ -0,0 +1,31 @@
MEMORY
{
RAM : ORIGIN = 0x0, LENGTH = 250K
}
SECTIONS
{
.text : ALIGN(4)
{
*(.text .text.*);
*(.rodata .rodata.*);
} > RAM
.data : ALIGN(4)
{
*(.data .data.*);
} > RAM
.bss : ALIGN(4)
{
*(.bss .bss.*);
*(COMMON);
} > RAM
.syscall_table (NOLOAD) : ALIGN(4)
{
__user_reloc_start = .;
KEEP(*(.user_reloc*));
__user_reloc_end = .;
} > RAM
}

View File

@@ -0,0 +1,10 @@
[package]
name = "snake"
version = "0.1.0"
edition = "2024"
[dependencies]
userlib = { path = "../../userlib" }
embedded-graphics = "0.8.1"
embedded-snake = { git = "https://github.com/LegitCamper/embedded-snake-rs" }
rand = { version = "0.9.0", default-features = false }

28
user_apps/snake/build.rs Normal file
View File

@@ -0,0 +1,28 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("../memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg-bins=-Tmemory.x");
}

View File

@@ -0,0 +1,65 @@
#![no_std]
#![no_main]
extern crate alloc;
use core::panic::PanicInfo;
use embedded_graphics::{pixelcolor::Rgb565, prelude::RgbColor};
use embedded_snake::{Direction, SnakeGame};
use userlib::{
Rng,
display::{Display, SCREEN_HEIGHT, SCREEN_WIDTH},
get_key,
keyboard::{KeyCode, KeyState},
println, sleep,
};
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()
}
const CELL_SIZE: usize = 8;
pub fn main() {
println!("Starting Snake app");
let mut display = Display::take().unwrap();
let mut game = SnakeGame::<100, Rgb565, Rng>::new(
SCREEN_WIDTH as u16,
SCREEN_HEIGHT as u16,
CELL_SIZE as u16,
CELL_SIZE as u16,
Rng,
Rgb565::BLACK,
Rgb565::GREEN,
Rgb565::RED,
50,
);
loop {
let event = get_key();
if event.state != KeyState::Idle {
let direction = match event.key {
KeyCode::Up => Direction::Up,
KeyCode::Down => Direction::Down,
KeyCode::Right => Direction::Right,
KeyCode::Left => Direction::Left,
KeyCode::Esc => return,
_ => Direction::None,
};
game.set_direction(direction);
};
// ensure all draws show up at once
game.pre_draw(&mut display);
game.draw(&mut display);
sleep(15);
}
}

View File

@@ -0,0 +1,11 @@
[package]
name = "wav_player"
version = "0.1.0"
edition = "2024"
[dependencies]
userlib = { path = "../../userlib" }
selection_ui = { path = "../../selection_ui" }
embedded-graphics = "0.8.1"
rand = { version = "0.9.0", default-features = false }
embedded-audio = { git = "https://github.com/LegitCamper/embedded-audio" }

View File

@@ -0,0 +1,28 @@
//! This build script copies the `memory.x` file from the crate root into
//! a directory where the linker can always find it at build time.
//! For many projects this is optional, as the linker always searches the
//! project root directory -- wherever `Cargo.toml` is. However, if you
//! are using a workspace or have a more complicated build setup, this
//! build script becomes required. Additionally, by requesting that
//! Cargo re-run the build script whenever `memory.x` is changed,
//! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings.
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
// Put `memory.x` in our output directory and ensure it's
// on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("memory.x"))
.unwrap()
.write_all(include_bytes!("../memory.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rustc-link-arg-bins=-Tmemory.x");
}

View File

@@ -0,0 +1,146 @@
#![no_std]
#![no_main]
extern crate alloc;
use alloc::{string::String, vec::Vec};
use core::panic::PanicInfo;
use embedded_audio::{wav::Wav, AudioFile, PlatformFile, PlatformFileError};
use embedded_graphics::{
mono_font::{ascii::FONT_6X10, MonoTextStyle},
pixelcolor::Rgb565,
prelude::RgbColor,
};
use selection_ui::{draw_text_center, SelectionUi, SelectionUiError};
use userlib::{
audio::{audio_buffer_ready, send_audio_buffer, AUDIO_BUFFER_LEN},
display::Display,
format,
fs::{file_len, list_dir, read_file, Entries},
get_key,
keyboard::{KeyCode, KeyState},
println,
};
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
println!("user panic: {} @ {:?}", info.message(), info.location(),);
loop {}
}
#[unsafe(no_mangle)]
pub extern "Rust" fn _start() {
main()
}
pub fn main() {
println!("Starting Wav player app");
let mut display = Display::take().unwrap();
loop {
let mut entries = Entries::new();
list_dir("/music", &mut entries);
let mut files = entries.entries();
files.retain(|e| e.extension().unwrap_or("") == "wav");
let mut wavs = files.iter().map(|e| e.full_name()).collect::<Vec<&str>>();
wavs.sort();
let mut selection_ui = SelectionUi::new(&mut wavs);
let selection = match selection_ui.run_selection_ui(&mut display) {
Ok(maybe_sel) => maybe_sel,
Err(e) => match e {
SelectionUiError::SelectionListEmpty => {
draw_text_center(
&mut display,
"No Wavs were found in /music",
MonoTextStyle::new(&FONT_6X10, Rgb565::RED),
)
.expect("Display Error");
None
}
SelectionUiError::DisplayError(_) => panic!("Display Error"),
},
};
assert!(selection.is_some());
draw_text_center(
&mut display,
&format!("Now playing {}", wavs[selection.unwrap()]),
MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE),
)
.expect("Display Error");
let file_name = format!("/music/{}", wavs[selection.unwrap()]);
let file = File::new(String::from(file_name));
let mut wav = Wav::new(file).unwrap();
println!("sample rate: {}", wav.sample_rate());
println!("channels: {:?}", wav.channels() as u8);
let mut buf = [0_u8; AUDIO_BUFFER_LEN];
loop {
if audio_buffer_ready() {
if wav.is_eof() {
break;
}
let _read = wav.read(&mut buf).unwrap();
send_audio_buffer(&buf);
}
let event = get_key();
if event.state == KeyState::Released {
match event.key {
KeyCode::Esc => return,
_ => (),
}
}
}
}
}
struct File {
current_pos: usize,
file: String,
}
impl File {
fn new(file: String) -> Self {
Self {
current_pos: 0,
file,
}
}
}
impl PlatformFile for File {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, PlatformFileError> {
let read = read_file(&self.file, self.current_pos, buf);
self.current_pos += read;
Ok(read)
}
fn seek_from_current(&mut self, offset: i64) -> Result<(), PlatformFileError> {
if offset.is_positive() {
self.current_pos += offset as usize;
} else {
self.current_pos -= offset as usize;
}
Ok(())
}
fn seek_from_start(&mut self, offset: usize) -> Result<(), PlatformFileError> {
self.current_pos = offset;
Ok(())
}
fn seek_from_end(&mut self, offset: usize) -> Result<(), PlatformFileError> {
self.current_pos = self.length() - offset;
Ok(())
}
fn length(&mut self) -> usize {
file_len(&self.file)
}
}