mirror of
https://github.com/kaesaecracker/servicepoint-simulator.git
synced 2025-01-18 02:20:14 +01:00
text rendering
This commit is contained in:
parent
05c7b4cb71
commit
ae12c82c2e
211
Cargo.lock
generated
211
Cargo.lock
generated
|
@ -534,6 +534,18 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-text"
|
||||
version = "20.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5"
|
||||
dependencies = [
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.0"
|
||||
|
@ -574,6 +586,16 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "cstr"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cursor-icon"
|
||||
version = "1.1.0"
|
||||
|
@ -591,6 +613,27 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch"
|
||||
version = "0.2.0"
|
||||
|
@ -618,6 +661,18 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53"
|
||||
|
||||
[[package]]
|
||||
name = "dwrote"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"winapi",
|
||||
"wio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.11.0"
|
||||
|
@ -698,6 +753,12 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-ord"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d"
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
|
@ -707,6 +768,31 @@ dependencies = [
|
|||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "font-kit"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d50ba02d3a19ab9012a00314ff4d105128cdc7ba223d69d48181f2d257244d51"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"byteorder",
|
||||
"core-foundation",
|
||||
"core-graphics",
|
||||
"core-text",
|
||||
"dirs-next",
|
||||
"dwrote",
|
||||
"float-ord",
|
||||
"freetype",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"pathfinder_geometry",
|
||||
"pathfinder_simd",
|
||||
"walkdir",
|
||||
"winapi",
|
||||
"yeslogic-fontconfig-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
|
@ -749,6 +835,27 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||
|
||||
[[package]]
|
||||
name = "freetype"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a440748e063798e4893ceb877151e84acef9bea9a8c6800645cf3f1b3a7806e"
|
||||
dependencies = [
|
||||
"freetype-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "freetype-sys"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "0.4.3"
|
||||
|
@ -1057,6 +1164,12 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
|
@ -1111,6 +1224,16 @@ dependencies = [
|
|||
"redox_syscall 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
|
@ -1480,7 +1603,7 @@ version = "0.3.47"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166"
|
||||
dependencies = [
|
||||
"libredox",
|
||||
"libredox 0.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1521,6 +1644,25 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_geometry"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pathfinder_simd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathfinder_simd"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebf45976c56919841273f2a0fc684c28437e2f304e264557d9c72be5d5a718be"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
|
@ -1559,14 +1701,18 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"env_logger",
|
||||
"font-kit",
|
||||
"image",
|
||||
"log",
|
||||
"num",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pathfinder_geometry",
|
||||
"pixels",
|
||||
"raw-window-handle 0.6.1",
|
||||
"servicepoint2",
|
||||
"winit",
|
||||
"yeslogic-fontconfig-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1631,12 +1777,11 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "1.3.1"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
|
||||
checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"toml_edit 0.19.15",
|
||||
"toml_edit 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1836,6 +1981,17 @@ dependencies = [
|
|||
"bitflags 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libredox 0.1.3",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.4"
|
||||
|
@ -1892,6 +2048,15 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
|
@ -1948,6 +2113,12 @@ dependencies = [
|
|||
"tiny-skia",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.201"
|
||||
|
@ -1977,6 +2148,11 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servicepoint2"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/kaesaecracker/servicepoint.git#a23ca55f607b64324eda02f27ae347998d227deb"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
|
@ -2205,9 +2381,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.19.15"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421"
|
||||
checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1"
|
||||
dependencies = [
|
||||
"indexmap 2.2.6",
|
||||
"toml_datetime",
|
||||
|
@ -2949,6 +3125,15 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wio"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.21.0"
|
||||
|
@ -3006,6 +3191,18 @@ version = "0.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621"
|
||||
|
||||
[[package]]
|
||||
name = "yeslogic-fontconfig-sys"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb6b23999a8b1a997bf47c7bb4d19ad4029c3327bb3386ebe0a5ff584b33c7a"
|
||||
dependencies = [
|
||||
"cstr",
|
||||
"dlib",
|
||||
"once_cell",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.34"
|
||||
|
|
|
@ -15,3 +15,7 @@ env_logger = "0.11"
|
|||
num = "0.4"
|
||||
num-derive = "0.4"
|
||||
num-traits = "0.2"
|
||||
font-kit = { version = "0.13.0", features = ["loader-freetype-default", "loader-freetype"], default-features = false }
|
||||
pathfinder_geometry = "0.5.1"
|
||||
yeslogic-fontconfig-sys = { version = "5.0", features = ["dlopen"] }
|
||||
servicepoint2 = { git = "https://github.com/kaesaecracker/servicepoint.git" }
|
||||
|
|
|
@ -8,3 +8,9 @@ Use cases:
|
|||
- getting error messages for invalid packages
|
||||
- test your project when outside of CCCB
|
||||
- test your project while other people are using the display
|
||||
|
||||
## Legal stuff
|
||||
|
||||
The included font is https://int10h.org/oldschool-pc-fonts/fontlist/font?ibm_bios (included in the download from https://int10h.org/oldschool-pc-fonts/download/). The font is CC BY-SA 4.0.
|
||||
|
||||
For everything else see the LICENSE file.
|
||||
|
|
BIN
Web437_IBM_BIOS.woff
Normal file
BIN
Web437_IBM_BIOS.woff
Normal file
Binary file not shown.
56
src/font.rs
Normal file
56
src/font.rs
Normal file
|
@ -0,0 +1,56 @@
|
|||
use font_kit::canvas::*;
|
||||
use font_kit::hinting::HintingOptions;
|
||||
use pathfinder_geometry::transform2d::Transform2F;
|
||||
use pathfinder_geometry::vector::{vec2f, vec2i};
|
||||
use servicepoint2::{PixelGrid, TILE_SIZE};
|
||||
|
||||
pub struct BitmapFont {
|
||||
bitmaps: [PixelGrid; u8::MAX as usize],
|
||||
}
|
||||
|
||||
impl BitmapFont {
|
||||
pub fn load_file(file: &str) -> BitmapFont {
|
||||
let font = font_kit::font::Font::from_path(file, 0).expect("could not load font");
|
||||
|
||||
let mut bitmaps =
|
||||
core::array::from_fn(|_| PixelGrid::new(TILE_SIZE as usize, TILE_SIZE as usize));
|
||||
|
||||
for char_code in u8::MIN..u8::MAX {
|
||||
let char = char_code as char;
|
||||
let glyph_id = match font.glyph_for_char(char) {
|
||||
None => continue,
|
||||
Some(val) => val,
|
||||
};
|
||||
|
||||
let size = 8f32;
|
||||
let transform = Transform2F::default();
|
||||
let mut canvas = Canvas::new(vec2i(size as i32, size as i32), Format::A8);
|
||||
font.rasterize_glyph(
|
||||
&mut canvas,
|
||||
glyph_id,
|
||||
size,
|
||||
Transform2F::from_translation(vec2f(0f32, size)) * transform,
|
||||
HintingOptions::None,
|
||||
RasterizationOptions::GrayscaleAa,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(canvas.pixels.len(), 64);
|
||||
assert_eq!(canvas.stride, 8);
|
||||
|
||||
for y in 0..TILE_SIZE as usize {
|
||||
for x in 0..TILE_SIZE as usize {
|
||||
let index = x + y * TILE_SIZE as usize;
|
||||
let canvas_val = canvas.pixels[index] != 0;
|
||||
bitmaps[char_code as usize].set(x, y, canvas_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BitmapFont { bitmaps };
|
||||
}
|
||||
|
||||
pub fn get_bitmap(&self, char_code: u8) -> &PixelGrid {
|
||||
&self.bitmaps[char_code as usize]
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use crate::protocol::{PIXEL_HEIGHT, PIXEL_WIDTH};
|
||||
use crate::DISPLAY;
|
||||
use log::{trace, warn};
|
||||
use pixels::wgpu::TextureFormat;
|
||||
use pixels::{Pixels, PixelsBuilder, SurfaceTexture};
|
||||
use servicepoint2::{PIXEL_HEIGHT, PIXEL_WIDTH};
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalSize, Size};
|
||||
use winit::event::WindowEvent;
|
||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -1,17 +1,17 @@
|
|||
#![deny(clippy::all)]
|
||||
|
||||
mod font;
|
||||
mod gui;
|
||||
mod upd_loop;
|
||||
mod protocol;
|
||||
mod upd_loop;
|
||||
|
||||
use std::default::Default;
|
||||
use std::sync::mpsc;
|
||||
|
||||
use crate::gui::App;
|
||||
use crate::upd_loop::start_udp_thread;
|
||||
use crate::upd_loop::UdpThread;
|
||||
use clap::Parser;
|
||||
use log::info;
|
||||
use protocol::PIXEL_COUNT;
|
||||
use servicepoint2::PIXEL_COUNT;
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
|
@ -28,8 +28,7 @@ fn main() {
|
|||
let cli = Cli::parse();
|
||||
info!("starting with args: {:?}", &cli);
|
||||
|
||||
let (stop_udp_tx, stop_udp_rx) = mpsc::channel();
|
||||
let thread = start_udp_thread(cli.bind, stop_udp_rx);
|
||||
let thread = UdpThread::start_new(cli.bind);
|
||||
|
||||
let event_loop = EventLoop::new().expect("could not create event loop");
|
||||
event_loop.set_control_flow(ControlFlow::Poll);
|
||||
|
@ -39,6 +38,5 @@ fn main() {
|
|||
.run_app(&mut app)
|
||||
.expect("could not run event loop");
|
||||
|
||||
stop_udp_tx.send(()).expect("could not cancel thread");
|
||||
thread.join().expect("could not join threads");
|
||||
thread.stop_and_wait();
|
||||
}
|
||||
|
|
|
@ -1,27 +1,38 @@
|
|||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use std::mem::size_of;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
#[repr(u16)]
|
||||
#[derive(Debug, FromPrimitive, ToPrimitive, Default)]
|
||||
pub enum DisplayCommand {
|
||||
pub enum DisplayCommandCode {
|
||||
#[default]
|
||||
CmdClear = 0x0002,
|
||||
CmdCp437data = 0x0003,
|
||||
CmdCharBrightness = 0x0005,
|
||||
CmdBrightness = 0x0007,
|
||||
CmdHardReset = 0x000b,
|
||||
CmdFadeOut = 0x000d,
|
||||
CmdBitmapLegacy = 0x0010,
|
||||
CmdBitmapLinear = 0x0012,
|
||||
CmdBitmapLinearWin = 0x0013,
|
||||
CmdBitmapLinearAnd = 0x0014,
|
||||
CmdBitmapLinearOr = 0x0015,
|
||||
CmdBitmapLinearXor = 0x0016,
|
||||
Clear = 0x0002,
|
||||
Cp437data = 0x0003,
|
||||
CharBrightness = 0x0005,
|
||||
Brightness = 0x0007,
|
||||
HardReset = 0x000b,
|
||||
FadeOut = 0x000d,
|
||||
BitmapLegacy = 0x0010,
|
||||
BitmapLinear = 0x0012,
|
||||
BitmapLinearWin = 0x0013,
|
||||
BitmapLinearAnd = 0x0014,
|
||||
BitmapLinearOr = 0x0015,
|
||||
BitmapLinearXor = 0x0016,
|
||||
}
|
||||
|
||||
impl DisplayCommandCode {
|
||||
pub fn from_primitive(value: u16) -> Option<Self> {
|
||||
num::FromPrimitive::from_u16(value)
|
||||
}
|
||||
|
||||
pub fn to_primitive(&self) -> u16 {
|
||||
num::ToPrimitive::to_u16(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct HdrWindow {
|
||||
pub command: DisplayCommand,
|
||||
pub command: DisplayCommandCode,
|
||||
pub x: u16,
|
||||
pub y: u16,
|
||||
pub w: u16,
|
||||
|
@ -49,27 +60,22 @@ pub enum DisplaySubcommand {
|
|||
SubCmdBitmapCompressZs = 0x7a73,
|
||||
}
|
||||
|
||||
pub const TILE_SIZE: u16 = 8;
|
||||
pub const TILE_WIDTH: u16 = 56;
|
||||
pub const TILE_HEIGHT: u16 = 20;
|
||||
pub const PIXEL_WIDTH: u16 = TILE_WIDTH * TILE_SIZE;
|
||||
pub const PIXEL_HEIGHT: u16 = TILE_HEIGHT * TILE_SIZE;
|
||||
pub const PIXEL_COUNT: usize = PIXEL_WIDTH as usize * PIXEL_HEIGHT as usize;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ReadHeaderError {
|
||||
BufferTooSmall,
|
||||
WrongCommandEndianness(u16, DisplayCommand),
|
||||
WrongCommandEndianness(u16, DisplayCommandCode),
|
||||
InvalidCommand(u16),
|
||||
}
|
||||
|
||||
pub fn read_header(buffer: &[u8]) -> Result<HdrWindow, ReadHeaderError> {
|
||||
assert_eq!(size_of::<HdrWindow>(), 10, "invalid struct size");
|
||||
|
||||
if buffer.len() < size_of::<HdrWindow>() {
|
||||
return Err(ReadHeaderError::BufferTooSmall);
|
||||
}
|
||||
|
||||
let command_u16 = read_beu16(&buffer[0..=1]);
|
||||
return match num::FromPrimitive::from_u16(command_u16) {
|
||||
return match DisplayCommandCode::from_primitive(command_u16) {
|
||||
Some(command) => Ok(HdrWindow {
|
||||
command,
|
||||
x: read_beu16(&buffer[2..=3]),
|
||||
|
@ -78,8 +84,7 @@ pub fn read_header(buffer: &[u8]) -> Result<HdrWindow, ReadHeaderError> {
|
|||
h: read_beu16(&buffer[8..=9]),
|
||||
}),
|
||||
None => {
|
||||
let maybe_command: Option<DisplayCommand> =
|
||||
num::FromPrimitive::from_u16(u16::swap_bytes(command_u16));
|
||||
let maybe_command = DisplayCommandCode::from_primitive(u16::swap_bytes(command_u16));
|
||||
return match maybe_command {
|
||||
None => Err(ReadHeaderError::InvalidCommand(command_u16)),
|
||||
Some(command) => Err(ReadHeaderError::WrongCommandEndianness(
|
||||
|
|
129
src/upd_loop.rs
129
src/upd_loop.rs
|
@ -1,28 +1,36 @@
|
|||
use crate::protocol::{
|
||||
read_header, DisplayCommand, HdrWindow, ReadHeaderError, PIXEL_WIDTH, TILE_SIZE,
|
||||
};
|
||||
use crate::font::BitmapFont;
|
||||
use crate::protocol::{read_header, DisplayCommandCode, HdrWindow, ReadHeaderError};
|
||||
use crate::DISPLAY;
|
||||
use log::{error, info, warn};
|
||||
use log::{debug, error, info, warn};
|
||||
use servicepoint2::{PixelGrid, PIXEL_WIDTH, TILE_SIZE};
|
||||
use std::io::ErrorKind;
|
||||
use std::mem::size_of;
|
||||
use std::net::UdpSocket;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use std::net::{ToSocketAddrs, UdpSocket};
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::thread;
|
||||
use std::thread::JoinHandle;
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn start_udp_thread(bind: String, stop_receiver: Receiver<()>) -> JoinHandle<()> {
|
||||
assert_eq!(size_of::<HdrWindow>(), 10, "invalid struct size");
|
||||
pub struct UdpThread {
|
||||
thread: JoinHandle<()>,
|
||||
stop_tx: Sender<()>,
|
||||
}
|
||||
|
||||
impl UdpThread {
|
||||
pub fn start_new(bind: impl ToSocketAddrs) -> Self {
|
||||
let (stop_tx, stop_rx) = mpsc::channel();
|
||||
|
||||
return thread::spawn(move || {
|
||||
let socket = UdpSocket::bind(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 thread = thread::spawn(move || {
|
||||
let mut buf = [0; 8985];
|
||||
|
||||
while stop_receiver.try_recv().is_err() {
|
||||
while stop_rx.try_recv().is_err() {
|
||||
let (amount, _) = match socket.recv_from(&mut buf) {
|
||||
Err(err) if err.kind() == ErrorKind::WouldBlock => {
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
|
@ -39,12 +47,19 @@ pub fn start_udp_thread(bind: String, stop_receiver: Receiver<()>) -> JoinHandle
|
|||
);
|
||||
}
|
||||
|
||||
handle_package(&mut buf[..amount]);
|
||||
Self::handle_package(&mut buf[..amount], &font);
|
||||
}
|
||||
});
|
||||
|
||||
return Self { stop_tx, thread };
|
||||
}
|
||||
|
||||
fn handle_package(received: &mut [u8]) {
|
||||
pub fn stop_and_wait(self) {
|
||||
self.stop_tx.send(()).expect("could not send stop packet");
|
||||
self.thread.join().expect("could not wait on udp thread");
|
||||
}
|
||||
|
||||
fn handle_package(received: &mut [u8], font: &BitmapFont) {
|
||||
let header = match read_header(&received[..10]) {
|
||||
Err(ReadHeaderError::BufferTooSmall) => {
|
||||
error!("received a packet that is too small");
|
||||
|
@ -73,21 +88,21 @@ fn handle_package(received: &mut [u8]) {
|
|||
);
|
||||
|
||||
match header.command {
|
||||
DisplayCommand::CmdClear => {
|
||||
DisplayCommandCode::Clear => {
|
||||
info!("clearing display");
|
||||
for v in unsafe { DISPLAY.iter_mut() } {
|
||||
*v = false;
|
||||
}
|
||||
}
|
||||
DisplayCommand::CmdHardReset => {
|
||||
DisplayCommandCode::HardReset => {
|
||||
warn!("display shutting down");
|
||||
return;
|
||||
}
|
||||
DisplayCommand::CmdBitmapLinearWin => {
|
||||
print_bitmap_linear_win(&header, payload);
|
||||
DisplayCommandCode::BitmapLinearWin => {
|
||||
Self::print_bitmap_linear_win(&header, payload);
|
||||
}
|
||||
DisplayCommand::CmdCp437data => {
|
||||
print_cp437_data(&header, payload);
|
||||
DisplayCommandCode::Cp437data => {
|
||||
Self::print_cp437_data(&header, payload, font);
|
||||
}
|
||||
_ => {
|
||||
error!("command {:?} not implemented yet", header.command);
|
||||
|
@ -109,56 +124,56 @@ fn check_payload_size(buf: &[u8], expected: usize) -> bool {
|
|||
}
|
||||
|
||||
fn print_bitmap_linear_win(header: &HdrWindow, payload: &[u8]) {
|
||||
if !check_payload_size(payload, header.w as usize * header.h as usize) {
|
||||
if !Self::check_payload_size(payload, header.w as usize * header.h as usize) {
|
||||
return;
|
||||
}
|
||||
|
||||
info!(
|
||||
"top left is offset {} tiles in x-direction and {} pixels in y-direction",
|
||||
header.x, header.y
|
||||
let pixel_grid = PixelGrid::load(
|
||||
header.w as usize * TILE_SIZE as usize,
|
||||
header.h as usize,
|
||||
payload,
|
||||
);
|
||||
|
||||
for y in 0..header.h {
|
||||
for byte_x in 0..header.w {
|
||||
let byte_index = (y * header.w + byte_x) as usize;
|
||||
let byte = payload[byte_index];
|
||||
|
||||
for pixel_x in 0u8..8u8 {
|
||||
let bit_index = 7 - pixel_x;
|
||||
let bitmask = 1 << bit_index;
|
||||
let is_set = byte & bitmask != 0;
|
||||
|
||||
let x = byte_x * TILE_SIZE + pixel_x as u16;
|
||||
|
||||
let translated_x = (x + header.x) as usize;
|
||||
let translated_y = (y + header.y) as usize;
|
||||
let index = translated_y * PIXEL_WIDTH as usize + translated_x;
|
||||
|
||||
unsafe {
|
||||
DISPLAY[index] = is_set;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::print_pixel_grid(
|
||||
header.x as usize * TILE_SIZE as usize,
|
||||
header.y as usize,
|
||||
&pixel_grid,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: actually convert from CP437
|
||||
fn print_cp437_data(header: &HdrWindow, payload: &[u8]) {
|
||||
if !check_payload_size(payload, (header.w * header.h) as usize) {
|
||||
fn print_cp437_data(header: &HdrWindow, payload: &[u8], font: &BitmapFont) {
|
||||
if !UdpThread::check_payload_size(payload, (header.w * header.h) as usize) {
|
||||
return;
|
||||
}
|
||||
|
||||
info!("top left is offset by ({} | {}) tiles", header.x, header.y);
|
||||
for char_y in 0usize..header.h as usize {
|
||||
for char_x in 0usize..header.w as usize {
|
||||
let char_code = payload[char_y * header.w as usize + char_x];
|
||||
|
||||
let mut str = String::new();
|
||||
for y in 0..header.h {
|
||||
for x in 0..header.w {
|
||||
let byte_index = (y * header.w + x) as usize;
|
||||
str.push(payload[byte_index] as char);
|
||||
let tile_x = char_x + header.x as usize;
|
||||
let tile_y = char_y + header.y as usize;
|
||||
|
||||
let bitmap = font.get_bitmap(char_code);
|
||||
Self::print_pixel_grid(
|
||||
tile_x * TILE_SIZE as usize,
|
||||
tile_y * TILE_SIZE as usize,
|
||||
bitmap,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
str.push('\n');
|
||||
fn print_pixel_grid(offset_x: usize, offset_y: usize, pixels: &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);
|
||||
let display_index =
|
||||
(offset_x + inner_x) + ((offset_y + inner_y) * PIXEL_WIDTH as usize);
|
||||
unsafe {
|
||||
DISPLAY[display_index] = is_set;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!("{}", str);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue