fix frontend board mapping, game-over handling, and WASM-4
- fix Xform yx and add a shared view-aware board_position helper - add GTK automatic game-over, restart, status, and multi-capture selection - pin librsvg to the 2.60 series to match GTK4/glib 0.20 - add WASM orientation, undo/redo/new/flip controls, and winner overlay - replace WASM crown images with canvas paths and remove dead files/deps - make WASM-4 playable with mouse input, crowns, winner handling, and correct mapping - move the WASM-4 release profile to the workspace root - clean up frontend dead code and update the README
This commit is contained in:
1 parent
dd3b918561
commit
f17d328ed5
20 files changed
+997
-1290
No files matched your search
@@ -10,13 +10,7 @@ version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
wasm4.workspace = true
|
||||
wasm4-sys.workspace = true
|
||||
rdraught.workspace = true
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
|
||||
+193
-186
@@ -1,26 +1,196 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use rdraught::{DraughtsBoard, DraughtsGame, Piece, Position};
|
||||
use core::arch::wasm32::unreachable;
|
||||
use rdraught::{DraughtsGame, Piece, Player, Position};
|
||||
use wasm4::draw::{Color, DrawIndex, DrawIndices, Framebuffer};
|
||||
use wasm4::rt::{Resources, Runtime};
|
||||
// use wasm4 as w4;
|
||||
use core::arch::wasm32::unreachable;
|
||||
use wasm4::sys::DRAW_COLORS;
|
||||
use wasm4::sys::{DRAW_COLORS, MOUSE_BUTTONS, MOUSE_LEFT, MOUSE_X, MOUSE_Y};
|
||||
use wasm4::trace;
|
||||
// mod wasm4;
|
||||
|
||||
// use wasm4::{DRAW_COLORS, oval, rect};
|
||||
const SQUARE_SIZE: i32 = 20;
|
||||
const BOARD_SIZE: i32 = 160;
|
||||
|
||||
// mod palette;
|
||||
|
||||
// static mut GAME: DraughtsGame = DraughtsGame::default();
|
||||
struct Rdraught_W4 {
|
||||
struct RdraughtW4 {
|
||||
framebuffer: Framebuffer,
|
||||
game: DraughtsGame,
|
||||
selected: Option<Position>,
|
||||
mouse_down: bool,
|
||||
}
|
||||
|
||||
impl Runtime for Rdraught_W4 {
|
||||
impl RdraughtW4 {
|
||||
fn set_colors(indices: [DrawIndex; 4]) {
|
||||
unsafe { *DRAW_COLORS = DrawIndices::from_array(indices).into_u16() }
|
||||
}
|
||||
|
||||
fn draw_piece(&self, piece: Piece, top_left: [i32; 2]) {
|
||||
let color = match piece.player() {
|
||||
Some(Player::White) => DrawIndex::First,
|
||||
Some(Player::Red) => DrawIndex::Second,
|
||||
None => return,
|
||||
};
|
||||
Self::set_colors([
|
||||
color,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
]);
|
||||
self.framebuffer.oval(
|
||||
[top_left[0] + 2, top_left[1] + 2],
|
||||
[(SQUARE_SIZE - 4) as u32, (SQUARE_SIZE - 4) as u32],
|
||||
);
|
||||
|
||||
if piece.is_crowned() {
|
||||
Self::set_colors([
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
]);
|
||||
self.framebuffer
|
||||
.rect([top_left[0] + 7, top_left[1] + 7], [6, 6]);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_selection(&self) {
|
||||
let Some(selected) = self.selected else {
|
||||
return;
|
||||
};
|
||||
let x = selected.col() as i32 * SQUARE_SIZE;
|
||||
let y = selected.row() as i32 * SQUARE_SIZE;
|
||||
|
||||
Self::set_colors([
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
]);
|
||||
self.framebuffer.rect([x, y], [SQUARE_SIZE as u32, 2]);
|
||||
self.framebuffer
|
||||
.rect([x, y + SQUARE_SIZE - 2], [SQUARE_SIZE as u32, 2]);
|
||||
self.framebuffer.rect([x, y], [2, SQUARE_SIZE as u32]);
|
||||
self.framebuffer
|
||||
.rect([x + SQUARE_SIZE - 2, y], [2, SQUARE_SIZE as u32]);
|
||||
|
||||
for mv in self.game.moves_for_piece(selected) {
|
||||
if let Some(end) = mv.get_end_position() {
|
||||
self.framebuffer.rect(
|
||||
[
|
||||
end.col() as i32 * SQUARE_SIZE + 8,
|
||||
end.row() as i32 * SQUARE_SIZE + 8,
|
||||
],
|
||||
[4, 4],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_board(&self) {
|
||||
for row in 0..8i32 {
|
||||
for col in 0..8i32 {
|
||||
let indices = if (row + col) % 2 == 0 {
|
||||
[
|
||||
DrawIndex::Third,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
]
|
||||
} else {
|
||||
[
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
]
|
||||
};
|
||||
Self::set_colors(indices);
|
||||
let top_left = [col * SQUARE_SIZE, row * SQUARE_SIZE];
|
||||
self.framebuffer
|
||||
.rect(top_left, [SQUARE_SIZE as u32, SQUARE_SIZE as u32]);
|
||||
|
||||
if let Ok(position) = Position::new(row as u8, col as u8) {
|
||||
let piece = self.game.piece_at(position);
|
||||
if piece.is_present() {
|
||||
self.draw_piece(piece, top_left);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.draw_selection();
|
||||
}
|
||||
|
||||
fn draw_winner(&self, winner: Player) {
|
||||
Self::set_colors([
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
]);
|
||||
self.framebuffer.rect([0, 70], [BOARD_SIZE as u32, 28]);
|
||||
|
||||
Self::set_colors([
|
||||
DrawIndex::First,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
]);
|
||||
let (message, x) = match winner {
|
||||
Player::Red => ("Red wins", 48),
|
||||
Player::White => ("White wins", 40),
|
||||
};
|
||||
self.framebuffer.text(message, [x, 74]);
|
||||
self.framebuffer.text("click to restart", [16, 88]);
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.game = DraughtsGame::default();
|
||||
self.selected = None;
|
||||
}
|
||||
|
||||
fn handle_click(&mut self, x: i32, y: i32) {
|
||||
if self.game.winner().is_some() {
|
||||
self.reset();
|
||||
return;
|
||||
}
|
||||
if x < 0 || y < 0 || x >= BOARD_SIZE || y >= BOARD_SIZE {
|
||||
self.selected = None;
|
||||
return;
|
||||
}
|
||||
|
||||
let row = (y / SQUARE_SIZE) as u8;
|
||||
let col = (x / SQUARE_SIZE) as u8;
|
||||
let Ok(position) = Position::new(row, col) else {
|
||||
self.selected = None;
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(selected) = self.selected {
|
||||
let found = self
|
||||
.game
|
||||
.moves_for_piece(selected)
|
||||
.find(|mv| mv.get_end_position() == Some(position));
|
||||
if let Some(mv) = found {
|
||||
if self.game.check_and_apply_move(&mv).is_ok() {
|
||||
self.selected = self.game.pending_capture();
|
||||
} else {
|
||||
self.selected = None;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if self.game.pending_capture().is_some() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if self.game.moves_for_piece(position).next().is_some() {
|
||||
self.selected = Some(position);
|
||||
} else {
|
||||
self.selected = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Runtime for RdraughtW4 {
|
||||
fn start(rs: Resources) -> Self {
|
||||
rs.framebuffer.replace_palette([
|
||||
Color(0x00edeada),
|
||||
@@ -28,196 +198,33 @@ impl Runtime for Rdraught_W4 {
|
||||
Color(0x005d8d60),
|
||||
Color(0x00100f24),
|
||||
]);
|
||||
Rdraught_W4 {
|
||||
RdraughtW4 {
|
||||
framebuffer: rs.framebuffer,
|
||||
game: DraughtsGame::default(),
|
||||
selected: None,
|
||||
mouse_down: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self) {
|
||||
let indices = DrawIndices::from_array([
|
||||
DrawIndex::Third,
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
]);
|
||||
unsafe {
|
||||
*DRAW_COLORS = indices.into_u16();
|
||||
let (mouse_x, mouse_y, buttons) = unsafe { (*MOUSE_X, *MOUSE_Y, *MOUSE_BUTTONS) };
|
||||
let pressed = buttons & MOUSE_LEFT != 0;
|
||||
if pressed && !self.mouse_down {
|
||||
self.handle_click(mouse_x as i32, mouse_y as i32);
|
||||
}
|
||||
self.mouse_down = pressed;
|
||||
|
||||
self.framebuffer.rect([10, 10], [50, 50]);
|
||||
self.framebuffer.oval([50, 50], [10, 10]);
|
||||
let SQUARE_SIZE = 20u32;
|
||||
for i in 0..8i32 {
|
||||
for j in 0..8i32 {
|
||||
let indices = if (i + j) % 2 == 0 {
|
||||
DrawIndices::from_array([
|
||||
DrawIndex::Third,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
])
|
||||
} else {
|
||||
DrawIndices::from_array([
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Fourth,
|
||||
DrawIndex::Transparent,
|
||||
])
|
||||
};
|
||||
unsafe {
|
||||
*DRAW_COLORS = indices.into_u16();
|
||||
}
|
||||
let tl = [i as i32 * SQUARE_SIZE as i32, j as i32 * SQUARE_SIZE as i32];
|
||||
self.framebuffer.rect(tl, [SQUARE_SIZE, SQUARE_SIZE]);
|
||||
|
||||
let score = self.game.score_for_player(rdraught::Player::Red);
|
||||
// let msg = format!("{}", score);
|
||||
// w4::trace(msg.as_str());
|
||||
|
||||
let piece = self.game.piece_at(Position::new(i as u8, j as u8).unwrap());
|
||||
match piece {
|
||||
Piece::SimpleWhitePawn => {
|
||||
let indices = DrawIndices::from_array([
|
||||
DrawIndex::First,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
]);
|
||||
unsafe {
|
||||
*DRAW_COLORS = indices.into_u16();
|
||||
}
|
||||
self.framebuffer.oval(tl, [SQUARE_SIZE, SQUARE_SIZE]);
|
||||
}
|
||||
Piece::SimpleRedPawn => {
|
||||
let indices = DrawIndices::from_array([
|
||||
DrawIndex::Second,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
DrawIndex::Transparent,
|
||||
]);
|
||||
unsafe {
|
||||
*DRAW_COLORS = indices.into_u16();
|
||||
}
|
||||
self.framebuffer.oval(tl, [SQUARE_SIZE, SQUARE_SIZE]);
|
||||
}
|
||||
Piece::CrownedRedPawn => {}
|
||||
Piece::CrownedWhitePawn => {}
|
||||
Piece::NoPiece => {}
|
||||
}
|
||||
}
|
||||
self.draw_board();
|
||||
if let Some(winner) = self.game.winner() {
|
||||
self.draw_winner(winner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wasm4::main! { Rdraught_W4 }
|
||||
|
||||
// use wasm4::*;
|
||||
// use wasm4_sys;
|
||||
|
||||
// #[unsafe(no_mangle)]
|
||||
// fn update() {
|
||||
// unsafe {
|
||||
// wasm4_sys::rect(10, 10, 32, 32);
|
||||
// }
|
||||
// }
|
||||
// #[unsafe(no_mangle)]
|
||||
// fn start() {
|
||||
// palette::change_palette(0usize);
|
||||
// palette::set_draw_color(2u16);
|
||||
// }
|
||||
|
||||
// #[unsafe(no_mangle)]
|
||||
// fn update() {
|
||||
// // if self.count % 60 == 0 {
|
||||
// // w4::trace("tick");
|
||||
// // self.count = 0;
|
||||
// // }
|
||||
// // self.count += 1;
|
||||
// // let indices = DrawIndices::from_array([
|
||||
// // DrawIndex::Third,
|
||||
// // DrawIndex::Fourth,
|
||||
// // DrawIndex::Fourth,
|
||||
// // DrawIndex::Transparent,
|
||||
// // ]);
|
||||
// // unsafe {
|
||||
// // *DRAW_COLORS = 0x;
|
||||
// // }
|
||||
// palette::set_draw_color([3, 4, 4, 0]);
|
||||
|
||||
// rect(10, 10, 50, 50);
|
||||
// oval(50, 50, 10, 10);
|
||||
// let SQUARE_SIZE = 20u32;
|
||||
// for i in 0..8i32 {
|
||||
// for j in 0..8i32 {
|
||||
// // let indices = if (i + j) % 2 == 0 {
|
||||
// // DrawIndices::from_array([
|
||||
// // DrawIndex::Third,
|
||||
// // DrawIndex::Transparent,
|
||||
// // DrawIndex::Fourth,
|
||||
// // DrawIndex::Transparent,
|
||||
// // ])
|
||||
// // } else {
|
||||
// // DrawIndices::from_array([
|
||||
// // DrawIndex::Fourth,
|
||||
// // DrawIndex::Transparent,
|
||||
// // DrawIndex::Fourth,
|
||||
// // DrawIndex::Transparent,
|
||||
// // ])
|
||||
// // };
|
||||
// // unsafe {
|
||||
// // *DRAW_COLORS = indices.into_u16();
|
||||
// // }
|
||||
// let tl = [i as i32 * SQUARE_SIZE as i32, j as i32 * SQUARE_SIZE as i32];
|
||||
// rect(tl.0, tl.1, SQUARE_SIZE, SQUARE_SIZE);
|
||||
|
||||
// // let score = self.game.score_for_player(rdraught::Player::Red);
|
||||
// // let msg = format!("{}", score);
|
||||
// // w4::trace(msg.as_str());
|
||||
// unsafe {
|
||||
// let piece = GAME.piece_at(Position::new(i as u8, j as u8).unwrap());
|
||||
// match piece {
|
||||
// Piece::SimpleWhitePawn => {
|
||||
// // let indices = DrawIndices::from_array([
|
||||
// // DrawIndex::First,
|
||||
// // DrawIndex::Transparent,
|
||||
// // DrawIndex::Transparent,
|
||||
// // DrawIndex::Transparent,
|
||||
// // ]);
|
||||
// // unsafe {
|
||||
// // *DRAW_COLORS = indices.into_u16();
|
||||
// // }
|
||||
// oval(tl, [SQUARE_SIZE, SQUARE_SIZE]);
|
||||
// }
|
||||
// Piece::SimpleRedPawn => {
|
||||
// // let indices = DrawIndices::from_array([
|
||||
// // DrawIndex::Second,
|
||||
// // DrawIndex::Transparent,
|
||||
// // DrawIndex::Transparent,
|
||||
// // DrawIndex::Transparent,
|
||||
// // ]);
|
||||
// // unsafe {
|
||||
// // *DRAW_COLORS = indices.into_u16();
|
||||
// // }
|
||||
// oval(tl, [SQUARE_SIZE, SQUARE_SIZE]);
|
||||
// }
|
||||
// Piece::CrownedRedPawn => {}
|
||||
// Piece::CrownedWhitePawn => {}
|
||||
// Piece::NoPiece => {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
wasm4::main! { RdraughtW4 }
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(_panic_info: &core::panic::PanicInfo<'_>) -> ! {
|
||||
trace("panic error");
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(cause) = _panic_info.payload().downcast_ref::<&str>() {
|
||||
trace(cause);
|
||||
}
|
||||
|
||||
unreachable()
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use super::wasm4::DRAW_COLORS;
|
||||
use super::wasm4::PALETTE;
|
||||
|
||||
type Palette = [u32; 4];
|
||||
|
||||
pub const W4_DEFAULT: Palette = [0xe0f8cf, 0x86c06c, 0x306850, 0x071821];
|
||||
|
||||
pub const PONG_OG: Palette = [0x1b1b1b, 0xdcdcdc, 0x1b1b1b, 0xdcdcdc];
|
||||
|
||||
pub const ICE_CREAM_GB: Palette = [0xfff6d3, 0xf9a875, 0xeb6b6f, 0x7c3f58];
|
||||
|
||||
pub const HOLLOW: Palette = [0x0f0f1b, 0x565a75, 0xc5b7be, 0xf9fbf5];
|
||||
|
||||
const PALETTES: [Palette; 4] = [PONG_OG, ICE_CREAM_GB, HOLLOW, W4_DEFAULT];
|
||||
|
||||
#[inline]
|
||||
fn palette_by_idx<T: Into<usize>>(idx: T) -> Palette {
|
||||
PALETTES[idx.into() % PALETTES.len()]
|
||||
}
|
||||
|
||||
pub fn change_palette<T: Into<usize>>(idx: T) {
|
||||
unsafe {
|
||||
*PALETTE = palette_by_idx(idx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_draw_color<T: Into<u16>>(idx: T) {
|
||||
unsafe { *DRAW_COLORS = idx.into() }
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
//
|
||||
// WASM-4: https://wasm4.org/docs
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Platform Constants │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pub const SCREEN_SIZE: u32 = 160;
|
||||
pub const FONT_SIZE: u32 = 8;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Memory Addresses │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
pub static mut PALETTE: *mut [u32; 4] = 0x04 as *mut [u32; 4];
|
||||
pub const DRAW_COLORS: *mut u16 = 0x14 as *mut u16;
|
||||
pub const GAMEPAD1: *const u8 = 0x16 as *const u8;
|
||||
pub const GAMEPAD2: *const u8 = 0x17 as *const u8;
|
||||
pub const GAMEPAD3: *const u8 = 0x18 as *const u8;
|
||||
pub const GAMEPAD4: *const u8 = 0x19 as *const u8;
|
||||
pub const MOUSE_X: *const i16 = 0x1a as *const i16;
|
||||
pub const MOUSE_Y: *const i16 = 0x1c as *const i16;
|
||||
pub const MOUSE_BUTTONS: *const u8 = 0x1e as *const u8;
|
||||
pub static mut FRAMEBUFFER: *mut [u8; 6400] = 0xa0 as *mut [u8; 6400];
|
||||
|
||||
pub const BUTTON_1: u8 = 1;
|
||||
pub const BUTTON_2: u8 = 2;
|
||||
pub const BUTTON_LEFT: u8 = 16;
|
||||
pub const BUTTON_RIGHT: u8 = 32;
|
||||
pub const BUTTON_UP: u8 = 64;
|
||||
pub const BUTTON_DOWN: u8 = 128;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Drawing Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Copies pixels to the framebuffer.
|
||||
pub fn blit(sprite: &[u8], x: i32, y: i32, width: u32, height: u32, flags: u32) {
|
||||
unsafe { extern_blit(sprite.as_ptr(), x, y, width, height, flags) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "blit"]
|
||||
fn extern_blit(sprite: *const u8, x: i32, y: i32, width: u32, height: u32, flags: u32);
|
||||
}
|
||||
|
||||
/// Copies a subregion within a larger sprite atlas to the framebuffer.
|
||||
pub fn blit_sub(
|
||||
sprite: &[u8],
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
src_x: u32,
|
||||
src_y: u32,
|
||||
stride: u32,
|
||||
flags: u32,
|
||||
) {
|
||||
unsafe {
|
||||
extern_blit_sub(
|
||||
sprite.as_ptr(),
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
src_x,
|
||||
src_y,
|
||||
stride,
|
||||
flags,
|
||||
)
|
||||
}
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "blitSub"]
|
||||
fn extern_blit_sub(
|
||||
sprite: *const u8,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
src_x: u32,
|
||||
src_y: u32,
|
||||
stride: u32,
|
||||
flags: u32,
|
||||
);
|
||||
}
|
||||
|
||||
pub const BLIT_2BPP: u32 = 1;
|
||||
pub const BLIT_1BPP: u32 = 0;
|
||||
pub const BLIT_FLIP_X: u32 = 2;
|
||||
pub const BLIT_FLIP_Y: u32 = 4;
|
||||
pub const BLIT_ROTATE: u32 = 8;
|
||||
|
||||
/// Draws a line between two points.
|
||||
pub fn line(x1: i32, y1: i32, x2: i32, y2: i32) {
|
||||
unsafe { extern_line(x1, y1, x2, y2) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "line"]
|
||||
fn extern_line(x1: i32, y1: i32, x2: i32, y2: i32);
|
||||
}
|
||||
|
||||
/// Draws an oval (or circle).
|
||||
pub fn oval(x: i32, y: i32, width: u32, height: u32) {
|
||||
unsafe { extern_oval(x, y, width, height) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "oval"]
|
||||
fn extern_oval(x: i32, y: i32, width: u32, height: u32);
|
||||
}
|
||||
|
||||
/// Draws a rectangle.
|
||||
pub fn rect(x: i32, y: i32, width: u32, height: u32) {
|
||||
unsafe { extern_rect(x, y, width, height) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "rect"]
|
||||
fn extern_rect(x: i32, y: i32, width: u32, height: u32);
|
||||
}
|
||||
|
||||
/// Draws text using the built-in system font.
|
||||
pub fn text(text: &str, x: i32, y: i32) {
|
||||
unsafe { extern_text(text.as_ptr(), text.len(), x, y) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "textUtf8"]
|
||||
fn extern_text(text: *const u8, length: usize, x: i32, y: i32);
|
||||
}
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Sound Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
/// Plays a sound tone.
|
||||
pub fn tone(frequency: u32, duration: u32, volume: u32, flags: u32) {
|
||||
unsafe { extern_tone(frequency, duration, volume, flags) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "tone"]
|
||||
fn extern_tone(frequency: u32, duration: u32, volume: u32, flags: u32);
|
||||
}
|
||||
|
||||
pub const TONE_PULSE1: u32 = 0;
|
||||
pub const TONE_PULSE2: u32 = 1;
|
||||
pub const TONE_TRIANGLE: u32 = 2;
|
||||
pub const TONE_NOISE: u32 = 3;
|
||||
pub const TONE_MODE1: u32 = 0;
|
||||
pub const TONE_MODE2: u32 = 4;
|
||||
pub const TONE_MODE3: u32 = 8;
|
||||
pub const TONE_MODE4: u32 = 12;
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Storage Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
extern "C" {
|
||||
/// Reads up to `size` bytes from persistent storage into the pointer `dest`.
|
||||
pub fn diskr(dest: *mut u8, size: u32) -> u32;
|
||||
|
||||
/// Writes up to `size` bytes from the pointer `src` into persistent storage.
|
||||
pub fn diskw(src: *const u8, size: u32) -> u32;
|
||||
}
|
||||
|
||||
// ┌───────────────────────────────────────────────────────────────────────────┐
|
||||
// │ │
|
||||
// │ Other Functions │
|
||||
// │ │
|
||||
// └───────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
extern "C" {
|
||||
/// Copies `size` bytes from `srcPtr` into `destPtr`.
|
||||
#[link_name = "memcpy"]
|
||||
pub fn memcpy(dest: *mut u8, src: *const u8, size: usize) -> usize;
|
||||
|
||||
/// Fills memory at `destPtr` with `size` bytes of the fixed value `value`.
|
||||
#[link_name = "memset"]
|
||||
pub fn memset(dest: *mut u8, byte: u8, size: usize) -> usize;
|
||||
}
|
||||
|
||||
/// Prints a message to the debug console.
|
||||
pub fn trace(text: &str) {
|
||||
unsafe { extern_trace(text.as_ptr(), text.len()) }
|
||||
}
|
||||
extern "C" {
|
||||
#[link_name = "traceUtf8"]
|
||||
fn extern_trace(trace: *const u8, length: usize);
|
||||
}
|
||||
Reference in new issue
Block a user