buncha stuff

This commit is contained in:
Joe Ardent 2023-08-05 13:16:56 -07:00
parent cd059a6552
commit 2b96b28db6
3 changed files with 85 additions and 48 deletions

View file

@ -3,15 +3,17 @@ name = "cuttle"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features]
default = ["desktop"]
desktop = ["dep:eframe", "dep:egui_extras"]
[dependencies] [dependencies]
clap = { version = "4.3.19", features = ["derive", "env"] } clap = { version = "4.3.19", features = ["derive", "env"] }
eframe = { version = "0.22", default-features = false, features = ["default_fonts", "wgpu", "tts", "accesskit"] } eframe = { version = "0.22", default-features = false, optional = true, features = ["default_fonts", "wgpu", "tts", "accesskit"] }
egui_extras = { version = "0.22", default-features = false, features = ["chrono", "image"] } egui_extras = { version = "0.22", default-features = false, optional = true, features = ["chrono", "image"] }
env_logger = "*" env_logger = "*"
fast_qr = { version = "0.9", default-features = false, features = ["image"] } fast_qr = { version = "0.9", default-features = false, features = ["image"] }
png = "0.17.6" # pinning this to resolve conflict between eframe and fast_qr with the image crate png = "0.17.6" # pinning this to resolve conflict between eframe and fast_qr with the image crate
rand = { version = "0.8", default-features = false, features = ["std"] } rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] }
raptorq = "1.7" raptorq = "1.7"
rkyv = { version = "0.7.42", features = ["validation"] } rkyv = { version = "0.7.42", features = ["validation"] }

View file

