servicepoint-simulator/src/main.rs

220 lines
6.8 KiB
Rust
Raw Normal View History

2024-05-09 00:46:03 +02:00
#![deny(clippy::all)]
2024-05-10 18:24:26 +02:00
mod font;
2024-05-09 13:26:33 +02:00
mod gui;
2024-05-11 00:26:25 +02:00
use crate::font::BitmapFont;
2024-05-09 13:26:33 +02:00
use crate::gui::App;
use clap::Parser;
2024-05-11 00:26:25 +02:00
use log::{debug, error, info, warn};
use servicepoint2::{
Command, Origin, Packet, PixelGrid, Size, Window, PIXEL_HEIGHT, PIXEL_WIDTH, TILE_SIZE,
2024-05-11 00:46:49 +02:00
TILE_WIDTH,
2024-05-11 00:26:25 +02:00
};
use std::io::ErrorKind;
use std::net::UdpSocket;
use std::sync::{mpsc, RwLock, RwLockWriteGuard};
use std::time::Duration;
2024-05-09 13:26:33 +02:00
use winit::event_loop::{ControlFlow, EventLoop};
#[derive(Parser, Debug)]
struct Cli {
2024-05-10 12:36:42 +02:00
#[arg(long, default_value = "0.0.0.0:2342")]
bind: String,
}
2024-05-09 00:46:03 +02:00
fn main() {
env_logger::init();
let cli = Cli::parse();
info!("starting with args: {:?}", &cli);
2024-05-08 13:21:31 +02:00
2024-05-11 00:26:25 +02:00
let socket = UdpSocket::bind(cli.bind).expect("could not bind socket");
socket
.set_nonblocking(true)
.expect("could not enter non blocking mode");
let font = BitmapFont::load_file("Web437_IBM_BIOS.woff");
let display = PixelGrid::new(PIXEL_WIDTH as usize, PIXEL_HEIGHT as usize);
let display_locked = RwLock::new(display);
let display_locked_ref = &display_locked;
std::thread::scope(move |scope| {
let (stop_udp_tx, stop_udp_rx) = mpsc::channel();
let (stop_ui_tx, stop_ui_rx) = mpsc::channel();
let udp_thread = scope.spawn(move || {
let mut buf = [0; 8985];
while stop_udp_rx.try_recv().is_err() {
let (amount, _) = match socket.recv_from(&mut buf) {
Err(err) if err.kind() == ErrorKind::WouldBlock => {
2024-05-11 00:46:49 +02:00
std::thread::sleep(Duration::from_millis(1));
2024-05-11 00:26:25 +02:00
continue;
}
Ok(result) => result,
other => other.unwrap(),
};
if amount == buf.len() {
warn!(
"the received package may have been truncated to a length of {}",
amount
);
}
let vec = buf[..amount].to_vec();
let package = servicepoint2::Packet::from(vec);
let mut display = display_locked_ref.write().unwrap();
handle_package(package, &font, &mut display);
}
stop_ui_tx.send(()).expect("could not stop ui thread");
});
let mut app = App::new(display_locked_ref, stop_ui_rx);
let event_loop = EventLoop::new().expect("could not create event loop");
event_loop.set_control_flow(ControlFlow::Poll);
event_loop
.run_app(&mut app)
.expect("could not run event loop");
stop_udp_tx.send(()).expect("could not stop udp thread");
udp_thread.join().expect("could not join udp thread");
});
2024-05-11 00:46:49 +02:00
}
2024-05-11 00:26:25 +02:00
2024-05-11 00:46:49 +02:00
fn handle_package(received: Packet, font: &BitmapFont, display: &mut RwLockWriteGuard<PixelGrid>) {
let command = match Command::try_from(received) {
Err(err) => {
warn!("could not read command for packet: {:?}", err);
return;
}
Ok(val) => val,
};
2024-05-11 00:26:25 +02:00
2024-05-11 00:46:49 +02:00
match command {
Command::Clear => {
info!("clearing display");
display.fill(false);
}
Command::HardReset => {
warn!("display shutting down");
return;
}
Command::BitmapLinearWin(Origin(x, y), pixels) => {
print_pixel_grid(x as usize, y as usize, &pixels, display);
}
Command::Cp437Data(window, payload) => {
print_cp437_data(window, &payload, font, display);
}
#[allow(deprecated)]
Command::BitmapLegacy => {
warn!("ignoring deprecated command {:?}", command);
}
Command::BitmapLinear(offset, vec) => {
for bitmap_index in 0..vec.len() {
let pixel_index = offset as usize + bitmap_index;
let y = pixel_index / TILE_WIDTH as usize;
let x = pixel_index % TILE_SIZE as usize;
display.set(x, y, vec.get(bitmap_index));
2024-05-11 00:26:25 +02:00
}
2024-05-11 00:46:49 +02:00
}
Command::BitmapLinearAnd(offset, vec) => {
for bitmap_index in 0..vec.len() {
let pixel_index = offset as usize + bitmap_index;
let y = pixel_index / TILE_WIDTH as usize;
let x = pixel_index % TILE_SIZE as usize;
let old_value = display.get(x, y);
display.set(x, y, old_value && vec.get(bitmap_index));
2024-05-11 00:26:25 +02:00
}
2024-05-11 00:46:49 +02:00
}
Command::BitmapLinearOr(offset, vec) => {
for bitmap_index in 0..vec.len() {
let pixel_index = offset as usize + bitmap_index;
let y = pixel_index / TILE_WIDTH as usize;
let x = pixel_index % TILE_SIZE as usize;
let old_value = display.get(x, y);
display.set(x, y, old_value || vec.get(bitmap_index));
2024-05-11 00:26:25 +02:00
}
2024-05-11 00:46:49 +02:00
}
Command::BitmapLinearXor(offset, vec) => {
for bitmap_index in 0..vec.len() {
let pixel_index = offset as usize + bitmap_index;
let y = pixel_index / TILE_WIDTH as usize;
let x = pixel_index % TILE_SIZE as usize;
let old_value = display.get(x, y);
display.set(x, y, old_value ^ vec.get(bitmap_index));
2024-05-11 00:26:25 +02:00
}
}
2024-05-11 00:46:49 +02:00
_ => {
error!("command not implemented: {command:?}")
}
};
}
2024-05-11 00:26:25 +02:00
2024-05-11 00:46:49 +02:00
fn check_payload_size(buf: &[u8], expected: usize) -> bool {
let actual = buf.len();
if actual == expected {
return true;
2024-05-11 00:26:25 +02:00
}
2024-05-11 00:46:49 +02:00
error!(
"expected a payload length of {} but got {}",
expected, actual
);
return false;
}
2024-05-11 00:26:25 +02:00
2024-05-11 00:46:49 +02:00
fn print_cp437_data(
window: Window,
payload: &[u8],
font: &BitmapFont,
display: &mut RwLockWriteGuard<PixelGrid>,
) {
let Window(Origin(x, y), Size(w, h)) = window;
if !check_payload_size(payload, (w * h) as usize) {
return;
}
2024-05-11 00:46:49 +02:00
for char_y in 0usize..h as usize {
for char_x in 0usize..w as usize {
let char_code = payload[char_y * w as usize + char_x];
2024-05-08 20:17:39 +02:00
2024-05-11 00:46:49 +02:00
let tile_x = char_x + x as usize;
let tile_y = char_y + y as usize;
let bitmap = font.get_bitmap(char_code);
print_pixel_grid(
tile_x * TILE_SIZE as usize,
tile_y * TILE_SIZE as usize,
bitmap,
display,
);
2024-05-11 00:26:25 +02:00
}
}
2024-05-11 00:46:49 +02:00
}
2024-05-11 00:46:49 +02:00
fn print_pixel_grid(
offset_x: usize,
offset_y: usize,
pixels: &PixelGrid,
display: &mut RwLockWriteGuard<PixelGrid>,
) {
debug!(
"printing {}x{} grid at {offset_x} {offset_y}",
pixels.width, pixels.height
);
for inner_y in 0..pixels.height {
for inner_x in 0..pixels.width {
let is_set = pixels.get(inner_x, inner_y);
display.set(offset_x + inner_x, offset_y + inner_y, is_set);
2024-05-11 00:26:25 +02:00
}
}
2024-05-09 00:46:03 +02:00
}