From 63a2fd00b9dd9f72802c51614577bfb4b6ce933c Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 1 May 2025 20:42:15 +0200 Subject: [PATCH 1/7] update to the (unreleased) version of servicepoint --- Cargo.lock | 242 ++-------------------------------------- Cargo.toml | 12 +- src/brightness.rs | 9 +- src/cli.rs | 28 ++--- src/image_processing.rs | 4 +- src/ledwand_dither.rs | 8 +- src/main.rs | 23 +--- src/pixels.rs | 33 +++--- src/stream_stdin.rs | 20 ++-- src/stream_window.rs | 15 +-- src/text.rs | 4 +- 11 files changed, 94 insertions(+), 304 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b9ad7ea..bbeb2eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,15 +204,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "built" version = "0.7.7" @@ -243,12 +234,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" -[[package]] -name = "bytes" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" - [[package]] name = "cc" version = "1.2.16" @@ -455,15 +440,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.4.2" @@ -504,16 +480,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "ctrlc" version = "3.4.5" @@ -524,12 +490,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "data-encoding" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" - [[package]] name = "dbus" version = "0.9.7" @@ -541,16 +501,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "dispatch" version = "0.2.0" @@ -649,12 +599,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - [[package]] name = "foreign-types" version = "0.5.0" @@ -777,16 +721,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -795,19 +729,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "wasi", ] [[package]] @@ -848,23 +770,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "http" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "httparse" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" - [[package]] name = "humantime" version = "2.1.0" @@ -946,12 +851,6 @@ dependencies = [ "either", ] -[[package]] -name = "itoa" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" - [[package]] name = "jobserver" version = "0.1.32" @@ -1363,7 +1262,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -1431,19 +1330,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.2", - "zerocopy 0.8.21", + "rand_chacha", + "rand_core", ] [[package]] @@ -1453,17 +1341,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.2", + "rand_core", ] [[package]] @@ -1472,17 +1350,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", -] - -[[package]] -name = "rand_core" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c" -dependencies = [ - "getrandom 0.3.1", - "zerocopy 0.8.21", + "getrandom", ] [[package]] @@ -1511,8 +1379,8 @@ dependencies = [ "once_cell", "paste", "profiling", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand", + "rand_chacha", "simd_helpers", "system-deps", "thiserror 1.0.69", @@ -1632,7 +1500,7 @@ dependencies = [ "dbus", "objc", "pipewire", - "rand 0.8.5", + "rand", "screencapturekit", "screencapturekit-sys", "sysinfo", @@ -1701,16 +1569,14 @@ dependencies = [ [[package]] name = "servicepoint" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33abd53582a995aaf5d387be4a1f7eb294a084185f88f8cf61652b6272041660" +version = "0.14.0" +source = "git+https://git.berlin.ccc.de/servicepoint/servicepoint/?branch=next#8ddbaeaaa64a4abb8ffb8b08a3a84cc1135e312f" dependencies = [ "bitvec", "log", "once_cell", "rust-lzma", "thiserror 2.0.11", - "tungstenite", ] [[package]] @@ -1726,17 +1592,6 @@ dependencies = [ "servicepoint", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -1927,29 +1782,6 @@ dependencies = [ "winnow", ] -[[package]] -name = "tungstenite" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.0", - "sha1", - "thiserror 2.0.11", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" - [[package]] name = "unicode-ident" version = "1.0.17" @@ -1968,12 +1800,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8parse" version = "0.2.2" @@ -2003,27 +1829,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasi" -version = "0.13.3+wasi-0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" -dependencies = [ - "wit-bindgen-rt", -] - [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -2289,15 +2100,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "wit-bindgen-rt" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" -dependencies = [ - "bitflags 2.8.0", -] - [[package]] name = "wyz" version = "0.5.1" @@ -2323,16 +2125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" -dependencies = [ - "zerocopy-derive 0.8.21", + "zerocopy-derive", ] [[package]] @@ -2346,17 +2139,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zerocopy-derive" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zune-core" version = "0.4.12" diff --git a/Cargo.toml b/Cargo.toml index a8cd60f..cc386bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,20 @@ homepage = "https://crates.io/crates/servicepoint-cli" keywords = ["cccb", "cccb-servicepoint", "cli"] [dependencies] -servicepoint = { version = "0.13.2", features = ["protocol_websocket"] } clap = { version = "4.5", features = ["derive"] } env_logger = "0.11" log = "0.4" scap = "0.0.8" image = "0.25.5" fast_image_resize = { version = "5.1.2", features = ["image"] } + +[dependencies.servicepoint] +package = "servicepoint" +version = "0.14.0" +git = "https://git.berlin.ccc.de/servicepoint/servicepoint/" +branch = "next" + +[profile.release] +lto = true # Enable link-time optimization +codegen-units = 1 # Reduce number of codegen units to increase optimizations +strip = true # Strip symbols from binary diff --git a/src/brightness.rs b/src/brightness.rs index adead44..c2011de 100644 --- a/src/brightness.rs +++ b/src/brightness.rs @@ -1,8 +1,9 @@ +use std::net::UdpSocket; use crate::cli::BrightnessCommand; use log::info; -use servicepoint::{Brightness, Command, Connection}; +use servicepoint::{Brightness, GlobalBrightnessCommand, SendCommandExt}; -pub(crate) fn brightness(connection: &Connection, brightness_command: BrightnessCommand) { +pub(crate) fn brightness(connection: &UdpSocket, brightness_command: BrightnessCommand) { match brightness_command { BrightnessCommand::Max => brightness_set(connection, Brightness::MAX), BrightnessCommand::Min => brightness_set(connection, Brightness::MIN), @@ -12,9 +13,9 @@ pub(crate) fn brightness(connection: &Connection, brightness_command: Brightness } } -pub(crate) fn brightness_set(connection: &Connection, brightness: Brightness) { +pub(crate) fn brightness_set(connection: &UdpSocket, brightness: Brightness) { connection - .send(Command::Brightness(brightness)) + .send_command(GlobalBrightnessCommand::from(brightness)) .expect("Failed to set brightness"); info!("set brightness to {brightness:?}"); } diff --git a/src/cli.rs b/src/cli.rs index 365ff5a..3d76c00 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -12,14 +12,14 @@ pub struct Cli { default_value = "127.0.0.1:2342" )] pub destination: String, - #[arg( - short, - long, - help = "protocol to use for communication with display", - value_enum, - default_value = "udp" - )] - pub transport: Protocol, + //#[arg( + // short, + // long, + // help = "protocol to use for communication with display", + // value_enum, + // default_value = "udp" + //)] + //pub transport: Protocol, #[clap(subcommand)] pub command: Mode, #[clap(short, long, help = "verbose logging")] @@ -103,12 +103,12 @@ pub enum BrightnessCommand { Min, } -#[derive(clap::ValueEnum, Clone, Debug)] -pub enum Protocol { - Udp, - WebSocket, - Fake, -} +//#[derive(clap::ValueEnum, Clone, Debug)] +//pub enum Protocol { +// Udp, +// WebSocket, +// Fake, +//} #[derive(clap::Parser, std::fmt::Debug)] #[clap(about = "Commands for sending text to the screen")] diff --git a/src/image_processing.rs b/src/image_processing.rs index 4e6baa8..9048dd3 100644 --- a/src/image_processing.rs +++ b/src/image_processing.rs @@ -100,7 +100,7 @@ impl ImageProcessingPipeline { let result = if self.options.no_dither { let cutoff = median_brightness(&orig); let bits = orig.iter().map(move |x| x > &cutoff).collect(); - Bitmap::from_bitvec(orig.width() as usize, bits) + Bitmap::from_bitvec(orig.width() as usize, bits).unwrap() } else { ostromoukhov_dither(orig, u8::MAX / 2) }; @@ -113,7 +113,7 @@ impl ImageProcessingPipeline { let width = source.width(); let result_height = Self::calc_height_without_spacers(source.height()); - let mut result = Bitmap::new(width, result_height); + let mut result = Bitmap::new(width, result_height).unwrap(); let mut source_y = 0; for result_y in 0..result_height { diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index d0e4b43..5c73358 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -1,7 +1,7 @@ //! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand use image::GrayImage; -use servicepoint::{BitVec, Bitmap, PIXEL_HEIGHT}; +use servicepoint::{BitVecU8Msb0, Bitmap, PIXEL_HEIGHT}; type GrayHistogram = [usize; 256]; @@ -169,7 +169,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { assert_eq!(width % 8, 0); let mut source = source.into_raw(); - let mut destination = BitVec::repeat(false, source.len()); + let mut destination = BitVecU8Msb0::repeat(false, source.len()); for y in 0..height as usize { let start = y * width as usize; @@ -200,13 +200,13 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { } } - Bitmap::from_bitvec(width as usize, destination) + Bitmap::from_bitvec(width as usize, destination).unwrap() } #[inline] fn ostromoukhov_dither_pixel( source: &mut [u8], - destination: &mut BitVec, + destination: &mut BitVecU8Msb0, position: usize, width: usize, last_row: bool, diff --git a/src/main.rs b/src/main.rs index 4ca1df4..ca8802e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ +use std::net::UdpSocket; use crate::{ brightness::{brightness, brightness_set}, - cli::{Cli, Mode, Protocol}, + cli::{Cli, Mode}, pixels::{pixels, pixels_off}, text::text }; use clap::Parser; use log::debug; -use servicepoint::{Brightness, Connection}; +use servicepoint::{Brightness, UdpSocketExt}; mod brightness; mod cli; @@ -22,13 +23,14 @@ fn main() { init_logging(cli.verbose); debug!("running with arguments: {:?}", cli); - let connection = make_connection(cli.destination, cli.transport); + let connection = UdpSocket::bind_connect(cli.destination) + .expect("Failed to connect"); debug!("connection established: {:#?}", connection); execute_mode(cli.command, connection); } -pub fn execute_mode(mode: Mode, connection: Connection) { +pub fn execute_mode(mode: Mode, connection: UdpSocket) { match mode { Mode::ResetEverything => { brightness_set(&connection, Brightness::MAX); @@ -40,19 +42,6 @@ pub fn execute_mode(mode: Mode, connection: Connection) { } } -fn make_connection(destination: String, transport: Protocol) -> Connection { - match transport { - Protocol::Udp => Connection::open(destination).expect("Failed to open UDP connection"), - Protocol::WebSocket => { - let url = destination.parse().expect( - "provided destination is not a valid url - make sure it starts with 'ws://'", - ); - Connection::open_websocket(url).expect("Failed to open WebSocket connection") - } - Protocol::Fake => Connection::Fake, - } -} - fn init_logging(debug: bool) { let filter = if debug { log::LevelFilter::Debug diff --git a/src/pixels.rs b/src/pixels.rs index da1aa6d..fdaf9f1 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -1,12 +1,13 @@ +use std::net::UdpSocket; use crate::{ image_processing::ImageProcessingPipeline, cli::{ImageProcessingOptions, PixelCommand, SendImageOptions}, stream_window::stream_window }; use log::info; -use servicepoint::{BitVec, Command, CompressionCode, Connection, Origin, PIXEL_COUNT}; +use servicepoint::{BinaryOperation, BitVecCommand, BitVecU8Msb0, BitmapCommand, ClearCommand, CompressionCode, Origin, SendCommandExt, PIXEL_COUNT}; -pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { +pub(crate) fn pixels(connection: &UdpSocket, pixel_command: PixelCommand) { match pixel_command { PixelCommand::Off => pixels_off(connection), PixelCommand::Flip => pixels_invert(connection), @@ -22,31 +23,33 @@ pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { } } -fn pixels_on(connection: &Connection) { - let mask = BitVec::repeat(true, PIXEL_COUNT); +fn pixels_on(connection: &UdpSocket) { + let mask = BitVecU8Msb0::repeat(true, PIXEL_COUNT); + let command = BitVecCommand{offset: 0, bitvec: mask, compression: CompressionCode::Lzma, operation: BinaryOperation::Overwrite}; connection - .send(Command::BitmapLinear(0, mask, CompressionCode::Lzma)) + .send_command(command) .expect("could not send command"); info!("turned on all pixels") } -fn pixels_invert(connection: &Connection) { - let mask = BitVec::repeat(true, PIXEL_COUNT); +fn pixels_invert(connection: &UdpSocket) { + let mask = BitVecU8Msb0::repeat(true, PIXEL_COUNT); + let command = BitVecCommand{offset: 0, bitvec: mask, compression: CompressionCode::Lzma, operation: BinaryOperation::Xor}; connection - .send(Command::BitmapLinearXor(0, mask, CompressionCode::Lzma)) + .send_command(command) .expect("could not send command"); info!("inverted all pixels"); } -pub(crate) fn pixels_off(connection: &Connection) { +pub(crate) fn pixels_off(connection: &UdpSocket) { connection - .send(Command::Clear) + .send_command(ClearCommand) .expect("failed to clear pixels"); info!("reset pixels"); } fn pixels_image( - connection: &Connection, + connection: &UdpSocket, options: SendImageOptions, processing_options: ImageProcessingOptions, ) { @@ -54,11 +57,11 @@ fn pixels_image( let mut pipeline = ImageProcessingPipeline::new(processing_options); let bitmap = pipeline.process(image); connection - .send(Command::BitmapLinearWin( - Origin::ZERO, + .send_command(BitmapCommand { + origin: Origin::ZERO, bitmap, - CompressionCode::default(), - )) + compression: CompressionCode::default(), + }) .expect("failed to send image command"); info!("sent image to display"); } diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index b8b6cfb..5980b4b 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -1,8 +1,9 @@ +use std::net::UdpSocket; use log::warn; use servicepoint::*; use std::thread::sleep; -pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { +pub(crate) fn stream_stdin(connection: &UdpSocket, slow: bool) { warn!("This mode will break when using multi-byte characters and does not support ANSI escape sequences yet."); let mut app = App { connection, @@ -14,7 +15,7 @@ pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { } struct App<'t> { - connection: &'t Connection, + connection: &'t UdpSocket, mirror: CharGrid, y: usize, slow: bool, @@ -23,7 +24,7 @@ struct App<'t> { impl App<'_> { fn run(&mut self) { self.connection - .send(Command::Clear) + .send_command(ClearCommand) .expect("couldn't clear screen"); let last_y = self.mirror.height() - 1; for line in std::io::stdin().lines() { @@ -63,10 +64,10 @@ impl App<'_> { fn send_mirror(&self) { self.connection - .send(Command::Utf8Data( - Origin::ZERO, - self.mirror.clone(), - )) + .send_command(CharGridCommand { + origin: Origin::ZERO, + grid: self.mirror.clone(), + }) .expect("couldn't send screen to display"); } @@ -76,7 +77,10 @@ impl App<'_> { Self::line_onto_grid(&mut line_grid, 0, line); Self::line_onto_grid(&mut self.mirror, self.y, line); self.connection - .send(Command::Utf8Data(Origin::new(0, self.y), line_grid)) + .send_command(CharGridCommand { + origin: Origin::new(0, self.y), + grid: line_grid + }) .expect("couldn't send single line to screen"); } } diff --git a/src/stream_window.rs b/src/stream_window.rs index f8e41ec..f5f1176 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,3 +1,4 @@ +use std::net::UdpSocket; use crate::{ cli::{ImageProcessingOptions, StreamScreenOptions}, image_processing::ImageProcessingPipeline, @@ -9,11 +10,11 @@ use scap::{ frame::convert_bgra_to_rgb, frame::Frame, }; -use servicepoint::{Command, CompressionCode, Connection, Origin, FRAME_PACING}; +use servicepoint::{BitmapCommand, CompressionCode, Origin, SendCommandExt, FRAME_PACING}; use std::time::{Duration, Instant}; pub fn stream_window( - connection: &Connection, + connection: &UdpSocket, options: StreamScreenOptions, processing_options: ImageProcessingOptions, ) { @@ -36,11 +37,11 @@ pub fn stream_window( trace!("bitmap ready to send in: {:?}", start.elapsed()); connection - .send(Command::BitmapLinearWin( - Origin::ZERO, - bitmap.clone(), - CompressionCode::default(), - )) + .send_command(BitmapCommand { + origin: Origin::ZERO, + bitmap: bitmap.clone(), + compression: CompressionCode::default(), + }) .expect("failed to send frame to display"); debug!("frame time: {:?}", start.elapsed()); diff --git a/src/text.rs b/src/text.rs index 247b9ad..c7ddb8a 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,7 +1,7 @@ -use servicepoint::Connection; +use std::net::UdpSocket; use crate::cli::TextCommand; use crate::stream_stdin::stream_stdin; -pub fn text(connection: &Connection, command: TextCommand) { +pub fn text(connection: &UdpSocket, command: TextCommand) { match command { TextCommand::Stdin { slow } => stream_stdin(connection, slow), } } From 57181b508fd477a23761a2a63bf9e0b2ef71a429 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 4 May 2025 14:35:41 +0200 Subject: [PATCH 2/7] update to servicepoint v0.14.1 --- Cargo.lock | 5 +++-- Cargo.toml | 4 +--- src/brightness.rs | 2 +- src/ledwand_dither.rs | 6 +++--- src/pixels.rs | 6 +++--- src/stream_window.rs | 2 +- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bbeb2eb..ecab629 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1569,8 +1569,9 @@ dependencies = [ [[package]] name = "servicepoint" -version = "0.14.0" -source = "git+https://git.berlin.ccc.de/servicepoint/servicepoint/?branch=next#8ddbaeaaa64a4abb8ffb8b08a3a84cc1135e312f" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f" dependencies = [ "bitvec", "log", diff --git a/Cargo.toml b/Cargo.toml index cc386bf..08994a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,9 +22,7 @@ fast_image_resize = { version = "5.1.2", features = ["image"] } [dependencies.servicepoint] package = "servicepoint" -version = "0.14.0" -git = "https://git.berlin.ccc.de/servicepoint/servicepoint/" -branch = "next" +version = "0.14.1" [profile.release] lto = true # Enable link-time optimization diff --git a/src/brightness.rs b/src/brightness.rs index c2011de..469727f 100644 --- a/src/brightness.rs +++ b/src/brightness.rs @@ -1,7 +1,7 @@ use std::net::UdpSocket; use crate::cli::BrightnessCommand; use log::info; -use servicepoint::{Brightness, GlobalBrightnessCommand, SendCommandExt}; +use servicepoint::{Brightness, GlobalBrightnessCommand, UdpSocketExt}; pub(crate) fn brightness(connection: &UdpSocket, brightness_command: BrightnessCommand) { match brightness_command { diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index 5c73358..61a3d3c 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -1,7 +1,7 @@ //! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand use image::GrayImage; -use servicepoint::{BitVecU8Msb0, Bitmap, PIXEL_HEIGHT}; +use servicepoint::{DisplayBitVec, Bitmap, PIXEL_HEIGHT}; type GrayHistogram = [usize; 256]; @@ -169,7 +169,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { assert_eq!(width % 8, 0); let mut source = source.into_raw(); - let mut destination = BitVecU8Msb0::repeat(false, source.len()); + let mut destination = DisplayBitVec::repeat(false, source.len()); for y in 0..height as usize { let start = y * width as usize; @@ -206,7 +206,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { #[inline] fn ostromoukhov_dither_pixel( source: &mut [u8], - destination: &mut BitVecU8Msb0, + destination: &mut DisplayBitVec, position: usize, width: usize, last_row: bool, diff --git a/src/pixels.rs b/src/pixels.rs index fdaf9f1..d22b39a 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -5,7 +5,7 @@ use crate::{ stream_window::stream_window }; use log::info; -use servicepoint::{BinaryOperation, BitVecCommand, BitVecU8Msb0, BitmapCommand, ClearCommand, CompressionCode, Origin, SendCommandExt, PIXEL_COUNT}; +use servicepoint::{BinaryOperation, BitVecCommand, UdpSocketExt, BitmapCommand, ClearCommand, CompressionCode, DisplayBitVec, Origin, PIXEL_COUNT}; pub(crate) fn pixels(connection: &UdpSocket, pixel_command: PixelCommand) { match pixel_command { @@ -24,7 +24,7 @@ pub(crate) fn pixels(connection: &UdpSocket, pixel_command: PixelCommand) { } fn pixels_on(connection: &UdpSocket) { - let mask = BitVecU8Msb0::repeat(true, PIXEL_COUNT); + let mask = DisplayBitVec::repeat(true, PIXEL_COUNT); let command = BitVecCommand{offset: 0, bitvec: mask, compression: CompressionCode::Lzma, operation: BinaryOperation::Overwrite}; connection .send_command(command) @@ -33,7 +33,7 @@ fn pixels_on(connection: &UdpSocket) { } fn pixels_invert(connection: &UdpSocket) { - let mask = BitVecU8Msb0::repeat(true, PIXEL_COUNT); + let mask = DisplayBitVec::repeat(true, PIXEL_COUNT); let command = BitVecCommand{offset: 0, bitvec: mask, compression: CompressionCode::Lzma, operation: BinaryOperation::Xor}; connection .send_command(command) diff --git a/src/stream_window.rs b/src/stream_window.rs index f5f1176..971e12d 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -10,7 +10,7 @@ use scap::{ frame::convert_bgra_to_rgb, frame::Frame, }; -use servicepoint::{BitmapCommand, CompressionCode, Origin, SendCommandExt, FRAME_PACING}; +use servicepoint::{BitmapCommand, CompressionCode, Origin, UdpSocketExt, FRAME_PACING}; use std::time::{Duration, Instant}; pub fn stream_window( From 3b30d061fa0f50c7c5e767c64d04de5ce4057235 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 4 May 2025 14:35:59 +0200 Subject: [PATCH 3/7] add hard reset --- src/cli.rs | 7 +++++-- src/main.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 3d76c00..0ca554e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -29,7 +29,10 @@ pub struct Cli { #[derive(clap::Parser, std::fmt::Debug)] pub enum Mode { #[command(visible_alias = "r", about = "Reset both pixels and brightness")] - ResetEverything, + Reset { + #[arg(short, long, help = "hard reset screen")] + force: bool, + }, #[command(visible_alias = "p")] Pixels { #[clap(subcommand)] @@ -114,7 +117,7 @@ pub enum BrightnessCommand { #[clap(about = "Commands for sending text to the screen")] pub enum TextCommand { #[command( - about = "Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin`" + about = "Pipe text to the display, example: `journalctl | servicepoint-cli text stdin`" )] Stdin { #[arg( diff --git a/src/main.rs b/src/main.rs index ca8802e..dd32a9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use crate::{ }; use clap::Parser; use log::debug; -use servicepoint::{Brightness, UdpSocketExt}; +use servicepoint::{Brightness, HardResetCommand, UdpSocketExt}; mod brightness; mod cli; @@ -32,9 +32,13 @@ fn main() { pub fn execute_mode(mode: Mode, connection: UdpSocket) { match mode { - Mode::ResetEverything => { - brightness_set(&connection, Brightness::MAX); - pixels_off(&connection); + Mode::Reset{force} => { + if force { + connection.send_command(HardResetCommand).unwrap() + } else { + brightness_set(&connection, Brightness::MAX); + pixels_off(&connection); + } } Mode::Pixels { pixel_command } => pixels(&connection, pixel_command), Mode::Brightness { brightness_command } => brightness(&connection, brightness_command), From 51c60250fbb572308ef0126ce8f60f6977046004 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 4 May 2025 15:29:54 +0200 Subject: [PATCH 4/7] reimplement websocket --- Cargo.lock | 217 ++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/brightness.rs | 9 +- src/cli.rs | 28 +++--- src/ledwand_dither.rs | 2 +- src/main.rs | 20 ++-- src/pixels.rs | 35 ++++--- src/stream_stdin.rs | 10 +- src/stream_window.rs | 6 +- src/text.rs | 10 +- src/transport.rs | 51 ++++++++++ 11 files changed, 327 insertions(+), 62 deletions(-) create mode 100644 src/transport.rs diff --git a/Cargo.lock b/Cargo.lock index ecab629..142bca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -204,6 +204,15 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "built" version = "0.7.7" @@ -234,6 +243,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + [[package]] name = "cc" version = "1.2.16" @@ -440,6 +455,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -480,6 +504,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ctrlc" version = "3.4.5" @@ -490,6 +524,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + [[package]] name = "dbus" version = "0.9.7" @@ -501,6 +541,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -599,6 +649,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foreign-types" version = "0.5.0" @@ -721,6 +777,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -729,7 +795,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -770,6 +848,23 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + [[package]] name = "humantime" version = "2.1.0" @@ -851,6 +946,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "jobserver" version = "0.1.32" @@ -1317,6 +1418,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -1330,8 +1437,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1341,7 +1458,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1350,7 +1477,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", ] [[package]] @@ -1379,8 +1515,8 @@ dependencies = [ "once_cell", "paste", "profiling", - "rand", - "rand_chacha", + "rand 0.8.5", + "rand_chacha 0.3.1", "simd_helpers", "system-deps", "thiserror 1.0.69", @@ -1500,7 +1636,7 @@ dependencies = [ "dbus", "objc", "pipewire", - "rand", + "rand 0.8.5", "screencapturekit", "screencapturekit-sys", "sysinfo", @@ -1591,6 +1727,18 @@ dependencies = [ "log", "scap", "servicepoint", + "tungstenite", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", ] [[package]] @@ -1783,6 +1931,29 @@ dependencies = [ "winnow", ] +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.1", + "sha1", + "thiserror 2.0.11", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "unicode-ident" version = "1.0.17" @@ -1801,6 +1972,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1830,12 +2007,27 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -2101,6 +2293,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 08994a2..f1f2266 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ log = "0.4" scap = "0.0.8" image = "0.25.5" fast_image_resize = { version = "5.1.2", features = ["image"] } +tungstenite = "0.26" [dependencies.servicepoint] package = "servicepoint" diff --git a/src/brightness.rs b/src/brightness.rs index 469727f..49ad898 100644 --- a/src/brightness.rs +++ b/src/brightness.rs @@ -1,9 +1,8 @@ -use std::net::UdpSocket; -use crate::cli::BrightnessCommand; +use crate::{cli::BrightnessCommand, transport::Transport}; use log::info; -use servicepoint::{Brightness, GlobalBrightnessCommand, UdpSocketExt}; +use servicepoint::{Brightness, GlobalBrightnessCommand}; -pub(crate) fn brightness(connection: &UdpSocket, brightness_command: BrightnessCommand) { +pub(crate) fn brightness(connection: &Transport, brightness_command: BrightnessCommand) { match brightness_command { BrightnessCommand::Max => brightness_set(connection, Brightness::MAX), BrightnessCommand::Min => brightness_set(connection, Brightness::MIN), @@ -13,7 +12,7 @@ pub(crate) fn brightness(connection: &UdpSocket, brightness_command: BrightnessC } } -pub(crate) fn brightness_set(connection: &UdpSocket, brightness: Brightness) { +pub(crate) fn brightness_set(connection: &Transport, brightness: Brightness) { connection .send_command(GlobalBrightnessCommand::from(brightness)) .expect("Failed to set brightness"); diff --git a/src/cli.rs b/src/cli.rs index 0ca554e..543ba36 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -12,14 +12,14 @@ pub struct Cli { default_value = "127.0.0.1:2342" )] pub destination: String, - //#[arg( - // short, - // long, - // help = "protocol to use for communication with display", - // value_enum, - // default_value = "udp" - //)] - //pub transport: Protocol, + #[arg( + short, + long, + help = "protocol to use for communication with display", + value_enum, + default_value = "udp" + )] + pub transport: TransportType, #[clap(subcommand)] pub command: Mode, #[clap(short, long, help = "verbose logging")] @@ -106,12 +106,12 @@ pub enum BrightnessCommand { Min, } -//#[derive(clap::ValueEnum, Clone, Debug)] -//pub enum Protocol { -// Udp, -// WebSocket, -// Fake, -//} +#[derive(clap::ValueEnum, Clone, Debug)] +pub enum TransportType { + Udp, + WebSocket, + Fake, +} #[derive(clap::Parser, std::fmt::Debug)] #[clap(about = "Commands for sending text to the screen")] diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index 61a3d3c..95ffe02 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -1,7 +1,7 @@ //! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand use image::GrayImage; -use servicepoint::{DisplayBitVec, Bitmap, PIXEL_HEIGHT}; +use servicepoint::{Bitmap, DisplayBitVec, PIXEL_HEIGHT}; type GrayHistogram = [usize; 256]; diff --git a/src/main.rs b/src/main.rs index dd32a9e..cdae13c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,13 @@ -use std::net::UdpSocket; use crate::{ brightness::{brightness, brightness_set}, cli::{Cli, Mode}, pixels::{pixels, pixels_off}, - text::text + text::text, + transport::Transport, }; use clap::Parser; use log::debug; -use servicepoint::{Brightness, HardResetCommand, UdpSocketExt}; +use servicepoint::{Brightness, HardResetCommand}; mod brightness; mod cli; @@ -17,22 +17,22 @@ mod pixels; mod stream_stdin; mod stream_window; mod text; +mod transport; fn main() { let cli = Cli::parse(); init_logging(cli.verbose); debug!("running with arguments: {:?}", cli); - let connection = UdpSocket::bind_connect(cli.destination) - .expect("Failed to connect"); - debug!("connection established: {:#?}", connection); + let transport = Transport::connect(cli.transport, &cli.destination); + debug!("connection established: {:#?}", transport); - execute_mode(cli.command, connection); + execute_mode(cli.command, transport); } -pub fn execute_mode(mode: Mode, connection: UdpSocket) { +pub fn execute_mode(mode: Mode, connection: Transport) { match mode { - Mode::Reset{force} => { + Mode::Reset { force } => { if force { connection.send_command(HardResetCommand).unwrap() } else { @@ -42,7 +42,7 @@ pub fn execute_mode(mode: Mode, connection: UdpSocket) { } Mode::Pixels { pixel_command } => pixels(&connection, pixel_command), Mode::Brightness { brightness_command } => brightness(&connection, brightness_command), - Mode::Text { text_command} => text(&connection, text_command), + Mode::Text { text_command } => text(&connection, text_command), } } diff --git a/src/pixels.rs b/src/pixels.rs index d22b39a..a7aad66 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -1,13 +1,16 @@ -use std::net::UdpSocket; use crate::{ - image_processing::ImageProcessingPipeline, cli::{ImageProcessingOptions, PixelCommand, SendImageOptions}, - stream_window::stream_window + image_processing::ImageProcessingPipeline, + stream_window::stream_window, + transport::Transport, }; use log::info; -use servicepoint::{BinaryOperation, BitVecCommand, UdpSocketExt, BitmapCommand, ClearCommand, CompressionCode, DisplayBitVec, Origin, PIXEL_COUNT}; +use servicepoint::{ + BinaryOperation, BitVecCommand, BitmapCommand, ClearCommand, CompressionCode, DisplayBitVec, + Origin, PIXEL_COUNT, +}; -pub(crate) fn pixels(connection: &UdpSocket, pixel_command: PixelCommand) { +pub(crate) fn pixels(connection: &Transport, pixel_command: PixelCommand) { match pixel_command { PixelCommand::Off => pixels_off(connection), PixelCommand::Flip => pixels_invert(connection), @@ -23,25 +26,35 @@ pub(crate) fn pixels(connection: &UdpSocket, pixel_command: PixelCommand) { } } -fn pixels_on(connection: &UdpSocket) { +fn pixels_on(connection: &Transport) { let mask = DisplayBitVec::repeat(true, PIXEL_COUNT); - let command = BitVecCommand{offset: 0, bitvec: mask, compression: CompressionCode::Lzma, operation: BinaryOperation::Overwrite}; + let command = BitVecCommand { + offset: 0, + bitvec: mask, + compression: CompressionCode::Lzma, + operation: BinaryOperation::Overwrite, + }; connection .send_command(command) .expect("could not send command"); info!("turned on all pixels") } -fn pixels_invert(connection: &UdpSocket) { +fn pixels_invert(connection: &Transport) { let mask = DisplayBitVec::repeat(true, PIXEL_COUNT); - let command = BitVecCommand{offset: 0, bitvec: mask, compression: CompressionCode::Lzma, operation: BinaryOperation::Xor}; + let command = BitVecCommand { + offset: 0, + bitvec: mask, + compression: CompressionCode::Lzma, + operation: BinaryOperation::Xor, + }; connection .send_command(command) .expect("could not send command"); info!("inverted all pixels"); } -pub(crate) fn pixels_off(connection: &UdpSocket) { +pub(crate) fn pixels_off(connection: &Transport) { connection .send_command(ClearCommand) .expect("failed to clear pixels"); @@ -49,7 +62,7 @@ pub(crate) fn pixels_off(connection: &UdpSocket) { } fn pixels_image( - connection: &UdpSocket, + connection: &Transport, options: SendImageOptions, processing_options: ImageProcessingOptions, ) { diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index 5980b4b..872461c 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -1,9 +1,9 @@ -use std::net::UdpSocket; +use crate::transport::Transport; use log::warn; use servicepoint::*; use std::thread::sleep; -pub(crate) fn stream_stdin(connection: &UdpSocket, slow: bool) { +pub(crate) fn stream_stdin(connection: &Transport, slow: bool) { warn!("This mode will break when using multi-byte characters and does not support ANSI escape sequences yet."); let mut app = App { connection, @@ -15,7 +15,7 @@ pub(crate) fn stream_stdin(connection: &UdpSocket, slow: bool) { } struct App<'t> { - connection: &'t UdpSocket, + connection: &'t Transport, mirror: CharGrid, y: usize, slow: bool, @@ -78,8 +78,8 @@ impl App<'_> { Self::line_onto_grid(&mut self.mirror, self.y, line); self.connection .send_command(CharGridCommand { - origin: Origin::new(0, self.y), - grid: line_grid + origin: Origin::new(0, self.y), + grid: line_grid, }) .expect("couldn't send single line to screen"); } diff --git a/src/stream_window.rs b/src/stream_window.rs index 971e12d..ffbc717 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,7 +1,7 @@ -use std::net::UdpSocket; use crate::{ cli::{ImageProcessingOptions, StreamScreenOptions}, image_processing::ImageProcessingPipeline, + transport::Transport, }; use image::{DynamicImage, ImageBuffer, Rgb, Rgba}; use log::{debug, error, info, trace, warn}; @@ -10,11 +10,11 @@ use scap::{ frame::convert_bgra_to_rgb, frame::Frame, }; -use servicepoint::{BitmapCommand, CompressionCode, Origin, UdpSocketExt, FRAME_PACING}; +use servicepoint::{BitmapCommand, CompressionCode, Origin, FRAME_PACING}; use std::time::{Duration, Instant}; pub fn stream_window( - connection: &UdpSocket, + connection: &Transport, options: StreamScreenOptions, processing_options: ImageProcessingOptions, ) { diff --git a/src/text.rs b/src/text.rs index c7ddb8a..b5fbd4c 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,7 +1,7 @@ -use std::net::UdpSocket; -use crate::cli::TextCommand; -use crate::stream_stdin::stream_stdin; +use crate::{cli::TextCommand, stream_stdin::stream_stdin, transport::Transport}; -pub fn text(connection: &UdpSocket, command: TextCommand) { - match command { TextCommand::Stdin { slow } => stream_stdin(connection, slow), } +pub fn text(connection: &Transport, command: TextCommand) { + match command { + TextCommand::Stdin { slow } => stream_stdin(connection, slow), + } } diff --git a/src/transport.rs b/src/transport.rs new file mode 100644 index 0000000..03c651a --- /dev/null +++ b/src/transport.rs @@ -0,0 +1,51 @@ +use crate::cli::TransportType; +use servicepoint::{FakeConnection, Packet, UdpSocketExt}; +use std::fmt::Debug; +use std::net::{TcpStream, UdpSocket}; +use std::sync::Mutex; +use tungstenite::client::IntoClientRequest; +use tungstenite::stream::MaybeTlsStream; +use tungstenite::{ClientRequestBuilder, WebSocket}; + +#[derive(Debug)] +pub enum Transport { + Fake, + Udp(UdpSocket), + WebSocket(Mutex>>), +} + +impl Transport { + pub fn connect(kind: TransportType, destination: &str) -> Transport { + match kind { + TransportType::Udp => { + Self::Udp(UdpSocket::bind_connect(destination).expect("failed to bind socket")) + } + TransportType::WebSocket => { + let request = ClientRequestBuilder::new( + destination.parse().expect("Invalid destination url"), + ) + .into_client_request() + .unwrap(); + let (sock, _) = + tungstenite::connect(request).expect("failed to connect to websocket"); + Self::WebSocket(Mutex::new(sock)) + } + TransportType::Fake => Self::Fake, + } + } + + pub(crate) fn send_command>(&self, command: T) -> Option<()> + where + >::Error: Debug, + { + match self { + Self::Udp(socket) => socket.send_command(command), + Self::WebSocket(socket) => { + let bytes: Vec = command.try_into().unwrap().into(); + let mut socket = socket.lock().unwrap(); + socket.send(tungstenite::Message::Binary(bytes.into())).ok() + } + Self::Fake => FakeConnection.send_command(command), + } + } +} From 8f3c209fa399fa1fb26737b94ba804b376f00ae7 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 4 May 2025 15:48:49 +0200 Subject: [PATCH 5/7] update README.md usage --- README.md | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index be1dab7..138dc7d 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,15 @@ cargo run -- ## Usage -``` +```text Usage: servicepoint-cli [OPTIONS] Commands: - reset-everything Reset both pixels and brightness [aliases: r] - pixels Commands for manipulating pixels [aliases: p] - brightness Commands for manipulating the brightness [aliases: b] - text Commands for sending text to the screen [aliases: t] - help Print this message or the help of the given subcommand(s) + reset Reset both pixels and brightness [aliases: r] + pixels Commands for manipulating pixels [aliases: p] + brightness Commands for manipulating the brightness [aliases: b] + text Commands for sending text to the screen [aliases: t] + help Print this message or the help of the given subcommand(s) Options: -d, --destination ip:port of the servicepoint display [default: 127.0.0.1:2342] @@ -53,7 +53,7 @@ Options: ### Pixels -``` +```text Commands for manipulating pixels Usage: servicepoint-cli pixels @@ -68,7 +68,7 @@ Commands: #### Image -``` +```text Send an image file (e.g. jpeg or png) to the display. Usage: servicepoint-cli pixels image [OPTIONS] @@ -87,7 +87,7 @@ Options: #### Screen -``` +```text Stream the default screen capture source to the display. On Linux Wayland, this pops up a screen or window chooser, but it also may directly start streaming your main screen. Usage: servicepoint-cli pixels screen [OPTIONS] @@ -104,7 +104,7 @@ Options: ### Brightness -``` +```text Commands for manipulating the brightness Usage: servicepoint-cli brightness @@ -117,18 +117,18 @@ Commands: ### Text -``` +```text Commands for sending text to the screen Usage: servicepoint-cli text Commands: - stdin Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` + stdin Pipe text to the display, example: `journalctl | servicepoint-cli text stdin` ``` #### Stdin -``` +```text Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` Usage: servicepoint-cli stream stdin [OPTIONS] @@ -137,6 +137,16 @@ Options: -s, --slow Wait for a short amount of time before sending the next line ``` +### Reset + +```text +Reset both pixels and brightness + +Usage: servicepoint-cli reset [OPTIONS] + +Options: +-f, --force hard reset screen +``` ## Contributing From 044583141c1fe8d6e7dbcfee527ffb32f73388aa Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 1 May 2025 20:42:15 +0200 Subject: [PATCH 6/7] update to servicepoint v0.14.1 --- Cargo.lock | 90 +++++++++++++++++------------------------ Cargo.toml | 11 ++++- src/brightness.rs | 10 ++--- src/cli.rs | 4 +- src/image_processing.rs | 4 +- src/ledwand_dither.rs | 8 ++-- src/main.rs | 31 +++++--------- src/pixels.rs | 50 +++++++++++++++-------- src/stream_stdin.rs | 20 +++++---- src/stream_window.rs | 15 +++---- src/text.rs | 10 ++--- src/transport.rs | 51 +++++++++++++++++++++++ 12 files changed, 179 insertions(+), 125 deletions(-) create mode 100644 src/transport.rs diff --git a/Cargo.lock b/Cargo.lock index b9ad7ea..142bca9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,9 +245,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" @@ -526,9 +526,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "dbus" @@ -800,14 +800,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -850,9 +850,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "http" -version = "1.2.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -861,9 +861,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "humantime" @@ -948,9 +948,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" @@ -1363,7 +1363,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy 0.7.35", + "zerocopy", ] [[package]] @@ -1418,6 +1418,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radium" version = "0.7.0" @@ -1437,13 +1443,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.2", - "zerocopy 0.8.21", + "rand_core 0.9.3", ] [[package]] @@ -1463,7 +1468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.2", + "rand_core 0.9.3", ] [[package]] @@ -1477,12 +1482,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", - "zerocopy 0.8.21", + "getrandom 0.3.2", ] [[package]] @@ -1701,16 +1705,15 @@ dependencies = [ [[package]] name = "servicepoint" -version = "0.13.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33abd53582a995aaf5d387be4a1f7eb294a084185f88f8cf61652b6272041660" +checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f" dependencies = [ "bitvec", "log", "once_cell", "rust-lzma", "thiserror 2.0.11", - "tungstenite", ] [[package]] @@ -1724,6 +1727,7 @@ dependencies = [ "log", "scap", "servicepoint", + "tungstenite", ] [[package]] @@ -1938,7 +1942,7 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.0", + "rand 0.9.1", "sha1", "thiserror 2.0.11", "utf-8", @@ -2017,9 +2021,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -2291,9 +2295,9 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.8.0", ] @@ -2323,16 +2327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive 0.7.35", -] - -[[package]] -name = "zerocopy" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" -dependencies = [ - "zerocopy-derive 0.8.21", + "zerocopy-derive", ] [[package]] @@ -2346,17 +2341,6 @@ dependencies = [ "syn", ] -[[package]] -name = "zerocopy-derive" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zune-core" version = "0.4.12" diff --git a/Cargo.toml b/Cargo.toml index a8cd60f..f1f2266 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,19 @@ homepage = "https://crates.io/crates/servicepoint-cli" keywords = ["cccb", "cccb-servicepoint", "cli"] [dependencies] -servicepoint = { version = "0.13.2", features = ["protocol_websocket"] } clap = { version = "4.5", features = ["derive"] } env_logger = "0.11" log = "0.4" scap = "0.0.8" image = "0.25.5" fast_image_resize = { version = "5.1.2", features = ["image"] } +tungstenite = "0.26" + +[dependencies.servicepoint] +package = "servicepoint" +version = "0.14.1" + +[profile.release] +lto = true # Enable link-time optimization +codegen-units = 1 # Reduce number of codegen units to increase optimizations +strip = true # Strip symbols from binary diff --git a/src/brightness.rs b/src/brightness.rs index adead44..49ad898 100644 --- a/src/brightness.rs +++ b/src/brightness.rs @@ -1,8 +1,8 @@ -use crate::cli::BrightnessCommand; +use crate::{cli::BrightnessCommand, transport::Transport}; use log::info; -use servicepoint::{Brightness, Command, Connection}; +use servicepoint::{Brightness, GlobalBrightnessCommand}; -pub(crate) fn brightness(connection: &Connection, brightness_command: BrightnessCommand) { +pub(crate) fn brightness(connection: &Transport, brightness_command: BrightnessCommand) { match brightness_command { BrightnessCommand::Max => brightness_set(connection, Brightness::MAX), BrightnessCommand::Min => brightness_set(connection, Brightness::MIN), @@ -12,9 +12,9 @@ pub(crate) fn brightness(connection: &Connection, brightness_command: Brightness } } -pub(crate) fn brightness_set(connection: &Connection, brightness: Brightness) { +pub(crate) fn brightness_set(connection: &Transport, brightness: Brightness) { connection - .send(Command::Brightness(brightness)) + .send_command(GlobalBrightnessCommand::from(brightness)) .expect("Failed to set brightness"); info!("set brightness to {brightness:?}"); } diff --git a/src/cli.rs b/src/cli.rs index 365ff5a..bfec0ac 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -19,7 +19,7 @@ pub struct Cli { value_enum, default_value = "udp" )] - pub transport: Protocol, + pub transport: TransportType, #[clap(subcommand)] pub command: Mode, #[clap(short, long, help = "verbose logging")] @@ -104,7 +104,7 @@ pub enum BrightnessCommand { } #[derive(clap::ValueEnum, Clone, Debug)] -pub enum Protocol { +pub enum TransportType { Udp, WebSocket, Fake, diff --git a/src/image_processing.rs b/src/image_processing.rs index 4e6baa8..9048dd3 100644 --- a/src/image_processing.rs +++ b/src/image_processing.rs @@ -100,7 +100,7 @@ impl ImageProcessingPipeline { let result = if self.options.no_dither { let cutoff = median_brightness(&orig); let bits = orig.iter().map(move |x| x > &cutoff).collect(); - Bitmap::from_bitvec(orig.width() as usize, bits) + Bitmap::from_bitvec(orig.width() as usize, bits).unwrap() } else { ostromoukhov_dither(orig, u8::MAX / 2) }; @@ -113,7 +113,7 @@ impl ImageProcessingPipeline { let width = source.width(); let result_height = Self::calc_height_without_spacers(source.height()); - let mut result = Bitmap::new(width, result_height); + let mut result = Bitmap::new(width, result_height).unwrap(); let mut source_y = 0; for result_y in 0..result_height { diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index d0e4b43..95ffe02 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -1,7 +1,7 @@ //! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand use image::GrayImage; -use servicepoint::{BitVec, Bitmap, PIXEL_HEIGHT}; +use servicepoint::{Bitmap, DisplayBitVec, PIXEL_HEIGHT}; type GrayHistogram = [usize; 256]; @@ -169,7 +169,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { assert_eq!(width % 8, 0); let mut source = source.into_raw(); - let mut destination = BitVec::repeat(false, source.len()); + let mut destination = DisplayBitVec::repeat(false, source.len()); for y in 0..height as usize { let start = y * width as usize; @@ -200,13 +200,13 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { } } - Bitmap::from_bitvec(width as usize, destination) + Bitmap::from_bitvec(width as usize, destination).unwrap() } #[inline] fn ostromoukhov_dither_pixel( source: &mut [u8], - destination: &mut BitVec, + destination: &mut DisplayBitVec, position: usize, width: usize, last_row: bool, diff --git a/src/main.rs b/src/main.rs index 4ca1df4..d72bbfd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ use crate::{ brightness::{brightness, brightness_set}, - cli::{Cli, Mode, Protocol}, + cli::{Cli, Mode}, pixels::{pixels, pixels_off}, - text::text + text::text, + transport::Transport, }; use clap::Parser; use log::debug; -use servicepoint::{Brightness, Connection}; +use servicepoint::{Brightness, UdpSocketExt}; mod brightness; mod cli; @@ -16,19 +17,20 @@ mod pixels; mod stream_stdin; mod stream_window; mod text; +mod transport; fn main() { let cli = Cli::parse(); init_logging(cli.verbose); debug!("running with arguments: {:?}", cli); - let connection = make_connection(cli.destination, cli.transport); - debug!("connection established: {:#?}", connection); + let transport = Transport::connect(cli.transport, &cli.destination); + debug!("connection established: {:#?}", transport); - execute_mode(cli.command, connection); + execute_mode(cli.command, transport); } -pub fn execute_mode(mode: Mode, connection: Connection) { +pub fn execute_mode(mode: Mode, connection: Transport) { match mode { Mode::ResetEverything => { brightness_set(&connection, Brightness::MAX); @@ -36,20 +38,7 @@ pub fn execute_mode(mode: Mode, connection: Connection) { } Mode::Pixels { pixel_command } => pixels(&connection, pixel_command), Mode::Brightness { brightness_command } => brightness(&connection, brightness_command), - Mode::Text { text_command} => text(&connection, text_command), - } -} - -fn make_connection(destination: String, transport: Protocol) -> Connection { - match transport { - Protocol::Udp => Connection::open(destination).expect("Failed to open UDP connection"), - Protocol::WebSocket => { - let url = destination.parse().expect( - "provided destination is not a valid url - make sure it starts with 'ws://'", - ); - Connection::open_websocket(url).expect("Failed to open WebSocket connection") - } - Protocol::Fake => Connection::Fake, + Mode::Text { text_command } => text(&connection, text_command), } } diff --git a/src/pixels.rs b/src/pixels.rs index da1aa6d..a7aad66 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -1,12 +1,16 @@ use crate::{ - image_processing::ImageProcessingPipeline, cli::{ImageProcessingOptions, PixelCommand, SendImageOptions}, - stream_window::stream_window + image_processing::ImageProcessingPipeline, + stream_window::stream_window, + transport::Transport, }; use log::info; -use servicepoint::{BitVec, Command, CompressionCode, Connection, Origin, PIXEL_COUNT}; +use servicepoint::{ + BinaryOperation, BitVecCommand, BitmapCommand, ClearCommand, CompressionCode, DisplayBitVec, + Origin, PIXEL_COUNT, +}; -pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { +pub(crate) fn pixels(connection: &Transport, pixel_command: PixelCommand) { match pixel_command { PixelCommand::Off => pixels_off(connection), PixelCommand::Flip => pixels_invert(connection), @@ -22,31 +26,43 @@ pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { } } -fn pixels_on(connection: &Connection) { - let mask = BitVec::repeat(true, PIXEL_COUNT); +fn pixels_on(connection: &Transport) { + let mask = DisplayBitVec::repeat(true, PIXEL_COUNT); + let command = BitVecCommand { + offset: 0, + bitvec: mask, + compression: CompressionCode::Lzma, + operation: BinaryOperation::Overwrite, + }; connection - .send(Command::BitmapLinear(0, mask, CompressionCode::Lzma)) + .send_command(command) .expect("could not send command"); info!("turned on all pixels") } -fn pixels_invert(connection: &Connection) { - let mask = BitVec::repeat(true, PIXEL_COUNT); +fn pixels_invert(connection: &Transport) { + let mask = DisplayBitVec::repeat(true, PIXEL_COUNT); + let command = BitVecCommand { + offset: 0, + bitvec: mask, + compression: CompressionCode::Lzma, + operation: BinaryOperation::Xor, + }; connection - .send(Command::BitmapLinearXor(0, mask, CompressionCode::Lzma)) + .send_command(command) .expect("could not send command"); info!("inverted all pixels"); } -pub(crate) fn pixels_off(connection: &Connection) { +pub(crate) fn pixels_off(connection: &Transport) { connection - .send(Command::Clear) + .send_command(ClearCommand) .expect("failed to clear pixels"); info!("reset pixels"); } fn pixels_image( - connection: &Connection, + connection: &Transport, options: SendImageOptions, processing_options: ImageProcessingOptions, ) { @@ -54,11 +70,11 @@ fn pixels_image( let mut pipeline = ImageProcessingPipeline::new(processing_options); let bitmap = pipeline.process(image); connection - .send(Command::BitmapLinearWin( - Origin::ZERO, + .send_command(BitmapCommand { + origin: Origin::ZERO, bitmap, - CompressionCode::default(), - )) + compression: CompressionCode::default(), + }) .expect("failed to send image command"); info!("sent image to display"); } diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index b8b6cfb..872461c 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -1,8 +1,9 @@ +use crate::transport::Transport; use log::warn; use servicepoint::*; use std::thread::sleep; -pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { +pub(crate) fn stream_stdin(connection: &Transport, slow: bool) { warn!("This mode will break when using multi-byte characters and does not support ANSI escape sequences yet."); let mut app = App { connection, @@ -14,7 +15,7 @@ pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { } struct App<'t> { - connection: &'t Connection, + connection: &'t Transport, mirror: CharGrid, y: usize, slow: bool, @@ -23,7 +24,7 @@ struct App<'t> { impl App<'_> { fn run(&mut self) { self.connection - .send(Command::Clear) + .send_command(ClearCommand) .expect("couldn't clear screen"); let last_y = self.mirror.height() - 1; for line in std::io::stdin().lines() { @@ -63,10 +64,10 @@ impl App<'_> { fn send_mirror(&self) { self.connection - .send(Command::Utf8Data( - Origin::ZERO, - self.mirror.clone(), - )) + .send_command(CharGridCommand { + origin: Origin::ZERO, + grid: self.mirror.clone(), + }) .expect("couldn't send screen to display"); } @@ -76,7 +77,10 @@ impl App<'_> { Self::line_onto_grid(&mut line_grid, 0, line); Self::line_onto_grid(&mut self.mirror, self.y, line); self.connection - .send(Command::Utf8Data(Origin::new(0, self.y), line_grid)) + .send_command(CharGridCommand { + origin: Origin::new(0, self.y), + grid: line_grid, + }) .expect("couldn't send single line to screen"); } } diff --git a/src/stream_window.rs b/src/stream_window.rs index f8e41ec..ffbc717 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,6 +1,7 @@ use crate::{ cli::{ImageProcessingOptions, StreamScreenOptions}, image_processing::ImageProcessingPipeline, + transport::Transport, }; use image::{DynamicImage, ImageBuffer, Rgb, Rgba}; use log::{debug, error, info, trace, warn}; @@ -9,11 +10,11 @@ use scap::{ frame::convert_bgra_to_rgb, frame::Frame, }; -use servicepoint::{Command, CompressionCode, Connection, Origin, FRAME_PACING}; +use servicepoint::{BitmapCommand, CompressionCode, Origin, FRAME_PACING}; use std::time::{Duration, Instant}; pub fn stream_window( - connection: &Connection, + connection: &Transport, options: StreamScreenOptions, processing_options: ImageProcessingOptions, ) { @@ -36,11 +37,11 @@ pub fn stream_window( trace!("bitmap ready to send in: {:?}", start.elapsed()); connection - .send(Command::BitmapLinearWin( - Origin::ZERO, - bitmap.clone(), - CompressionCode::default(), - )) + .send_command(BitmapCommand { + origin: Origin::ZERO, + bitmap: bitmap.clone(), + compression: CompressionCode::default(), + }) .expect("failed to send frame to display"); debug!("frame time: {:?}", start.elapsed()); diff --git a/src/text.rs b/src/text.rs index 247b9ad..b5fbd4c 100644 --- a/src/text.rs +++ b/src/text.rs @@ -1,7 +1,7 @@ -use servicepoint::Connection; -use crate::cli::TextCommand; -use crate::stream_stdin::stream_stdin; +use crate::{cli::TextCommand, stream_stdin::stream_stdin, transport::Transport}; -pub fn text(connection: &Connection, command: TextCommand) { - match command { TextCommand::Stdin { slow } => stream_stdin(connection, slow), } +pub fn text(connection: &Transport, command: TextCommand) { + match command { + TextCommand::Stdin { slow } => stream_stdin(connection, slow), + } } diff --git a/src/transport.rs b/src/transport.rs new file mode 100644 index 0000000..03c651a --- /dev/null +++ b/src/transport.rs @@ -0,0 +1,51 @@ +use crate::cli::TransportType; +use servicepoint::{FakeConnection, Packet, UdpSocketExt}; +use std::fmt::Debug; +use std::net::{TcpStream, UdpSocket}; +use std::sync::Mutex; +use tungstenite::client::IntoClientRequest; +use tungstenite::stream::MaybeTlsStream; +use tungstenite::{ClientRequestBuilder, WebSocket}; + +#[derive(Debug)] +pub enum Transport { + Fake, + Udp(UdpSocket), + WebSocket(Mutex>>), +} + +impl Transport { + pub fn connect(kind: TransportType, destination: &str) -> Transport { + match kind { + TransportType::Udp => { + Self::Udp(UdpSocket::bind_connect(destination).expect("failed to bind socket")) + } + TransportType::WebSocket => { + let request = ClientRequestBuilder::new( + destination.parse().expect("Invalid destination url"), + ) + .into_client_request() + .unwrap(); + let (sock, _) = + tungstenite::connect(request).expect("failed to connect to websocket"); + Self::WebSocket(Mutex::new(sock)) + } + TransportType::Fake => Self::Fake, + } + } + + pub(crate) fn send_command>(&self, command: T) -> Option<()> + where + >::Error: Debug, + { + match self { + Self::Udp(socket) => socket.send_command(command), + Self::WebSocket(socket) => { + let bytes: Vec = command.try_into().unwrap().into(); + let mut socket = socket.lock().unwrap(); + socket.send(tungstenite::Message::Binary(bytes.into())).ok() + } + Self::Fake => FakeConnection.send_command(command), + } + } +} From 6a5af6b4f4d8e5598e9fe0bcbcafc7ad59c90004 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 4 May 2025 14:35:59 +0200 Subject: [PATCH 7/7] add hard reset --- README.md | 36 +++++++++++++++++++++++------------- src/cli.rs | 7 +++++-- src/main.rs | 12 ++++++++---- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index be1dab7..138dc7d 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,15 @@ cargo run -- ## Usage -``` +```text Usage: servicepoint-cli [OPTIONS] Commands: - reset-everything Reset both pixels and brightness [aliases: r] - pixels Commands for manipulating pixels [aliases: p] - brightness Commands for manipulating the brightness [aliases: b] - text Commands for sending text to the screen [aliases: t] - help Print this message or the help of the given subcommand(s) + reset Reset both pixels and brightness [aliases: r] + pixels Commands for manipulating pixels [aliases: p] + brightness Commands for manipulating the brightness [aliases: b] + text Commands for sending text to the screen [aliases: t] + help Print this message or the help of the given subcommand(s) Options: -d, --destination ip:port of the servicepoint display [default: 127.0.0.1:2342] @@ -53,7 +53,7 @@ Options: ### Pixels -``` +```text Commands for manipulating pixels Usage: servicepoint-cli pixels @@ -68,7 +68,7 @@ Commands: #### Image -``` +```text Send an image file (e.g. jpeg or png) to the display. Usage: servicepoint-cli pixels image [OPTIONS] @@ -87,7 +87,7 @@ Options: #### Screen -``` +```text Stream the default screen capture source to the display. On Linux Wayland, this pops up a screen or window chooser, but it also may directly start streaming your main screen. Usage: servicepoint-cli pixels screen [OPTIONS] @@ -104,7 +104,7 @@ Options: ### Brightness -``` +```text Commands for manipulating the brightness Usage: servicepoint-cli brightness @@ -117,18 +117,18 @@ Commands: ### Text -``` +```text Commands for sending text to the screen Usage: servicepoint-cli text Commands: - stdin Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` + stdin Pipe text to the display, example: `journalctl | servicepoint-cli text stdin` ``` #### Stdin -``` +```text Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` Usage: servicepoint-cli stream stdin [OPTIONS] @@ -137,6 +137,16 @@ Options: -s, --slow Wait for a short amount of time before sending the next line ``` +### Reset + +```text +Reset both pixels and brightness + +Usage: servicepoint-cli reset [OPTIONS] + +Options: +-f, --force hard reset screen +``` ## Contributing diff --git a/src/cli.rs b/src/cli.rs index bfec0ac..543ba36 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -29,7 +29,10 @@ pub struct Cli { #[derive(clap::Parser, std::fmt::Debug)] pub enum Mode { #[command(visible_alias = "r", about = "Reset both pixels and brightness")] - ResetEverything, + Reset { + #[arg(short, long, help = "hard reset screen")] + force: bool, + }, #[command(visible_alias = "p")] Pixels { #[clap(subcommand)] @@ -114,7 +117,7 @@ pub enum TransportType { #[clap(about = "Commands for sending text to the screen")] pub enum TextCommand { #[command( - about = "Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin`" + about = "Pipe text to the display, example: `journalctl | servicepoint-cli text stdin`" )] Stdin { #[arg( diff --git a/src/main.rs b/src/main.rs index d72bbfd..a485526 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use crate::{ }; use clap::Parser; use log::debug; -use servicepoint::{Brightness, UdpSocketExt}; +use servicepoint::{Brightness, HardResetCommand, UdpSocketExt}; mod brightness; mod cli; @@ -32,9 +32,13 @@ fn main() { pub fn execute_mode(mode: Mode, connection: Transport) { match mode { - Mode::ResetEverything => { - brightness_set(&connection, Brightness::MAX); - pixels_off(&connection); + Mode::Reset { force } => { + if force { + connection.send_command(HardResetCommand).unwrap() + } else { + brightness_set(&connection, Brightness::MAX); + pixels_off(&connection); + } } Mode::Pixels { pixel_command } => pixels(&connection, pixel_command), Mode::Brightness { brightness_command } => brightness(&connection, brightness_command),