@ -1,79 +1,105 @@
use std::{fmt::Debug, time::Duration}; use std::{
fmt::Debug,
time::{Duration, Instant},
};
#[cfg(feature = "desktop")]
use eframe::egui; use eframe::egui;
#[cfg(feature = "desktop")]
use egui_extras::RetainedImage; use egui_extras::RetainedImage;
use fast_qr::convert::image::ImageBuilder; use fast_qr::convert::image::ImageBuilder;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use raptorq::{Encoder, ObjectTransmissionInformation}; use raptorq::{Encoder, ObjectTransmissionInformation};
use rkyv::{Archive, Deserialize, Serialize}; use rkyv::{Archive, Deserialize, Serialize};
pub const STREAMING_MTU: u16 = 776; pub const STREAMING_MTU: u16 = 2326;
pub type Receiver = std::sync::mpsc::Receiver<Vec<u8>>; pub type Receiver = std::sync::mpsc::Receiver<(usize, Vec<u8>)>;
pub type Sender = std::sync::mpsc::SyncSender<Vec<u8>>; pub type Sender = std::sync::mpsc::SyncSender<(usize, Vec<u8>)>;
/// The application state
#[derive(Debug)]
pub struct Flasher {
pub heading: String,
pub content: Content,
pub sleep: Duration,
pub last: Instant,
pub idx: usize,
}
#[derive(Debug)]
pub enum Content {
Static(&'static [u8]),
Streaming(Receiver),
}
#[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Archive, Serialize, Deserialize)]
#[repr(C)]
pub struct TxConfig { pub struct TxConfig {
pub len: u64, pub len: u64,
pub mtu: u16, pub mtu: u16,
pub description: String, pub description: String,
} }
pub fn mk_img(bytes: &[u8]) -> RetainedImage { pub fn stream_bytes(bytes: Vec<u8>, tx: Sender, desc: String) {
let qr = fast_qr::QRBuilder::new(bytes).build().unwrap();
let bytes = ImageBuilder::default()
.fit_width(1100)
.to_pixmap(&qr)
.encode_png()
.unwrap();
RetainedImage::from_image_bytes("generated qr code", &bytes).unwrap()
}
pub fn stream_bytes(bytes: Vec<u8>, tx: Sender, rate: u64) {
let sleep = (1000.0 / rate as f64) as u64;
std::thread::spawn(move || { std::thread::spawn(move || {
let rng = &mut rand::thread_rng(); let rng = &mut rand::thread_rng();
let config =
ObjectTransmissionInformation::with_defaults(bytes.len() as u64, STREAMING_MTU); let len = bytes.len() as u64;
let txconfig = TxConfig {
len,
mtu: STREAMING_MTU,
description: desc.to_string(),
};
let txconfig =
rkyv::to_bytes::<_, 256>(&txconfig).expect("tried to serialize the txconfig");
let config = ObjectTransmissionInformation::with_defaults(len, STREAMING_MTU);
let encoder = Encoder::new(&bytes, config); let encoder = Encoder::new(&bytes, config);
let mut packets = encoder let mut packets = encoder
.get_encoded_packets(10) .get_encoded_packets(10)
.iter() .iter()
.map(|p| p.serialize()) .map(|p| p.serialize())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let txcfgs = vec![txconfig.to_vec(); packets.len() / 10];
packets.extend(txcfgs);
packets.shuffle(rng); packets.shuffle(rng);
for (_counter, packet) in packets.iter().cycle().enumerate() { for (counter, packet) in packets.iter().cycle().enumerate() {
tx.send(packet.clone()).unwrap(); tx.send((counter, packet.clone())).unwrap();
std::thread::sleep(Duration::from_millis(sleep));
} }
}); });
} }
pub struct Flasher { pub fn mk_img(bytes: &[u8]) -> Vec<u8> {
pub heading: String, let qr = fast_qr::QRBuilder::new(bytes)
pub content: Content, .ecl(fast_qr::ECL::M)
pub rate: u64, .build()
} .unwrap();
pub enum Content { ImageBuilder::default()
Static(&'static [u8]), .fit_width(1100)
Streaming(Receiver), .to_pixmap(&qr)
.encode_png()
.unwrap()
} }
#[cfg(feature = "desktop")]
impl eframe::App for Flasher { impl eframe::App for Flasher {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
let sleep = 1000.0 / self.rate as f64; ctx.request_repaint_after(self.sleep);
ctx.request_repaint_after(Duration::from_millis(sleep as u64));
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
ui.heading(&self.heading); ui.heading(&self.heading);
let img = match self.content { let img = match self.content {
Content::Static(bytes) => mk_img(bytes), Content::Static(bytes) => {
RetainedImage::from_image_bytes("generated qr code", &mk_img(bytes)).unwrap()
}
Content::Streaming(ref rx) => { Content::Streaming(ref rx) => {
if let Ok(bytes) = rx.try_recv() { if let Ok((_counter, ref bytes)) = rx.try_recv() {
mk_img(&bytes) RetainedImage::from_image_bytes("generated qr code", &mk_img(bytes))
.unwrap()
} else { } else {
return; return;
} }

View file

@ -1,4 +1,7 @@
use std::ffi::OsString; use std::{
ffi::OsString,
time::{Duration, Instant},
};
use clap::Parser; use clap::Parser;
use cuttle::{stream_bytes, Content, Flasher}; use cuttle::{stream_bytes, Content, Flasher};
@ -10,8 +13,9 @@ struct Cli {
#[clap(long, short, help = "File to expose")] #[clap(long, short, help = "File to expose")]
pub file: Option<OsString>, pub file: Option<OsString>,
#[clap(long, short, help = "Frames per second", default_value_t = 10)] #[clap(long, help = "Frames per second", default_value_t = 10)]
pub rate: u64, pub fps: u64,
#[clap( #[clap(
help = "all remaining arguments treated as a string; this string is the whole message if `-f` is not given, otherwise it's an optional description of the file" help = "all remaining arguments treated as a string; this string is the whole message if `-f` is not given, otherwise it's an optional description of the file"
)] )]
@ -41,12 +45,17 @@ fn main() -> Result<(), eframe::Error> {
"text message".to_string() "text message".to_string()
}; };
let content: Content = get_content(&cli); let content: Content = get_content(&cli, &description);
let sleep = 1000.0 / cli.fps as f64;
let sleep = Duration::from_millis(sleep as u64);
let last = Instant::now();
let flasher = Flasher { let flasher = Flasher {
heading: description, heading: description,
content, content,
rate: cli.rate, sleep,
last,
idx: 0,
}; };
let options = eframe::NativeOptions { let options = eframe::NativeOptions {
@ -63,7 +72,7 @@ fn main() -> Result<(), eframe::Error> {
) )
} }
fn get_content(cli: &Cli) -> Content { fn get_content(cli: &Cli, desc: &str) -> Content {
let bytes = if let Some(ref file) = cli.file { let bytes = if let Some(ref file) = cli.file {
std::fs::read(file).unwrap_or_else(|e| panic!("tried to open {file:?}, got {e:?}")) std::fs::read(file).unwrap_or_else(|e| panic!("tried to open {file:?}, got {e:?}"))
} else { } else {
@ -75,7 +84,7 @@ fn get_content(cli: &Cli) -> Content {
Content::Static(bytes) Content::Static(bytes)
} else { } else {
let (tx, rx) = std::sync::mpsc::sync_channel(2); let (tx, rx) = std::sync::mpsc::sync_channel(2);
stream_bytes(bytes, tx, cli.rate); stream_bytes(bytes, tx, desc.to_string());
Content::Streaming(rx) Content::Streaming(rx)
} }
} }