Switch to persistent printer with queue
This commit is contained in:
parent
0ea4cf1d22
commit
6555e9c0bd
14 changed files with 221 additions and 76 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod backlog;
|
||||||
mod calendar;
|
mod calendar;
|
||||||
mod cells;
|
mod cells;
|
||||||
mod chat_message;
|
mod chat_message;
|
||||||
|
|
@ -11,21 +12,23 @@ mod typst;
|
||||||
use showbits_common::widgets::{FontStuff, HasFontStuff};
|
use showbits_common::widgets::{FontStuff, HasFontStuff};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
calendar::CalendarDrawing, cells::CellsDrawing, chat_message::ChatMessageDrawing,
|
backlog::BacklogDrawing, calendar::CalendarDrawing, cells::CellsDrawing,
|
||||||
egg::EggDrawing, image::ImageDrawing, photo::PhotoDrawing, text::TextDrawing,
|
chat_message::ChatMessageDrawing, egg::EggDrawing, image::ImageDrawing, photo::PhotoDrawing,
|
||||||
tictactoe::TicTacToeDrawing, typst::TypstDrawing,
|
text::TextDrawing, tictactoe::TicTacToeDrawing, typst::TypstDrawing,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const FEED: f32 = 64.0;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
font_stuff: FontStuff,
|
font_stuff: FontStuff,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Drawing {
|
pub trait Drawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()>;
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Command(Box<dyn Drawing + Send>);
|
pub struct Command(Box<dyn Drawing + Send>);
|
||||||
|
|
@ -44,12 +47,12 @@ impl HasFontStuff for Context {
|
||||||
|
|
||||||
pub struct Drawer {
|
pub struct Drawer {
|
||||||
rx: mpsc::Receiver<Command>,
|
rx: mpsc::Receiver<Command>,
|
||||||
printer: Printer,
|
printer: PersistentPrinter,
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawer {
|
impl Drawer {
|
||||||
pub fn new(rx: mpsc::Receiver<Command>, printer: Printer) -> Self {
|
pub fn new(rx: mpsc::Receiver<Command>, printer: PersistentPrinter) -> Self {
|
||||||
Self {
|
Self {
|
||||||
rx,
|
rx,
|
||||||
printer,
|
printer,
|
||||||
|
|
|
||||||
12
showbits-thermal-printer/src/drawer/backlog.rs
Normal file
12
showbits-thermal-printer/src/drawer/backlog.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
|
use super::{Context, Drawing};
|
||||||
|
|
||||||
|
pub struct BacklogDrawing;
|
||||||
|
|
||||||
|
impl Drawing for BacklogDrawing {
|
||||||
|
fn draw(&self, printer: &mut PersistentPrinter, _ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
|
printer.print_backlog()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,9 +9,9 @@ use taffy::{
|
||||||
style_helpers::{length, percent, repeat},
|
style_helpers::{length, percent, repeat},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing, FEED};
|
||||||
|
|
||||||
pub struct CalendarDrawing {
|
pub struct CalendarDrawing {
|
||||||
pub year: i16,
|
pub year: i16,
|
||||||
|
|
@ -19,7 +19,7 @@ pub struct CalendarDrawing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawing for CalendarDrawing {
|
impl Drawing for CalendarDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let mut date = civil::Date::new(self.year, self.month, 1)?;
|
let mut date = civil::Date::new(self.year, self.month, 1)?;
|
||||||
|
|
||||||
let mut tree = Tree::<Context>::new(WHITE);
|
let mut tree = Tree::<Context>::new(WHITE);
|
||||||
|
|
@ -86,6 +86,7 @@ impl Drawing for CalendarDrawing {
|
||||||
|
|
||||||
let root = Node::empty()
|
let root = Node::empty()
|
||||||
.with_size_width(percent(1.0))
|
.with_size_width(percent(1.0))
|
||||||
|
.with_padding_bottom(length(FEED))
|
||||||
.with_display(Display::Flex)
|
.with_display(Display::Flex)
|
||||||
.with_flex_direction(FlexDirection::Column)
|
.with_flex_direction(FlexDirection::Column)
|
||||||
.with_align_items(Some(AlignItems::Center))
|
.with_align_items(Some(AlignItems::Center))
|
||||||
|
|
@ -94,7 +95,6 @@ impl Drawing for CalendarDrawing {
|
||||||
.register(&mut tree)?;
|
.register(&mut tree)?;
|
||||||
|
|
||||||
printer.print_tree(&mut tree, ctx, root)?;
|
printer.print_tree(&mut tree, ctx, root)?;
|
||||||
printer.feed()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ use image::{
|
||||||
imageops::{self, FilterType},
|
imageops::{self, FilterType},
|
||||||
};
|
};
|
||||||
use showbits_common::{Node, Tree, WidgetExt, color, widgets::Image};
|
use showbits_common::{Node, Tree, WidgetExt, color, widgets::Image};
|
||||||
use taffy::{AlignItems, Display, FlexDirection, style_helpers::percent};
|
use taffy::{AlignItems, Display, FlexDirection, prelude::length, style_helpers::percent};
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::{persistent_printer::PersistentPrinter, printer::Printer};
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing, FEED};
|
||||||
|
|
||||||
const BLACK: Rgba<u8> = Rgba([0, 0, 0, 255]);
|
const BLACK: Rgba<u8> = Rgba([0, 0, 0, 255]);
|
||||||
const WHITE: Rgba<u8> = Rgba([255, 255, 255, 255]);
|
const WHITE: Rgba<u8> = Rgba([255, 255, 255, 255]);
|
||||||
|
|
@ -49,8 +49,8 @@ pub struct CellsDrawing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawing for CellsDrawing {
|
impl Drawing for CellsDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let mut image = RgbaImage::new(Printer::WIDTH / self.scale, self.rows);
|
let mut image: image::ImageBuffer<Rgba<u8>, Vec<u8>> = RgbaImage::new(Printer::WIDTH / self.scale, self.rows);
|
||||||
|
|
||||||
// Initialize first line randomly
|
// Initialize first line randomly
|
||||||
for x in 0..image.width() {
|
for x in 0..image.width() {
|
||||||
|
|
@ -79,6 +79,7 @@ impl Drawing for CellsDrawing {
|
||||||
|
|
||||||
let root = Node::empty()
|
let root = Node::empty()
|
||||||
.with_size_width(percent(1.0))
|
.with_size_width(percent(1.0))
|
||||||
|
.with_padding_bottom(length(FEED))
|
||||||
.with_display(Display::Flex)
|
.with_display(Display::Flex)
|
||||||
.with_flex_direction(FlexDirection::Column)
|
.with_flex_direction(FlexDirection::Column)
|
||||||
.with_align_items(Some(AlignItems::Center))
|
.with_align_items(Some(AlignItems::Center))
|
||||||
|
|
@ -86,7 +87,6 @@ impl Drawing for CellsDrawing {
|
||||||
.register(&mut tree)?;
|
.register(&mut tree)?;
|
||||||
|
|
||||||
printer.print_tree(&mut tree, ctx, root)?;
|
printer.print_tree(&mut tree, ctx, root)?;
|
||||||
printer.feed()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use taffy::{
|
||||||
style_helpers::{length, percent},
|
style_helpers::{length, percent},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing};
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub struct ChatMessageDrawing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawing for ChatMessageDrawing {
|
impl Drawing for ChatMessageDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let mut tree = Tree::<Context>::new(WHITE);
|
let mut tree = Tree::<Context>::new(WHITE);
|
||||||
|
|
||||||
let max_username_width_in_chars = 32.0;
|
let max_username_width_in_chars = 32.0;
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ use showbits_common::{
|
||||||
color::{self, WHITE},
|
color::{self, WHITE},
|
||||||
widgets::{Image, Text},
|
widgets::{Image, Text},
|
||||||
};
|
};
|
||||||
use taffy::{AlignItems, Display, FlexDirection, style_helpers::percent};
|
use taffy::{prelude::length, style_helpers::percent, AlignItems, Display, FlexDirection};
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing, FEED};
|
||||||
|
|
||||||
pub struct EggDrawing;
|
pub struct EggDrawing;
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ fn load_image(bytes: &[u8]) -> RgbaImage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawing for EggDrawing {
|
impl Drawing for EggDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
|
|
||||||
// Choose which set of egg images to use
|
// Choose which set of egg images to use
|
||||||
|
|
@ -84,6 +84,7 @@ impl Drawing for EggDrawing {
|
||||||
|
|
||||||
let root = Node::empty()
|
let root = Node::empty()
|
||||||
.with_size_width(percent(1.0))
|
.with_size_width(percent(1.0))
|
||||||
|
.with_padding_bottom(length(FEED))
|
||||||
.with_display(Display::Flex)
|
.with_display(Display::Flex)
|
||||||
.with_flex_direction(FlexDirection::Column)
|
.with_flex_direction(FlexDirection::Column)
|
||||||
.with_align_items(Some(AlignItems::Center))
|
.with_align_items(Some(AlignItems::Center))
|
||||||
|
|
@ -92,7 +93,6 @@ impl Drawing for EggDrawing {
|
||||||
.register(&mut tree)?;
|
.register(&mut tree)?;
|
||||||
|
|
||||||
printer.print_tree(&mut tree, ctx, root)?;
|
printer.print_tree(&mut tree, ctx, root)?;
|
||||||
printer.feed()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,11 @@ use showbits_common::{
|
||||||
color::{self, BLACK, WHITE},
|
color::{self, BLACK, WHITE},
|
||||||
widgets::{DitherAlgorithm, Image},
|
widgets::{DitherAlgorithm, Image},
|
||||||
};
|
};
|
||||||
use taffy::{AlignItems, Display, FlexDirection, style_helpers::percent};
|
use taffy::{AlignItems, Display, FlexDirection, prelude::length, style_helpers::percent};
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing, FEED};
|
||||||
|
|
||||||
pub struct ImageDrawing {
|
pub struct ImageDrawing {
|
||||||
pub image: RgbaImage,
|
pub image: RgbaImage,
|
||||||
|
|
@ -19,7 +19,7 @@ pub struct ImageDrawing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawing for ImageDrawing {
|
impl Drawing for ImageDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let mut image = self.image.clone();
|
let mut image = self.image.clone();
|
||||||
if self.bright {
|
if self.bright {
|
||||||
for pixel in image.pixels_mut() {
|
for pixel in image.pixels_mut() {
|
||||||
|
|
@ -40,6 +40,7 @@ impl Drawing for ImageDrawing {
|
||||||
|
|
||||||
let root = Node::empty()
|
let root = Node::empty()
|
||||||
.with_size_width(percent(1.0))
|
.with_size_width(percent(1.0))
|
||||||
|
.with_padding_bottom(length(FEED))
|
||||||
.with_display(Display::Flex)
|
.with_display(Display::Flex)
|
||||||
.with_flex_direction(FlexDirection::Column)
|
.with_flex_direction(FlexDirection::Column)
|
||||||
.with_align_items(Some(AlignItems::Center))
|
.with_align_items(Some(AlignItems::Center))
|
||||||
|
|
@ -47,7 +48,6 @@ impl Drawing for ImageDrawing {
|
||||||
.register(&mut tree)?;
|
.register(&mut tree)?;
|
||||||
|
|
||||||
printer.print_tree(&mut tree, ctx, root)?;
|
printer.print_tree(&mut tree, ctx, root)?;
|
||||||
printer.feed()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ use taffy::{
|
||||||
style_helpers::{length, percent},
|
style_helpers::{length, percent},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing, FEED};
|
||||||
|
|
||||||
pub struct PhotoDrawing {
|
pub struct PhotoDrawing {
|
||||||
pub image: RgbaImage,
|
pub image: RgbaImage,
|
||||||
|
|
@ -19,7 +19,7 @@ pub struct PhotoDrawing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drawing for PhotoDrawing {
|
impl Drawing for PhotoDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let mut tree = Tree::<Context>::new(WHITE);
|
let mut tree = Tree::<Context>::new(WHITE);
|
||||||
|
|
||||||
let mut image = self.image.clone();
|
let mut image = self.image.clone();
|
||||||
|
|
@ -45,6 +45,7 @@ impl Drawing for PhotoDrawing {
|
||||||
|
|
||||||
let root = Node::empty()
|
let root = Node::empty()
|
||||||
.with_size_width(percent(1.0))
|
.with_size_width(percent(1.0))
|
||||||
|
.with_padding_bottom(length(FEED))
|
||||||
.with_display(Display::Flex)
|
.with_display(Display::Flex)
|
||||||
.with_flex_direction(FlexDirection::Column)
|
.with_flex_direction(FlexDirection::Column)
|
||||||
.with_align_items(Some(AlignItems::Center))
|
.with_align_items(Some(AlignItems::Center))
|
||||||
|
|
@ -54,7 +55,6 @@ impl Drawing for PhotoDrawing {
|
||||||
.register(&mut tree)?;
|
.register(&mut tree)?;
|
||||||
|
|
||||||
printer.print_tree(&mut tree, ctx, root)?;
|
printer.print_tree(&mut tree, ctx, root)?;
|
||||||
printer.feed()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use showbits_common::{Node, Tree, WidgetExt, color::WHITE, widgets::Text};
|
use showbits_common::{Node, Tree, WidgetExt, color::WHITE, widgets::Text};
|
||||||
use taffy::style_helpers::percent;
|
use taffy::style_helpers::percent;
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing};
|
||||||
|
|
||||||
pub struct TextDrawing(pub String);
|
pub struct TextDrawing(pub String);
|
||||||
|
|
||||||
impl Drawing for TextDrawing {
|
impl Drawing for TextDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let mut tree = Tree::<Context>::new(WHITE);
|
let mut tree = Tree::<Context>::new(WHITE);
|
||||||
|
|
||||||
let text = Text::new()
|
let text = Text::new()
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@ use taffy::{
|
||||||
style_helpers::{length, percent, repeat},
|
style_helpers::{length, percent, repeat},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing, FEED};
|
||||||
|
|
||||||
pub struct TicTacToeDrawing;
|
pub struct TicTacToeDrawing;
|
||||||
|
|
||||||
impl Drawing for TicTacToeDrawing {
|
impl Drawing for TicTacToeDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let block_size = length(128.0);
|
let block_size = length(128.0);
|
||||||
let width = length(2.0);
|
let width = length(2.0);
|
||||||
|
|
||||||
|
|
@ -57,6 +57,7 @@ impl Drawing for TicTacToeDrawing {
|
||||||
|
|
||||||
let root = Node::empty()
|
let root = Node::empty()
|
||||||
.with_size_width(percent(1.0))
|
.with_size_width(percent(1.0))
|
||||||
|
.with_padding_bottom(length(FEED))
|
||||||
.with_display(Display::Flex)
|
.with_display(Display::Flex)
|
||||||
.with_flex_direction(FlexDirection::Column)
|
.with_flex_direction(FlexDirection::Column)
|
||||||
.with_align_items(Some(AlignItems::Center))
|
.with_align_items(Some(AlignItems::Center))
|
||||||
|
|
@ -66,7 +67,6 @@ impl Drawing for TicTacToeDrawing {
|
||||||
.register(&mut tree)?;
|
.register(&mut tree)?;
|
||||||
|
|
||||||
printer.print_tree(&mut tree, ctx, root)?;
|
printer.print_tree(&mut tree, ctx, root)?;
|
||||||
printer.feed()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,25 @@
|
||||||
use showbits_common::{Node, Tree, WidgetExt, color::WHITE, widgets::Typst};
|
use showbits_common::{Node, Tree, WidgetExt, color::WHITE, widgets::Typst};
|
||||||
use taffy::style_helpers::percent;
|
use taffy::{prelude::length, style_helpers::percent};
|
||||||
|
|
||||||
use crate::printer::Printer;
|
use crate::persistent_printer::PersistentPrinter;
|
||||||
|
|
||||||
use super::{Context, Drawing};
|
use super::{Context, Drawing, FEED};
|
||||||
|
|
||||||
pub struct TypstDrawing(pub String);
|
pub struct TypstDrawing(pub String);
|
||||||
|
|
||||||
impl Drawing for TypstDrawing {
|
impl Drawing for TypstDrawing {
|
||||||
fn draw(&self, printer: &mut Printer, ctx: &mut Context) -> anyhow::Result<()> {
|
fn draw(&self, printer: &mut PersistentPrinter, ctx: &mut Context) -> anyhow::Result<()> {
|
||||||
let mut tree = Tree::<Context>::new(WHITE);
|
let mut tree = Tree::<Context>::new(WHITE);
|
||||||
|
|
||||||
let typst = Typst::new(self.0.clone()).node().register(&mut tree)?;
|
let typst = Typst::new(self.0.clone()).node().register(&mut tree)?;
|
||||||
|
|
||||||
let root = Node::empty()
|
let root = Node::empty()
|
||||||
.with_size_width(percent(1.0))
|
.with_size_width(percent(1.0))
|
||||||
|
.with_padding_bottom(length(FEED))
|
||||||
.and_child(typst)
|
.and_child(typst)
|
||||||
.register(&mut tree)?;
|
.register(&mut tree)?;
|
||||||
|
|
||||||
printer.print_tree(&mut tree, ctx, root)?;
|
printer.print_tree(&mut tree, ctx, root)?;
|
||||||
printer.feed()?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
mod drawer;
|
mod drawer;
|
||||||
|
mod persistent_printer;
|
||||||
mod printer;
|
mod printer;
|
||||||
mod server;
|
mod server;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::{path::PathBuf, time::Duration};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use drawer::Drawer;
|
use drawer::{BacklogDrawing, Command};
|
||||||
use printer::Printer;
|
|
||||||
use tokio::{runtime::Runtime, sync::mpsc};
|
use tokio::{runtime::Runtime, sync::mpsc};
|
||||||
|
|
||||||
|
use self::{drawer::Drawer, persistent_printer::PersistentPrinter};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Args {
|
struct Args {
|
||||||
/// Address the web server will listen at.
|
/// Address the web server will listen at.
|
||||||
addr: String,
|
addr: String,
|
||||||
|
|
||||||
|
/// Path to the queue directory.
|
||||||
|
queue: PathBuf,
|
||||||
|
|
||||||
/// Path to the printer's USB device file.
|
/// Path to the printer's USB device file.
|
||||||
///
|
///
|
||||||
/// Usually, this is located at `/dev/usb/lp0` or a similar location.
|
/// Usually, this is located at `/dev/usb/lp0` or a similar location.
|
||||||
|
|
@ -30,11 +35,17 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let (tx, rx) = mpsc::channel(3);
|
let (tx, rx) = mpsc::channel(3);
|
||||||
|
|
||||||
let printer = Printer::new(args.printer, args.export)?;
|
let printer = PersistentPrinter::new(args.printer, args.export, args.queue);
|
||||||
let mut drawer = Drawer::new(rx, printer);
|
let mut drawer = Drawer::new(rx, printer);
|
||||||
|
|
||||||
let runtime = Runtime::new()?;
|
let runtime = Runtime::new()?;
|
||||||
runtime.spawn(server::run(tx, args.addr));
|
runtime.spawn(server::run(tx.clone(), args.addr));
|
||||||
|
runtime.spawn(async move {
|
||||||
|
loop {
|
||||||
|
let _ = tx.send(Command::draw(BacklogDrawing)).await;
|
||||||
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
println!("Running");
|
println!("Running");
|
||||||
drawer.run()?;
|
drawer.run()?;
|
||||||
|
|
|
||||||
135
showbits-thermal-printer/src/persistent_printer.rs
Normal file
135
showbits-thermal-printer/src/persistent_printer.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
use std::{fs, io::ErrorKind, path::PathBuf};
|
||||||
|
|
||||||
|
use anyhow::{Context, bail};
|
||||||
|
use image::RgbaImage;
|
||||||
|
use jiff::Timestamp;
|
||||||
|
use showbits_common::Tree;
|
||||||
|
use taffy::{AvailableSpace, NodeId, Size};
|
||||||
|
|
||||||
|
use crate::printer::Printer;
|
||||||
|
|
||||||
|
pub struct PersistentPrinter {
|
||||||
|
printer_file: Option<PathBuf>,
|
||||||
|
export_file: Option<PathBuf>,
|
||||||
|
queue_dir: PathBuf,
|
||||||
|
|
||||||
|
printer: Option<Printer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersistentPrinter {
|
||||||
|
pub fn new(
|
||||||
|
printer_file: Option<PathBuf>,
|
||||||
|
export_file: Option<PathBuf>,
|
||||||
|
queue_dir: PathBuf,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
printer_file,
|
||||||
|
export_file,
|
||||||
|
queue_dir,
|
||||||
|
printer: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_tree_to_image<C>(
|
||||||
|
tree: &mut Tree<C>,
|
||||||
|
ctx: &mut C,
|
||||||
|
root: NodeId,
|
||||||
|
) -> anyhow::Result<RgbaImage> {
|
||||||
|
let available = Size {
|
||||||
|
width: AvailableSpace::Definite(Printer::WIDTH as f32),
|
||||||
|
// TODO Maybe MinContent? If not, why not?
|
||||||
|
height: AvailableSpace::MaxContent,
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.render(ctx, root, available)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_image(&mut self, image: &RgbaImage) -> anyhow::Result<()> {
|
||||||
|
let Some(printer) = &mut self.printer else {
|
||||||
|
bail!("no printer found");
|
||||||
|
};
|
||||||
|
printer.print_image(image)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reconnect_printer(&mut self) -> anyhow::Result<()> {
|
||||||
|
let printer = Printer::new(self.printer_file.clone(), self.export_file.clone())?;
|
||||||
|
self.printer = Some(printer);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_image_robustly(&mut self, image: &RgbaImage) -> anyhow::Result<()> {
|
||||||
|
println!("Printing image");
|
||||||
|
if self.print_image(image).is_ok() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
println!("First attempt failed, reconnecting and retrying");
|
||||||
|
self.reconnect_printer()?;
|
||||||
|
self.print_image(image)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enqueue_image(&mut self, image: &RgbaImage) -> anyhow::Result<()> {
|
||||||
|
let now = Timestamp::now();
|
||||||
|
let path = self.queue_dir.join(format!("{now}.png"));
|
||||||
|
println!("Enqueuing image {}", path.display());
|
||||||
|
|
||||||
|
fs::create_dir_all(&self.queue_dir)
|
||||||
|
.with_context(|| format!("At {}", self.queue_dir.display()))
|
||||||
|
.context("Failed to create queue directory")?;
|
||||||
|
|
||||||
|
image
|
||||||
|
.save(&path)
|
||||||
|
.with_context(|| format!("At {}", path.display()))
|
||||||
|
.context("Failed to save image to queue")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_tree<C>(
|
||||||
|
&mut self,
|
||||||
|
tree: &mut Tree<C>,
|
||||||
|
ctx: &mut C,
|
||||||
|
root: NodeId,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let image = Self::render_tree_to_image(tree, ctx, root)?;
|
||||||
|
if self.print_image_robustly(&image).is_err() {
|
||||||
|
self.enqueue_image(&image)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_backlog(&mut self) -> anyhow::Result<()> {
|
||||||
|
let mut files = vec![];
|
||||||
|
|
||||||
|
match self.queue_dir.read_dir() {
|
||||||
|
Err(err) if err.kind() == ErrorKind::NotFound => {}
|
||||||
|
|
||||||
|
Err(err) => Err(err)
|
||||||
|
.with_context(|| format!("At {}", self.queue_dir.display()))
|
||||||
|
.context("Failed to open queue dir")?,
|
||||||
|
|
||||||
|
Ok(dir) => {
|
||||||
|
for entry in dir {
|
||||||
|
let entry = entry?;
|
||||||
|
if entry.file_type()?.is_file() {
|
||||||
|
files.push(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
files.sort_unstable();
|
||||||
|
|
||||||
|
for file in files {
|
||||||
|
println!("Dequeuing image {}", file.display());
|
||||||
|
let image: RgbaImage = image::open(&file)?.into_rgba8();
|
||||||
|
if self.print_image_robustly(&image).is_err() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
fs::remove_file(&file)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use escpos::{
|
use escpos::{
|
||||||
driver::FileDriver,
|
driver::FileDriver,
|
||||||
printer::Printer as EPrinter,
|
printer::Printer as EPrinter,
|
||||||
|
|
@ -7,8 +8,7 @@ use escpos::{
|
||||||
utils::{GS, PageCode, Protocol},
|
utils::{GS, PageCode, Protocol},
|
||||||
};
|
};
|
||||||
use image::{Rgba, RgbaImage};
|
use image::{Rgba, RgbaImage};
|
||||||
use showbits_common::{Tree, color};
|
use showbits_common::color;
|
||||||
use taffy::{AvailableSpace, NodeId, Size};
|
|
||||||
|
|
||||||
pub struct Printer {
|
pub struct Printer {
|
||||||
printer: Option<EPrinter<FileDriver>>,
|
printer: Option<EPrinter<FileDriver>>,
|
||||||
|
|
@ -44,7 +44,9 @@ impl Printer {
|
||||||
export_path: Option<PathBuf>,
|
export_path: Option<PathBuf>,
|
||||||
) -> anyhow::Result<Self> {
|
) -> anyhow::Result<Self> {
|
||||||
let printer = if let Some(path) = printer_path {
|
let printer = if let Some(path) = printer_path {
|
||||||
let driver = FileDriver::open(&path)?;
|
let driver = FileDriver::open(&path)
|
||||||
|
.with_context(|| format!("At {}", path.display()))
|
||||||
|
.context("Failed to open printer driver")?;
|
||||||
let protocol = Protocol::default();
|
let protocol = Protocol::default();
|
||||||
let mut options = PrinterOptions::default();
|
let mut options = PrinterOptions::default();
|
||||||
options.page_code(Some(Self::PAGE_CODE));
|
options.page_code(Some(Self::PAGE_CODE));
|
||||||
|
|
@ -59,34 +61,16 @@ impl Printer {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn feed(&mut self) -> anyhow::Result<()> {
|
pub fn print_image(&mut self, image: &RgbaImage) -> anyhow::Result<()> {
|
||||||
if let Some(printer) = &mut self.printer {
|
|
||||||
printer.init()?.feeds(3)?.print()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_tree<C>(
|
|
||||||
&mut self,
|
|
||||||
tree: &mut Tree<C>,
|
|
||||||
ctx: &mut C,
|
|
||||||
root: NodeId,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let available = Size {
|
|
||||||
width: AvailableSpace::Definite(Self::WIDTH as f32),
|
|
||||||
// TODO Maybe MinContent? If not, why not?
|
|
||||||
height: AvailableSpace::MaxContent,
|
|
||||||
};
|
|
||||||
|
|
||||||
let image = tree.render(ctx, root, available)?;
|
|
||||||
|
|
||||||
if let Some(path) = &self.export_path {
|
if let Some(path) = &self.export_path {
|
||||||
image.save(path)?;
|
image
|
||||||
|
.save(path)
|
||||||
|
.with_context(|| format!("At {}", path.display()))
|
||||||
|
.context("Failed to export to-be-printed image")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(printer) = &mut self.printer {
|
if let Some(printer) = &mut self.printer {
|
||||||
Self::print_image_to_printer(printer, &image)?;
|
Self::print_image_to_printer(printer, image).context("Failed to print image")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue