From 9e299f69f66ce47873cbd4eb141936e732451780 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 12 Feb 2025 18:11:38 +0100 Subject: [PATCH 01/36] add simple stdin streaming --- src/cli.rs | 4 +++ src/execute.rs | 2 ++ src/main.rs | 1 + src/stream_stdin.rs | 81 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 src/stream_stdin.rs diff --git a/src/cli.rs b/src/cli.rs index 8826a7d..d59ca53 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -36,6 +36,10 @@ pub enum Mode { #[clap(subcommand)] brightness_command: BrightnessCommand, }, + StreamStdin { + #[arg(long, short, default_value_t = false)] + slow: bool + } } #[derive(clap::Parser, std::fmt::Debug)] diff --git a/src/execute.rs b/src/execute.rs index bbad4ed..fa28cdd 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -1,6 +1,7 @@ use crate::cli::{BrightnessCommand, Mode, PixelCommand}; use log::info; use servicepoint::{Brightness, Command, Connection}; +use crate::stream_stdin::stream_stdin; pub fn execute_mode(mode: Mode, connection: Connection) { match mode { @@ -10,6 +11,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::StreamStdin{slow} => stream_stdin(&connection, slow), } } diff --git a/src/main.rs b/src/main.rs index cb6515a..76db66a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ use servicepoint::Connection; mod cli; mod execute; +mod stream_stdin; fn main() { let cli = Cli::parse(); diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs new file mode 100644 index 0000000..5deffb3 --- /dev/null +++ b/src/stream_stdin.rs @@ -0,0 +1,81 @@ +use std::thread::sleep; +use log::warn; +use servicepoint::*; + +pub(crate) fn stream_stdin(connection: &Connection, 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, + mirror: CharGrid::new(TILE_WIDTH, TILE_HEIGHT), + y: 0, + slow + }; + app.run() +} + +struct App<'t> { + connection: &'t Connection, + mirror: CharGrid, + y: usize, + slow: bool, +} + +impl<'t> App<'t> { + fn run(&mut self) { + self.connection + .send(Command::Clear) + .expect("couldn't clear screen"); + let last_y = self.mirror.height() - 1; + for line in std::io::stdin().lines() { + let line = line.expect("could not read from stdin"); + + if self.y <= last_y { + self.single_line(&line); + self.y += 1; + } else { + self.shift_rows(); + Self::line_onto_grid(&mut self.mirror, last_y, &line); + self.send_mirror() + // we stay on last y + } + + if self.slow { + sleep(FRAME_PACING); + } + } + } + + fn shift_rows(&mut self) { + let data = self.mirror.data_ref_mut(); + data.rotate_left(TILE_WIDTH); + if let Some(row) = data.last_chunk_mut::() { + row.fill(' ') + } + } + + fn line_onto_grid(grid: &mut CharGrid, y: usize, line: &str) { + for (x, char) in line.chars().enumerate() { + if x < grid.width() { + grid.set(x, y, char); + } + } + } + + fn send_mirror(&self) { + self.connection + .send(Command::Cp437Data( + Origin::ZERO, + Cp437Grid::from(&self.mirror), + )) + .expect("couldn't send screen to display"); + } + + fn single_line(&mut self, line: &str) { + let mut line_grid = CharGrid::new(TILE_WIDTH, 1); + 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)) + .expect("couldn't send single line to screen"); + } +} From ef19ab8b3f91164bf9053f657d4508c82af57ce6 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 12 Feb 2025 20:30:27 +0100 Subject: [PATCH 02/36] stream screen to display --- Cargo.lock | 1626 +++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 4 +- flake.nix | 23 +- src/cli.rs | 11 +- src/execute.rs | 6 +- src/main.rs | 1 + src/stream_stdin.rs | 4 +- src/stream_window.rs | 115 +++ 8 files changed, 1775 insertions(+), 15 deletions(-) create mode 100644 src/stream_window.rs diff --git a/Cargo.lock b/Cargo.lock index b05ca8f..d91bde5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -11,6 +17,22 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "annotate-snippets" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e" +dependencies = [ + "unicode-width", + "yansi-term", +] + [[package]] name = "anstream" version = "0.6.18" @@ -61,6 +83,109 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "annotate-snippets", + "bitflags 2.8.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + [[package]] name = "bitvec" version = "1.0.1" @@ -73,6 +198,12 @@ dependencies = [ "wyz", ] +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + [[package]] name = "block-buffer" version = "0.10.4" @@ -82,24 +213,95 @@ dependencies = [ "generic-array", ] +[[package]] +name = "built" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73848a43c5d63a1251d17adf6c2bf78aa94830e60a335a95eeea45d6ba9e1e4d" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytemuck" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +[[package]] +name = "cc" +version = "1.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.28" @@ -140,12 +342,119 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cocoa" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation", + "core-graphics-types", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" +dependencies = [ + "futures", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-helmer-fork" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" +dependencies = [ + "bitflags 2.8.0", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.16" @@ -155,6 +464,46 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + [[package]] name = "crypto-common" version = "0.1.6" @@ -165,12 +514,33 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctrlc" +version = "3.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +dependencies = [ + "nix 0.29.0", + "windows-sys", +] + [[package]] name = "data-encoding" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" +[[package]] +name = "dbus" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" +dependencies = [ + "libc", + "libdbus-sys", + "winapi", +] + [[package]] name = "digest" version = "0.10.7" @@ -181,6 +551,18 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "env_filter" version = "0.1.3" @@ -204,18 +586,174 @@ dependencies = [ "log", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -237,6 +775,38 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + [[package]] name = "heck" version = "0.5.0" @@ -266,48 +836,486 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libfuzzer-sys" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "libspa" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65f3a4b81b2a2d8c7f300643676202debd1b7c929dbf5c9bb89402ea11d19810" +dependencies = [ + "bitflags 2.8.0", + "cc", + "convert_case", + "cookie-factory", + "libc", + "libspa-sys", + "nix 0.27.1", + "nom", + "system-deps", +] + +[[package]] +name = "libspa-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0d9716420364790e85cbb9d3ac2c950bde16a7dd36f3209b7dfdfc4a24d01f" +dependencies = [ + "bindgen", + "cc", + "system-deps", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "libc", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.8.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pipewire" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08e645ba5c45109106d56610b3ee60eb13a6f2beb8b74f8dc8186cf261788dda" +dependencies = [ + "anyhow", + "bitflags 2.8.0", + "libc", + "libspa", + "libspa-sys", + "nix 0.27.1", + "once_cell", + "pipewire-sys", + "thiserror 1.0.69", +] + +[[package]] +name = "pipewire-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "849e188f90b1dda88fe2bfe1ad31fe5f158af2c98f80fb5d13726c44f3f01112" +dependencies = [ + "bindgen", + "libspa-sys", + "system-deps", +] + [[package]] name = "pkg-config" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -326,6 +1334,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.38" @@ -371,6 +1413,85 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "regex" version = "1.11.1" @@ -400,6 +1521,12 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" + [[package]] name = "rust-lzma" version = "0.6.0" @@ -410,6 +1537,96 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "scap" +version = "0.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55817d631bc81ba5f347ff5e112c87b8cc0dd99fac0e8e363bf451c3edfdb763" +dependencies = [ + "cocoa", + "core-graphics-helmer-fork", + "dbus", + "objc", + "pipewire", + "rand", + "screencapturekit", + "screencapturekit-sys", + "sysinfo", + "tao-core-video-sys", + "windows 0.58.0", + "windows-capture", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "screencapturekit" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5eeeb57ac94960cfe5ff4c402be6585ae4c8d29a2cf41b276048c2e849d64e" +dependencies = [ + "screencapturekit-sys", +] + +[[package]] +name = "screencapturekit-sys" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22411b57f7d49e7fe08025198813ee6fd65e1ee5eff4ebc7880c12c82bde4c60" +dependencies = [ + "block", + "dispatch", + "objc", + "objc-foundation", + "objc_id", + "once_cell", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "servicepoint" version = "0.13.0" @@ -420,7 +1637,7 @@ dependencies = [ "log", "once_cell", "rust-lzma", - "thiserror", + "thiserror 2.0.11", "tungstenite", ] @@ -430,7 +1647,9 @@ version = "0.1.0" dependencies = [ "clap", "env_logger", + "image", "log", + "scap", "servicepoint", ] @@ -445,6 +1664,42 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "strsim" version = "0.11.1" @@ -462,19 +1717,85 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "rayon", + "windows 0.52.0", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tao-core-video-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271450eb289cb4d8d0720c6ce70c72c8c858c93dd61fc625881616752e6b98f6" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "objc", +] + [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.11", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -488,6 +1809,51 @@ dependencies = [ "syn", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tungstenite" version = "0.26.1" @@ -502,7 +1868,7 @@ dependencies = [ "log", "rand", "sha1", - "thiserror", + "thiserror 2.0.11", "utf-8", ] @@ -518,6 +1884,18 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "utf-8" version = "0.7.6" @@ -530,12 +1908,29 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.5" @@ -548,6 +1943,189 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets", +] + +[[package]] +name = "windows-capture" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6001b777f61cafce437201de46a019ed7f4afed3b669f02e5ce4e0759164cb3e" +dependencies = [ + "clap", + "ctrlc", + "parking_lot", + "rayon", + "thiserror 1.0.69", + "windows 0.58.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -621,6 +2199,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +dependencies = [ + "memchr", +] + [[package]] name = "wyz" version = "0.5.1" @@ -630,6 +2217,15 @@ dependencies = [ "tap", ] +[[package]] +name = "yansi-term" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1" +dependencies = [ + "winapi", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -650,3 +2246,27 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index d9bd0fb..3aefe3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,8 @@ keywords = ["cccb", "cccb-servicepoint", "cli"] [dependencies] servicepoint = { version = "0.13.0", features = ["protocol_websocket"] } -clap = { version = "4.5", features = ["derive"]} +clap = { version = "4.5", features = ["derive"] } env_logger = "0.11" log = "0.4" +scap = "0.0.8" +image = "0.25.5" diff --git a/flake.nix b/flake.nix index b5ad692..8bf4958 100644 --- a/flake.nix +++ b/flake.nix @@ -1,5 +1,5 @@ { - description = "Flake for servicepoint-cli"; + description = "Flake for command line interface of the ServicePoint display."; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; @@ -55,12 +55,25 @@ }; nativeBuildInputs = with pkgs; [ pkg-config + libclang + rustPlatform.bindgenHook ]; strictDeps = true; - buildInputs = with pkgs; [ - xe - xz - ]; + buildInputs = + with pkgs; + [ + xe + xz + clang + ] + ++ lib.optionals pkgs.stdenv.isLinux ( + with pkgs; + [ + dbus + pipewire + libclang + ] + ); }; default = servicepoint-cli; diff --git a/src/cli.rs b/src/cli.rs index d59ca53..2d0bd39 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,5 @@ +use crate::stream_window::StreamScreenOptions; + #[derive(clap::Parser, std::fmt::Debug)] #[clap(version, arg_required_else_help = true)] pub struct Cli { @@ -38,8 +40,12 @@ pub enum Mode { }, StreamStdin { #[arg(long, short, default_value_t = false)] - slow: bool - } + slow: bool, + }, + StreamScreen { + #[command(flatten)] + options: StreamScreenOptions, + }, } #[derive(clap::Parser, std::fmt::Debug)] @@ -66,3 +72,4 @@ pub enum Protocol { WebSocket, Fake, } + diff --git a/src/execute.rs b/src/execute.rs index fa28cdd..90500f1 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -1,7 +1,8 @@ use crate::cli::{BrightnessCommand, Mode, PixelCommand}; +use crate::stream_stdin::stream_stdin; +use crate::stream_window::stream_window; use log::info; use servicepoint::{Brightness, Command, Connection}; -use crate::stream_stdin::stream_stdin; pub fn execute_mode(mode: Mode, connection: Connection) { match mode { @@ -11,7 +12,8 @@ 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::StreamStdin{slow} => stream_stdin(&connection, slow), + Mode::StreamStdin { slow } => stream_stdin(&connection, slow), + Mode::StreamScreen { options } => stream_window(&connection, options), } } diff --git a/src/main.rs b/src/main.rs index 76db66a..a9878ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use servicepoint::Connection; mod cli; mod execute; mod stream_stdin; +mod stream_window; fn main() { let cli = Cli::parse(); diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index 5deffb3..5949906 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -1,6 +1,6 @@ -use std::thread::sleep; use log::warn; use servicepoint::*; +use std::thread::sleep; pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { warn!("This mode will break when using multi-byte characters and does not support ANSI escape sequences yet."); @@ -8,7 +8,7 @@ pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { connection, mirror: CharGrid::new(TILE_WIDTH, TILE_HEIGHT), y: 0, - slow + slow, }; app.run() } diff --git a/src/stream_window.rs b/src/stream_window.rs new file mode 100644 index 0000000..f109c90 --- /dev/null +++ b/src/stream_window.rs @@ -0,0 +1,115 @@ +use image::{ + imageops::{dither, resize, BiLevel, FilterType}, + DynamicImage, ImageBuffer, Rgb, Rgba, +}; +use log::{debug, error, warn}; +use scap::{ + capturer::{Capturer, Options}, + frame::convert_bgra_to_rgb, + frame::Frame, +}; +use servicepoint::{ + Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, +}; +use std::ops::Div; +use std::time::Duration; + +#[derive(clap::Parser, std::fmt::Debug, Clone)] +pub struct StreamScreenOptions { + #[arg(long, short, default_value_t = true)] + pub dither: bool, +} + +pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { + let capturer = match start_capture() { + Some(value) => value, + None => return, + }; + + let mut bitmap = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); + loop { + let frame = capturer.get_next_frame().expect("failed to capture frame"); + let frame = frame_to_image(frame); + let frame = frame.grayscale().to_luma8(); + let mut frame = resize( + &frame, + PIXEL_WIDTH as u32, + PIXEL_HEIGHT as u32, + FilterType::Nearest, + ); + + if options.dither { + dither(&mut frame, &BiLevel); + } + + for (mut dest, src) in bitmap.iter_mut().zip(frame.pixels()) { + *dest = src.0[0] > u8::MAX / 2; + } + + connection + .send(Command::BitmapLinearWin( + Origin::ZERO, + bitmap.clone(), + CompressionCode::Uncompressed, + )) + .expect("failed to send frame to display"); + } +} + +fn start_capture() -> Option { + if !scap::is_supported() { + error!("platform not supported by scap"); + return None; + } + + if !scap::has_permission() { + warn!("requesting screen recording permission"); + if !scap::request_permission() { + error!("screen recording ermission denied"); + return None; + } + } + + let mut capturer = Capturer::build(Options { + fps: FRAME_PACING + .div_duration_f32(Duration::from_secs(1)) + .div(2f32) as u32, + target: None, + show_cursor: true, + show_highlight: true, + excluded_targets: None, + output_type: scap::frame::FrameType::BGR0, + ..Default::default() + }) + .expect("failed to create screen capture"); + capturer.start_capture(); + Some(capturer) +} + +fn frame_to_image(frame: Frame) -> DynamicImage { + match frame { + Frame::BGRx(frame) => bgrx_to_rgb(frame.width, frame.height, frame.data), + Frame::RGBx(frame) => DynamicImage::from( + ImageBuffer::, _>::from_raw( + frame.width as u32, + frame.height as u32, + frame.data, + ) + .unwrap(), + ), + Frame::BGR0(frame) => bgrx_to_rgb(frame.width, frame.height, frame.data), + Frame::RGB(frame) => DynamicImage::from( + ImageBuffer::, _>::from_raw(frame.width as u32, frame.height as u32, frame.data) + .unwrap(), + ), + Frame::BGRA(frame) => bgrx_to_rgb(frame.width, frame.height, frame.data), + Frame::YUVFrame(_) | Frame::XBGR(_) => panic!("unsupported frame format"), + } +} + +fn bgrx_to_rgb(width: i32, height: i32, data: Vec) -> DynamicImage { + DynamicImage::from( + ImageBuffer::, _>::from_raw(width as u32, height as u32, convert_bgra_to_rgb(data)) + .unwrap(), + ) +} From 83baf7b419e2e0300dc9a6a27ead1abe316e5441 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 12 Feb 2025 20:46:34 +0100 Subject: [PATCH 03/36] improve cli --- Cargo.toml | 2 +- README.md | 64 +++++++++++++++++++++++++++++++++++++++++++- src/cli.rs | 24 +++++++++++------ src/execute.rs | 8 +++--- src/stream_window.rs | 13 ++++----- 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3aefe3c..7d16ac6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "servicepoint-cli" description = "A command line interface for the ServicePoint display." version = "0.1.0" edition = "2021" -rust-version = "1.78.0" +rust-version = "1.80.0" publish = true resolver = "2" readme = "README.md" diff --git a/README.md b/README.md index f16d4ab..1a8f88b 100644 --- a/README.md +++ b/README.md @@ -31,13 +31,75 @@ cd servicepoint-cli cargo run -- ``` +## Usage + +``` +Usage: servicepoint-cli [OPTIONS] + +Commands: + reset-everything [aliases: r] + pixels [aliases: p] + brightness [aliases: b] + stream [aliases: s] + 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] + -t, --transport protocol to use for communication with display [default: udp] [possible values: udp, web-socket, fake] + -v, --verbose verbose logging + -h, --help Print help + -V, --version Print version +``` + +### Stream + +``` +Usage: servicepoint-cli stream + +Commands: + stdin + screen + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + +### Brightness + +``` +Usage: servicepoint-cli brightness + +Commands: + reset [aliases: r] + set [aliases: s] + min + max + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + +### Pixels +``` +Usage: servicepoint-cli pixels + +Commands: + reset [aliases: r] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help +``` + ## Contributing If you have ideas on how to improve the code, add features or improve documentation feel free to open a pull request. You think you found a bug? Please open an issue. -Submissions on Forgejo are preferred, but you can also use GitHub. +Submissions on [Forgejo](https://git.berlin.ccc.de/servicepoint/servicepoint-cli) are preferred, but you can also use [GitHub](https://github.com/kaesaecracker/servicepoint-cli). All creatures welcome. diff --git a/src/cli.rs b/src/cli.rs index 2d0bd39..afa61f1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -38,14 +38,11 @@ pub enum Mode { #[clap(subcommand)] brightness_command: BrightnessCommand, }, - StreamStdin { - #[arg(long, short, default_value_t = false)] - slow: bool, - }, - StreamScreen { - #[command(flatten)] - options: StreamScreenOptions, - }, + #[command(visible_alias = "s")] + Stream { + #[clap(subcommand)] + stream_command: StreamCommand, + } } #[derive(clap::Parser, std::fmt::Debug)] @@ -73,3 +70,14 @@ pub enum Protocol { Fake, } +#[derive(clap::Parser, std::fmt::Debug)] +pub enum StreamCommand { + Stdin { + #[arg(long, short, default_value_t = false)] + slow: bool, + }, + Screen { + #[command(flatten)] + options: StreamScreenOptions, + }, +} diff --git a/src/execute.rs b/src/execute.rs index 90500f1..74f7db4 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -1,4 +1,4 @@ -use crate::cli::{BrightnessCommand, Mode, PixelCommand}; +use crate::cli::{BrightnessCommand, Mode, PixelCommand, StreamCommand}; use crate::stream_stdin::stream_stdin; use crate::stream_window::stream_window; use log::info; @@ -12,8 +12,10 @@ 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::StreamStdin { slow } => stream_stdin(&connection, slow), - Mode::StreamScreen { options } => stream_window(&connection, options), + Mode::Stream { stream_command } => match stream_command { + StreamCommand::Stdin { slow } => stream_stdin(&connection, slow), + StreamCommand::Screen { options } => stream_window(&connection, options), + }, } } diff --git a/src/stream_window.rs b/src/stream_window.rs index f109c90..a528281 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -2,7 +2,7 @@ use image::{ imageops::{dither, resize, BiLevel, FilterType}, DynamicImage, ImageBuffer, Rgb, Rgba, }; -use log::{debug, error, warn}; +use log::{error, warn}; use scap::{ capturer::{Capturer, Options}, frame::convert_bgra_to_rgb, @@ -11,13 +11,12 @@ use scap::{ use servicepoint::{ Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, }; -use std::ops::Div; use std::time::Duration; #[derive(clap::Parser, std::fmt::Debug, Clone)] pub struct StreamScreenOptions { - #[arg(long, short, default_value_t = true)] - pub dither: bool, + #[arg(long, short, default_value_t = false)] + pub no_dither: bool, } pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { @@ -38,7 +37,7 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { FilterType::Nearest, ); - if options.dither { + if !options.no_dither { dither(&mut frame, &BiLevel); } @@ -71,9 +70,7 @@ fn start_capture() -> Option { } let mut capturer = Capturer::build(Options { - fps: FRAME_PACING - .div_duration_f32(Duration::from_secs(1)) - .div(2f32) as u32, + fps: FRAME_PACING.div_duration_f32(Duration::from_secs(1)) as u32, target: None, show_cursor: true, show_highlight: true, From b770607893d1654daba5803d30e6b5d930215b80 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 12 Feb 2025 22:02:17 +0100 Subject: [PATCH 04/36] add invert, more help text, ... choose if pointer is visible to make dithering more stable --- README.md | 44 ++++++++++++++++++++++----------- src/cli.rs | 59 +++++++++++++++++++++++++++++++++++--------- src/execute.rs | 23 ++++++++++++++--- src/stream_window.rs | 58 +++++++++++++++++++++---------------------- 4 files changed, 125 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 1a8f88b..439cf6d 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,29 @@ Options: Usage: servicepoint-cli stream Commands: - stdin - screen + stdin Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` + screen Stream the default 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. help Print this message or the help of the given subcommand(s) +``` + +#### Screen + +``` +Usage: servicepoint-cli stream screen [OPTIONS] Options: + -n, --no-dither Disable dithering + -p, --pointer Show mouse pointer in video feed + -h, --help Print help +``` + +#### Stdin + +``` +Usage: servicepoint-cli stream stdin [OPTIONS] + +Options: + -s, --slow -h, --help Print help ``` @@ -71,26 +89,22 @@ Options: Usage: servicepoint-cli brightness Commands: - reset [aliases: r] - set [aliases: s] - min - max - help Print this message or the help of the given subcommand(s) - -Options: - -h, --help Print help + max Reset brightness to the default (max) level [aliases: r, reset] + set Set one brightness for the whole screen [aliases: s] + min Set brightness to lowest possible level. + help Print this message or the help of the given subcommand(s) ``` ### Pixels + ``` Usage: servicepoint-cli pixels Commands: - reset [aliases: r] - help Print this message or the help of the given subcommand(s) - -Options: - -h, --help Print help + off Reset all pixels to the default (off) state [aliases: r, reset] + invert Invert the state of all pixels [aliases: i] + on Set all pixels to the on state + help Print this message or the help of the given subcommand(s) ``` ## Contributing diff --git a/src/cli.rs b/src/cli.rs index afa61f1..fa420d6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,9 @@ -use crate::stream_window::StreamScreenOptions; - #[derive(clap::Parser, std::fmt::Debug)] -#[clap(version, arg_required_else_help = true)] +#[clap( + version, + arg_required_else_help = true, + about = "A command line interface for the ServicePoint display." +)] pub struct Cli { #[arg( short, @@ -26,7 +28,7 @@ pub struct Cli { #[derive(clap::Parser, std::fmt::Debug)] pub enum Mode { - #[command(visible_alias = "r")] + #[command(visible_alias = "r", about = "Reset both pixels and brightness")] ResetEverything, #[command(visible_alias = "p")] Pixels { @@ -42,25 +44,40 @@ pub enum Mode { Stream { #[clap(subcommand)] stream_command: StreamCommand, - } + }, } #[derive(clap::Parser, std::fmt::Debug)] +#[clap(about = "Commands for manipulating pixels")] pub enum PixelCommand { - #[command(visible_alias = "r")] - Reset, + #[command( + visible_alias = "r", + visible_alias = "reset", + about = "Reset all pixels to the default (off) state" + )] + Off, + #[command(visible_alias = "i", about = "Invert the state of all pixels")] + Invert, + #[command(about = "Set all pixels to the on state")] + On, } #[derive(clap::Parser, std::fmt::Debug)] +#[clap(about = "Commands for manipulating the brightness")] pub enum BrightnessCommand { - #[command(visible_alias = "r")] - Reset, - #[command(visible_alias = "s")] + #[command( + visible_alias = "r", + visible_alias = "reset", + about = "Reset brightness to the default (max) level" + )] + Max, + #[command(visible_alias = "s", about = "Set one brightness for the whole screen")] Set { + #[arg()] brightness: u8, }, + #[command(about = "Set brightness to lowest possible level.")] Min, - Max, } #[derive(clap::ValueEnum, Clone, Debug)] @@ -71,13 +88,33 @@ pub enum Protocol { } #[derive(clap::Parser, std::fmt::Debug)] +#[clap(about = "Continuously send data to the display")] pub enum StreamCommand { + #[clap( + about = "Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin`" + )] Stdin { #[arg(long, short, default_value_t = false)] slow: bool, }, + #[clap(about = "Stream the default 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.")] Screen { #[command(flatten)] options: StreamScreenOptions, }, } + +#[derive(clap::Parser, std::fmt::Debug, Clone)] +pub struct StreamScreenOptions { + #[arg(long, short, default_value_t = false, help = "Disable dithering")] + pub no_dither: bool, + + #[arg( + long, + short, + default_value_t = false, + help = "Show mouse pointer in video feed" + )] + pub pointer: bool, +} diff --git a/src/execute.rs b/src/execute.rs index 74f7db4..9859d3f 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -2,7 +2,7 @@ use crate::cli::{BrightnessCommand, Mode, PixelCommand, StreamCommand}; use crate::stream_stdin::stream_stdin; use crate::stream_window::stream_window; use log::info; -use servicepoint::{Brightness, Command, Connection}; +use servicepoint::{BitVec, Brightness, Command, CompressionCode, Connection, PIXEL_COUNT}; pub fn execute_mode(mode: Mode, connection: Connection) { match mode { @@ -21,15 +21,30 @@ pub fn execute_mode(mode: Mode, connection: Connection) { fn pixels(connection: &Connection, pixel_command: PixelCommand) { match pixel_command { - PixelCommand::Reset => pixels_reset(connection), + PixelCommand::Off => pixels_reset(connection), + PixelCommand::Invert => pixels_invert(connection), + PixelCommand::On => pixels_on(connection) } } +fn pixels_on(connection: &Connection) { + let mask = BitVec::repeat(true, PIXEL_COUNT); + connection + .send(Command::BitmapLinearXor(0, mask, CompressionCode::Lzma)) + .expect("could not send command") +} + +fn pixels_invert(connection: &Connection) { + let mask = BitVec::repeat(true, PIXEL_COUNT); + connection + .send(Command::BitmapLinearXor(0, mask, CompressionCode::Lzma)) + .expect("could not send command") +} + fn brightness(connection: &Connection, brightness_command: BrightnessCommand) { match brightness_command { - BrightnessCommand::Reset => brightness_reset(connection), + BrightnessCommand::Max => brightness_reset(connection), BrightnessCommand::Min => brightness_set(connection, Brightness::MIN), - BrightnessCommand::Max => brightness_set(connection, Brightness::MAX), BrightnessCommand::Set { brightness } => { brightness_set(connection, Brightness::saturating_from(brightness)) } diff --git a/src/stream_window.rs b/src/stream_window.rs index a528281..e3a626a 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,8 +1,9 @@ +use crate::cli::StreamScreenOptions; use image::{ imageops::{dither, resize, BiLevel, FilterType}, - DynamicImage, ImageBuffer, Rgb, Rgba, + DynamicImage, ImageBuffer, Luma, Rgb, Rgba, }; -use log::{error, warn}; +use log::{error, info, warn}; use scap::{ capturer::{Capturer, Options}, frame::convert_bgra_to_rgb, @@ -13,34 +14,19 @@ use servicepoint::{ }; use std::time::Duration; -#[derive(clap::Parser, std::fmt::Debug, Clone)] -pub struct StreamScreenOptions { - #[arg(long, short, default_value_t = false)] - pub no_dither: bool, -} - pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { - let capturer = match start_capture() { + info!("Starting capture with options: {:?}", options); + warn!("this implementation does not drop any frames - set a lower fps or disable dithering if your computer cannot keep up."); + + let capturer = match start_capture(&options) { Some(value) => value, None => return, }; let mut bitmap = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); + info!("now starting to stream images"); loop { - let frame = capturer.get_next_frame().expect("failed to capture frame"); - let frame = frame_to_image(frame); - let frame = frame.grayscale().to_luma8(); - let mut frame = resize( - &frame, - PIXEL_WIDTH as u32, - PIXEL_HEIGHT as u32, - FilterType::Nearest, - ); - - if !options.no_dither { - dither(&mut frame, &BiLevel); - } - + let frame = get_next_frame(&capturer, options.no_dither); for (mut dest, src) in bitmap.iter_mut().zip(frame.pixels()) { *dest = src.0[0] > u8::MAX / 2; } @@ -55,7 +41,24 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { } } -fn start_capture() -> Option { +fn get_next_frame(capturer: &Capturer, no_dither: bool) -> ImageBuffer, Vec> { + let frame = capturer.get_next_frame().expect("failed to capture frame"); + let frame = frame_to_image(frame); + let frame = frame.grayscale().to_luma8(); + let mut frame = resize( + &frame, + PIXEL_WIDTH as u32, + PIXEL_HEIGHT as u32, + FilterType::Nearest, + ); + + if !no_dither { + dither(&mut frame, &BiLevel); + } + frame +} + +fn start_capture(options: &StreamScreenOptions) -> Option { if !scap::is_supported() { error!("platform not supported by scap"); return None; @@ -71,11 +74,8 @@ fn start_capture() -> Option { let mut capturer = Capturer::build(Options { fps: FRAME_PACING.div_duration_f32(Duration::from_secs(1)) as u32, - target: None, - show_cursor: true, - show_highlight: true, - excluded_targets: None, - output_type: scap::frame::FrameType::BGR0, + show_cursor: options.pointer, + output_type: scap::frame::FrameType::BGR0, // this is more like a suggestion ..Default::default() }) .expect("failed to create screen capture"); From eb3c441f0b27d9a77f5b6faa7213483826c6adf0 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Wed, 12 Feb 2025 22:03:15 +0100 Subject: [PATCH 05/36] version 0.2.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d91bde5..6097160 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1643,7 +1643,7 @@ dependencies = [ [[package]] name = "servicepoint-cli" -version = "0.1.0" +version = "0.2.0" dependencies = [ "clap", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index 7d16ac6..25daf24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "servicepoint-cli" description = "A command line interface for the ServicePoint display." -version = "0.1.0" +version = "0.2.0" edition = "2021" rust-version = "1.80.0" publish = true From 7c79468eda856e04b12304571c67895f243a3acb Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 13 Feb 2025 18:38:07 +0100 Subject: [PATCH 06/36] install libpipewire, libdbus and libclang in CI --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 62304ee..e944336 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,7 +26,7 @@ jobs: - name: Install rust toolchain run: sudo apt-get install -qy cargo-1.80 rust-1.80-clippy - name: Install system dependencies - run: sudo apt-get install -qy liblzma-dev + run: sudo apt-get install -qy liblzma-dev libpipewire-0.3-dev libclang-dev libdbus-1-dev - name: Run Clippy run: cargo clippy --all-targets --all-features From 542bd19acef86df66b22ffe6c5a0c52d50c9e32f Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 13 Feb 2025 19:14:46 +0100 Subject: [PATCH 07/36] fix warning --- src/execute.rs | 2 +- src/stream_stdin.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/execute.rs b/src/execute.rs index 9859d3f..ad91eba 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -13,7 +13,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::Stream { stream_command } => match stream_command { - StreamCommand::Stdin { slow } => stream_stdin(&connection, slow), + StreamCommand::Stdin { slow } => stream_stdin(connection, slow), StreamCommand::Screen { options } => stream_window(&connection, options), }, } diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index 5949906..a349aa2 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -2,7 +2,7 @@ use log::warn; use servicepoint::*; use std::thread::sleep; -pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { +pub(crate) fn stream_stdin(connection: Connection, 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, @@ -13,14 +13,14 @@ pub(crate) fn stream_stdin(connection: &Connection, slow: bool) { app.run() } -struct App<'t> { - connection: &'t Connection, +struct App { + connection: Connection, mirror: CharGrid, y: usize, slow: bool, } -impl<'t> App<'t> { +impl App { fn run(&mut self) { self.connection .send(Command::Clear) From 6c7250b41e93dddb4b5351c35155d3ec4c0ecb80 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 17 Feb 2025 22:54:12 +0100 Subject: [PATCH 08/36] update servicepoint to 0.13.2 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6097160..547fa91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1629,9 +1629,9 @@ dependencies = [ [[package]] name = "servicepoint" -version = "0.13.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8bc9e40503ece07e3f12232f648484191323b8126e74abce3d1644ba04dbd0" +checksum = "33abd53582a995aaf5d387be4a1f7eb294a084185f88f8cf61652b6272041660" dependencies = [ "bitvec", "log", diff --git a/Cargo.toml b/Cargo.toml index 25daf24..0ae04d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ homepage = "https://crates.io/crates/servicepoint-cli" keywords = ["cccb", "cccb-servicepoint", "cli"] [dependencies] -servicepoint = { version = "0.13.0", features = ["protocol_websocket"] } +servicepoint = { version = "0.13.2", features = ["protocol_websocket"] } clap = { version = "4.5", features = ["derive"] } env_logger = "0.11" log = "0.4" From e902d8701b4102b4bf57a5783f05736185cf4584 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 17 Feb 2025 22:55:52 +0100 Subject: [PATCH 09/36] update dependencies --- Cargo.lock | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 547fa91..88060ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,9 +215,9 @@ dependencies = [ [[package]] name = "built" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73848a43c5d63a1251d17adf6c2bf78aa94830e60a335a95eeea45d6ba9e1e4d" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" [[package]] name = "bumpalo" @@ -245,15 +245,15 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cc" -version = "1.2.13" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "jobserver", "libc", @@ -304,9 +304,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.28" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e77c3243bd94243c03672cb5154667347c457ca271254724f9f393aee1c05ff" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" dependencies = [ "clap_builder", "clap_derive", @@ -314,9 +314,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.27" +version = "4.5.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" dependencies = [ "anstream", "anstyle", @@ -457,9 +457,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -526,9 +526,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" [[package]] name = "dbus" @@ -588,9 +588,9 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "exr" @@ -826,9 +826,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.5" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +checksum = "f2d708df4e7140240a16cd6ab0ab65c972d7433ab77819ea693fde9c43811e2a" [[package]] name = "humantime" @@ -1696,9 +1696,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" [[package]] name = "strsim" From dc45c003b3a8407078cba83ba44bb33861d47fd0 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 17 Feb 2025 22:57:14 +0100 Subject: [PATCH 10/36] version v0.2.1 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88060ab..31e39ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1643,7 +1643,7 @@ dependencies = [ [[package]] name = "servicepoint-cli" -version = "0.2.0" +version = "0.2.1" dependencies = [ "clap", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index 0ae04d7..8076574 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "servicepoint-cli" description = "A command line interface for the ServicePoint display." -version = "0.2.0" +version = "0.2.1" edition = "2021" rust-version = "1.80.0" publish = true From 304317a86ed3a43ee03cae48e1d41edd653a3e0e Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 13 Feb 2025 20:05:28 +0100 Subject: [PATCH 11/36] fix pixels on inverts --- src/cli.rs | 12 +++++++++--- src/execute.rs | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index fa420d6..3ef2286 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -94,11 +94,17 @@ pub enum StreamCommand { about = "Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin`" )] Stdin { - #[arg(long, short, default_value_t = false)] + #[arg( + long, + short, + default_value_t = false, + help = "Wait for a short amount of time before sending the next line" + )] slow: bool, }, #[clap(about = "Stream the default 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.")] + On Linux Wayland, this pops up a screen or window chooser, \ + but it also may directly start streaming your main screen.")] Screen { #[command(flatten)] options: StreamScreenOptions, @@ -107,7 +113,7 @@ pub enum StreamCommand { #[derive(clap::Parser, std::fmt::Debug, Clone)] pub struct StreamScreenOptions { - #[arg(long, short, default_value_t = false, help = "Disable dithering")] + #[arg(long, short, default_value_t = false, help = "Disable dithering - improves performance")] pub no_dither: bool, #[arg( diff --git a/src/execute.rs b/src/execute.rs index ad91eba..17ac5df 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -23,14 +23,14 @@ fn pixels(connection: &Connection, pixel_command: PixelCommand) { match pixel_command { PixelCommand::Off => pixels_reset(connection), PixelCommand::Invert => pixels_invert(connection), - PixelCommand::On => pixels_on(connection) + PixelCommand::On => pixels_on(connection), } } fn pixels_on(connection: &Connection) { let mask = BitVec::repeat(true, PIXEL_COUNT); connection - .send(Command::BitmapLinearXor(0, mask, CompressionCode::Lzma)) + .send(Command::BitmapLinear(0, mask, CompressionCode::Lzma)) .expect("could not send command") } From 2dcf092100003c60b99a7839f1fa7704a0cfa0eb Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 13 Feb 2025 20:14:29 +0100 Subject: [PATCH 12/36] split execute.rs --- src/brightness.rs | 20 +++++++++++++ src/cli.rs | 1 + src/execute.rs | 73 ----------------------------------------------- src/main.rs | 30 ++++++++++++++++--- src/pixels.rs | 34 ++++++++++++++++++++++ 5 files changed, 81 insertions(+), 77 deletions(-) create mode 100644 src/brightness.rs delete mode 100644 src/execute.rs create mode 100644 src/pixels.rs diff --git a/src/brightness.rs b/src/brightness.rs new file mode 100644 index 0000000..09b7e55 --- /dev/null +++ b/src/brightness.rs @@ -0,0 +1,20 @@ +use servicepoint::{Brightness, Command, Connection}; +use log::info; +use crate::cli::BrightnessCommand; + +pub(crate) fn brightness(connection: &Connection, brightness_command: BrightnessCommand) { + match brightness_command { + BrightnessCommand::Max => brightness_set(connection, Brightness::MAX), + BrightnessCommand::Min => brightness_set(connection, Brightness::MIN), + BrightnessCommand::Set { brightness } => { + brightness_set(connection, Brightness::saturating_from(brightness)) + } + } +} + +pub(crate) fn brightness_set(connection: &Connection, brightness: Brightness) { + connection + .send(Command::Brightness(brightness)) + .expect("Failed to set brightness"); + info!("set brightness to {brightness:?}"); +} \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 3ef2286..8f205f1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -53,6 +53,7 @@ pub enum PixelCommand { #[command( visible_alias = "r", visible_alias = "reset", + visible_alias = "clear", about = "Reset all pixels to the default (off) state" )] Off, diff --git a/src/execute.rs b/src/execute.rs deleted file mode 100644 index 17ac5df..0000000 --- a/src/execute.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::cli::{BrightnessCommand, Mode, PixelCommand, StreamCommand}; -use crate::stream_stdin::stream_stdin; -use crate::stream_window::stream_window; -use log::info; -use servicepoint::{BitVec, Brightness, Command, CompressionCode, Connection, PIXEL_COUNT}; - -pub fn execute_mode(mode: Mode, connection: Connection) { - match mode { - Mode::ResetEverything => { - brightness_reset(&connection); - pixels_reset(&connection); - } - Mode::Pixels { pixel_command } => pixels(&connection, pixel_command), - Mode::Brightness { brightness_command } => brightness(&connection, brightness_command), - Mode::Stream { stream_command } => match stream_command { - StreamCommand::Stdin { slow } => stream_stdin(connection, slow), - StreamCommand::Screen { options } => stream_window(&connection, options), - }, - } -} - -fn pixels(connection: &Connection, pixel_command: PixelCommand) { - match pixel_command { - PixelCommand::Off => pixels_reset(connection), - PixelCommand::Invert => pixels_invert(connection), - PixelCommand::On => pixels_on(connection), - } -} - -fn pixels_on(connection: &Connection) { - let mask = BitVec::repeat(true, PIXEL_COUNT); - connection - .send(Command::BitmapLinear(0, mask, CompressionCode::Lzma)) - .expect("could not send command") -} - -fn pixels_invert(connection: &Connection) { - let mask = BitVec::repeat(true, PIXEL_COUNT); - connection - .send(Command::BitmapLinearXor(0, mask, CompressionCode::Lzma)) - .expect("could not send command") -} - -fn brightness(connection: &Connection, brightness_command: BrightnessCommand) { - match brightness_command { - BrightnessCommand::Max => brightness_reset(connection), - BrightnessCommand::Min => brightness_set(connection, Brightness::MIN), - BrightnessCommand::Set { brightness } => { - brightness_set(connection, Brightness::saturating_from(brightness)) - } - } -} - -fn pixels_reset(connection: &Connection) { - connection - .send(Command::Clear) - .expect("failed to clear pixels"); - info!("Reset pixels"); -} - -fn brightness_reset(connection: &Connection) { - connection - .send(Command::Brightness(Brightness::MAX)) - .expect("Failed to reset brightness to maximum"); - info!("Reset brightness"); -} - -fn brightness_set(connection: &Connection, brightness: Brightness) { - connection - .send(Command::Brightness(brightness)) - .expect("Failed to set brightness"); - info!("set brightness to {brightness:?}"); -} diff --git a/src/main.rs b/src/main.rs index a9878ca..bbdd20a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,17 @@ -use crate::cli::{Cli, Protocol}; +use crate::{ + brightness::{brightness, brightness_set}, + cli::{Cli, Mode, Protocol, StreamCommand}, + pixels::{pixels, pixels_off}, + stream_stdin::stream_stdin, + stream_window::stream_window, +}; use clap::Parser; use log::debug; -use servicepoint::Connection; +use servicepoint::{Brightness, Connection}; +mod brightness; mod cli; -mod execute; +mod pixels; mod stream_stdin; mod stream_window; @@ -16,7 +23,22 @@ fn main() { let connection = make_connection(cli.destination, cli.transport); debug!("connection established: {:#?}", connection); - execute::execute_mode(cli.command, connection); + execute_mode(cli.command, connection); +} + +pub fn execute_mode(mode: Mode, connection: Connection) { + match mode { + Mode::ResetEverything => { + brightness_set(&connection, Brightness::MAX); + pixels_off(&connection); + } + Mode::Pixels { pixel_command } => pixels(&connection, pixel_command), + Mode::Brightness { brightness_command } => brightness(&connection, brightness_command), + Mode::Stream { stream_command } => match stream_command { + StreamCommand::Stdin { slow } => stream_stdin(connection, slow), + StreamCommand::Screen { options } => stream_window(&connection, options), + }, + } } fn make_connection(destination: String, transport: Protocol) -> Connection { diff --git a/src/pixels.rs b/src/pixels.rs new file mode 100644 index 0000000..0e4597e --- /dev/null +++ b/src/pixels.rs @@ -0,0 +1,34 @@ +use servicepoint::{BitVec, Command, CompressionCode, Connection, PIXEL_COUNT}; +use log::info; +use crate::cli::PixelCommand; + +pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { + match pixel_command { + PixelCommand::Off => pixels_off(connection), + PixelCommand::Invert => pixels_invert(connection), + PixelCommand::On => pixels_on(connection), + } +} + +fn pixels_on(connection: &Connection) { + let mask = BitVec::repeat(true, PIXEL_COUNT); + connection + .send(Command::BitmapLinear(0, mask, CompressionCode::Lzma)) + .expect("could not send command"); + info!("turned on all pixels") +} + +fn pixels_invert(connection: &Connection) { + let mask = BitVec::repeat(true, PIXEL_COUNT); + connection + .send(Command::BitmapLinearXor(0, mask, CompressionCode::Lzma)) + .expect("could not send command"); + info!("inverted all pixels"); +} + +pub(crate) fn pixels_off(connection: &Connection) { + connection + .send(Command::Clear) + .expect("failed to clear pixels"); + info!("reset pixels"); +} \ No newline at end of file From 1de6caa8a7b09fcec35adc59a7c72a68aa15aec9 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Fri, 28 Feb 2025 10:48:03 +0100 Subject: [PATCH 13/36] implement histogram correction from CCCB_Ledwand --- src/brightness.rs | 6 +- src/cli.rs | 7 ++- src/ledwand_dither.rs | 127 ++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/pixels.rs | 6 +- src/stream_stdin.rs | 1 + src/stream_window.rs | 27 +++++---- 7 files changed, 158 insertions(+), 17 deletions(-) create mode 100644 src/ledwand_dither.rs diff --git a/src/brightness.rs b/src/brightness.rs index 09b7e55..adead44 100644 --- a/src/brightness.rs +++ b/src/brightness.rs @@ -1,6 +1,6 @@ -use servicepoint::{Brightness, Command, Connection}; -use log::info; use crate::cli::BrightnessCommand; +use log::info; +use servicepoint::{Brightness, Command, Connection}; pub(crate) fn brightness(connection: &Connection, brightness_command: BrightnessCommand) { match brightness_command { @@ -17,4 +17,4 @@ pub(crate) fn brightness_set(connection: &Connection, brightness: Brightness) { .send(Command::Brightness(brightness)) .expect("Failed to set brightness"); info!("set brightness to {brightness:?}"); -} \ No newline at end of file +} diff --git a/src/cli.rs b/src/cli.rs index 8f205f1..12a478e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -114,7 +114,12 @@ pub enum StreamCommand { #[derive(clap::Parser, std::fmt::Debug, Clone)] pub struct StreamScreenOptions { - #[arg(long, short, default_value_t = false, help = "Disable dithering - improves performance")] + #[arg( + long, + short, + default_value_t = false, + help = "Disable dithering - improves performance" + )] pub no_dither: bool, #[arg( diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs new file mode 100644 index 0000000..c235a95 --- /dev/null +++ b/src/ledwand_dither.rs @@ -0,0 +1,127 @@ +//! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand + +use image::GrayImage; +use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH}; + +pub struct LedwandDither { + options: LedwandDitherOptions, + tmpbuf: GrayImage, +} + +#[derive(Debug, Default)] +pub struct LedwandDitherOptions { + pub size: Option<(u32, u32)>, +} + +type GrayHistogram = [usize; 256]; + +struct HistogramCorrection { + pre_offset: f32, + post_offset: f32, + factor: f32, +} + +impl LedwandDither { + pub fn new(options: LedwandDitherOptions) -> Self { + let (width, height) = options + .size + .unwrap_or((PIXEL_WIDTH as u32, PIXEL_HEIGHT as u32)); + Self { + tmpbuf: GrayImage::new(width, height), + options, + } + } + + pub fn histogram_correction(image: &mut GrayImage) { + let histogram = Self::make_histogram(image); + let correction = Self::determine_histogram_correction(image, histogram); + Self::apply_histogram_correction(image, correction) + } + + fn make_histogram(image: &GrayImage) -> GrayHistogram { + let mut histogram = [0; 256]; + for pixel in image.pixels() { + histogram[pixel.0[0] as usize] += 1; + } + histogram + } + + fn determine_histogram_correction( + image: &GrayImage, + histogram: GrayHistogram, + ) -> HistogramCorrection { + let adjustment_pixels = image.len() / 100; + + let mut num_pixels = 0; + let mut brightness = 0; + + let mincut = loop { + num_pixels += histogram[brightness as usize] as usize; + brightness += 1; + if num_pixels >= adjustment_pixels { + break u8::min(brightness, 20); + } + }; + + let minshift = loop { + num_pixels += histogram[brightness as usize] as usize; + brightness += 1; + if num_pixels >= 2 * adjustment_pixels { + break u8::min(brightness, 64); + } + }; + + brightness = u8::MAX; + num_pixels = 0; + let maxshift = loop { + num_pixels += histogram[brightness as usize] as usize; + brightness -= 1; + if num_pixels >= 2 * adjustment_pixels { + break u8::max(brightness, 192); + } + }; + + let pre_offset = -(mincut as f32 / 2.); + let post_offset = -(minshift as f32); + let factor = (255.0 - post_offset) / maxshift as f32; + HistogramCorrection { + pre_offset, + post_offset, + factor, + } + } + + fn apply_histogram_correction(image: &mut GrayImage, correction: HistogramCorrection) { + let midpoint = image.width() / 2; + for (x, _, pixel) in image.enumerate_pixels_mut() { + if x > midpoint { + continue; + } + + let pixel = &mut pixel.0[0]; + let value = (*pixel as f32 + correction.pre_offset) * correction.factor + + correction.post_offset; + *pixel = value.clamp(0f32, u8::MAX as f32) as u8; + } + } + + pub fn median_brightness(image: &GrayImage) -> u8 { + let histogram = Self::make_histogram(image); + let midpoint = image.len() / 2; + + debug_assert_eq!( + image.len(), + histogram.iter().copied().map(usize::from).sum() + ); + + let mut num_pixels = 0; + for brightness in u8::MIN..=u8::MAX { + num_pixels += histogram[brightness as usize] as usize; + if num_pixels >= midpoint { + return brightness; + } + } + + unreachable!("Somehow less pixels where counted in the histogram than exist in the image") + } +} diff --git a/src/main.rs b/src/main.rs index bbdd20a..cd7937c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use servicepoint::{Brightness, Connection}; mod brightness; mod cli; +mod ledwand_dither; mod pixels; mod stream_stdin; mod stream_window; diff --git a/src/pixels.rs b/src/pixels.rs index 0e4597e..4e6e3cf 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -1,6 +1,6 @@ -use servicepoint::{BitVec, Command, CompressionCode, Connection, PIXEL_COUNT}; -use log::info; use crate::cli::PixelCommand; +use log::info; +use servicepoint::{BitVec, Command, CompressionCode, Connection, PIXEL_COUNT}; pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { match pixel_command { @@ -31,4 +31,4 @@ pub(crate) fn pixels_off(connection: &Connection) { .send(Command::Clear) .expect("failed to clear pixels"); info!("reset pixels"); -} \ No newline at end of file +} diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index a349aa2..82109ba 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -72,6 +72,7 @@ impl App { fn single_line(&mut self, line: &str) { let mut line_grid = CharGrid::new(TILE_WIDTH, 1); + line_grid.fill(' '); Self::line_onto_grid(&mut line_grid, 0, line); Self::line_onto_grid(&mut self.mirror, self.y, line); self.connection diff --git a/src/stream_window.rs b/src/stream_window.rs index e3a626a..db2b0bd 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,4 +1,5 @@ use crate::cli::StreamScreenOptions; +use crate::ledwand_dither::{LedwandDither, LedwandDitherOptions}; use image::{ imageops::{dither, resize, BiLevel, FilterType}, DynamicImage, ImageBuffer, Luma, Rgb, Rgba, @@ -26,9 +27,18 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { let mut bitmap = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); info!("now starting to stream images"); loop { - let frame = get_next_frame(&capturer, options.no_dither); + let mut frame = get_next_frame(&capturer); + + LedwandDither::histogram_correction(&mut frame); + let cutoff = if options.no_dither { + LedwandDither::median_brightness(&frame) + } else { + dither(&mut frame, &BiLevel); + u8::MAX / 2 + }; + for (mut dest, src) in bitmap.iter_mut().zip(frame.pixels()) { - *dest = src.0[0] > u8::MAX / 2; + *dest = src.0[0] > cutoff; } connection @@ -41,21 +51,18 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { } } -fn get_next_frame(capturer: &Capturer, no_dither: bool) -> ImageBuffer, Vec> { +/// returns next frame from the capturer, resized and grayscale +fn get_next_frame(capturer: &Capturer) -> ImageBuffer, Vec> { let frame = capturer.get_next_frame().expect("failed to capture frame"); let frame = frame_to_image(frame); let frame = frame.grayscale().to_luma8(); - let mut frame = resize( + + resize( &frame, PIXEL_WIDTH as u32, PIXEL_HEIGHT as u32, FilterType::Nearest, - ); - - if !no_dither { - dither(&mut frame, &BiLevel); - } - frame + ) } fn start_capture(options: &StreamScreenOptions) -> Option { From ea7262f8f534b9654ebfb4968ea876a90b6ace05 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Fri, 28 Feb 2025 11:58:04 +0100 Subject: [PATCH 14/36] implement blur --- src/ledwand_dither.rs | 57 ++++++++++++++++++++++++++++++++++--------- src/stream_window.rs | 3 ++- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index c235a95..ce92b03 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -1,6 +1,6 @@ //! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand -use image::GrayImage; +use image::{GenericImage, GrayImage}; use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH}; pub struct LedwandDither { @@ -50,13 +50,13 @@ impl LedwandDither { image: &GrayImage, histogram: GrayHistogram, ) -> HistogramCorrection { - let adjustment_pixels = image.len() / 100; + let adjustment_pixels = image.len() / PIXEL_HEIGHT; let mut num_pixels = 0; let mut brightness = 0; let mincut = loop { - num_pixels += histogram[brightness as usize] as usize; + num_pixels += histogram[brightness as usize]; brightness += 1; if num_pixels >= adjustment_pixels { break u8::min(brightness, 20); @@ -64,7 +64,7 @@ impl LedwandDither { }; let minshift = loop { - num_pixels += histogram[brightness as usize] as usize; + num_pixels += histogram[brightness as usize]; brightness += 1; if num_pixels >= 2 * adjustment_pixels { break u8::min(brightness, 64); @@ -74,7 +74,7 @@ impl LedwandDither { brightness = u8::MAX; num_pixels = 0; let maxshift = loop { - num_pixels += histogram[brightness as usize] as usize; + num_pixels += histogram[brightness as usize]; brightness -= 1; if num_pixels >= 2 * adjustment_pixels { break u8::max(brightness, 192); @@ -92,12 +92,7 @@ impl LedwandDither { } fn apply_histogram_correction(image: &mut GrayImage, correction: HistogramCorrection) { - let midpoint = image.width() / 2; - for (x, _, pixel) in image.enumerate_pixels_mut() { - if x > midpoint { - continue; - } - + for pixel in image.pixels_mut() { let pixel = &mut pixel.0[0]; let value = (*pixel as f32 + correction.pre_offset) * correction.factor + correction.post_offset; @@ -116,7 +111,7 @@ impl LedwandDither { let mut num_pixels = 0; for brightness in u8::MIN..=u8::MAX { - num_pixels += histogram[brightness as usize] as usize; + num_pixels += histogram[brightness as usize]; if num_pixels >= midpoint { return brightness; } @@ -124,4 +119,42 @@ impl LedwandDither { unreachable!("Somehow less pixels where counted in the histogram than exist in the image") } + + pub fn blur(source: &GrayImage, destination: &mut GrayImage) { + assert_eq!(source.len(), destination.len()); + + Self::copy_border(source, destination); + Self::blur_inner_pixels(source, destination); + } + + fn copy_border(source: &GrayImage, destination: &mut GrayImage) { + let last_row = source.height() -1; + for x in 0..source.width() { + destination[(x, 0)] = source[(x, 0)]; + destination[(x, last_row)] = source[(x, last_row)]; + } + let last_col = source.width() - 1; + for y in 0..source.height() { + destination[(0, y)] = source[(0, y)]; + destination[(last_col, y)] = source[(last_col, y)]; + } + } + + fn blur_inner_pixels(source: &GrayImage, destination: &mut GrayImage) { + for y in 1..source.height() - 2 { + for x in 1..source.width() - 2 { + let weighted_sum = source.get_pixel(x - 1, y - 1).0[0] as u32 + + source.get_pixel(x, y - 1).0[0] as u32 + + source.get_pixel(x + 1, y - 1).0[0] as u32 + + source.get_pixel(x - 1, y).0[0] as u32 + + 8 * source.get_pixel(x, y).0[0] as u32 + + source.get_pixel(x + 1, y).0[0] as u32 + + source.get_pixel(x - 1, y + 1).0[0] as u32 + + source.get_pixel(x, y + 1).0[0] as u32 + + source.get_pixel(x + 1, y + 1).0[0] as u32; + let blurred = weighted_sum / 16; + destination.get_pixel_mut(x, y).0[0] = blurred.clamp(u8::MIN as u32, u8::MAX as u32) as u8; + } + } + } } diff --git a/src/stream_window.rs b/src/stream_window.rs index db2b0bd..5ab7c3d 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -29,10 +29,11 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { loop { let mut frame = get_next_frame(&capturer); - LedwandDither::histogram_correction(&mut frame); let cutoff = if options.no_dither { LedwandDither::median_brightness(&frame) } else { + LedwandDither::histogram_correction(&mut frame); + LedwandDither::blur(&frame.clone(), &mut frame); dither(&mut frame, &BiLevel); u8::MAX / 2 }; From f64365f5bd4991c49ccd934dda34348b5eda207e Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Fri, 28 Feb 2025 12:12:39 +0100 Subject: [PATCH 15/36] implement sharpen --- src/ledwand_dither.rs | 24 ++++++++++++++++++++++++ src/stream_window.rs | 10 ++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index ce92b03..135515c 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -127,6 +127,13 @@ impl LedwandDither { Self::blur_inner_pixels(source, destination); } + pub fn sharpen(source: &GrayImage, destination: &mut GrayImage) { + assert_eq!(source.len(), destination.len()); + + Self::copy_border(source, destination); + Self::sharpen_inner_pixels(source, destination); + } + fn copy_border(source: &GrayImage, destination: &mut GrayImage) { let last_row = source.height() -1; for x in 0..source.width() { @@ -157,4 +164,21 @@ impl LedwandDither { } } } + + fn sharpen_inner_pixels(source: &GrayImage, destination: &mut GrayImage) { + for y in 1..source.height() - 2 { + for x in 1..source.width() - 2 { + let weighted_sum = -(source.get_pixel(x - 1, y - 1).0[0] as i32) + - source.get_pixel(x, y - 1).0[0] as i32 + - source.get_pixel(x + 1, y - 1).0[0] as i32 + - source.get_pixel(x - 1, y).0[0] as i32 + + 9 * source.get_pixel(x, y).0[0] as i32 + - source.get_pixel(x + 1, y).0[0] as i32 + - source.get_pixel(x - 1, y + 1).0[0] as i32 + - source.get_pixel(x, y + 1).0[0] as i32 + - source.get_pixel(x + 1, y + 1).0[0] as i32; + destination.get_pixel_mut(x, y).0[0] = weighted_sum.clamp(u8::MIN as i32, u8::MAX as i32) as u8; + } + } + } } diff --git a/src/stream_window.rs b/src/stream_window.rs index 5ab7c3d..0358c61 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -29,11 +29,17 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { loop { let mut frame = get_next_frame(&capturer); + LedwandDither::histogram_correction(&mut frame); + + let mut orig = frame.clone(); + LedwandDither::blur(&orig, &mut frame); + + std::mem::swap(&mut frame, &mut orig); + LedwandDither::sharpen(&orig, &mut frame); + let cutoff = if options.no_dither { LedwandDither::median_brightness(&frame) } else { - LedwandDither::histogram_correction(&mut frame); - LedwandDither::blur(&frame.clone(), &mut frame); dither(&mut frame, &BiLevel); u8::MAX / 2 }; From 9d5b21673a581605fff14b899e697a70a0c578c7 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Fri, 28 Feb 2025 17:38:38 +0100 Subject: [PATCH 16/36] ostromoukhov dither --- src/cli.rs | 8 - src/ledwand_dither.rs | 649 +++++++++++++++++++++++++++++++----------- src/stream_window.rs | 25 +- 3 files changed, 492 insertions(+), 190 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 12a478e..1ee5783 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -114,14 +114,6 @@ pub enum StreamCommand { #[derive(clap::Parser, std::fmt::Debug, Clone)] pub struct StreamScreenOptions { - #[arg( - long, - short, - default_value_t = false, - help = "Disable dithering - improves performance" - )] - pub no_dither: bool, - #[arg( long, short, diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index 135515c..be1091e 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -1,17 +1,7 @@ //! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand -use image::{GenericImage, GrayImage}; -use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH}; - -pub struct LedwandDither { - options: LedwandDitherOptions, - tmpbuf: GrayImage, -} - -#[derive(Debug, Default)] -pub struct LedwandDitherOptions { - pub size: Option<(u32, u32)>, -} +use image::GrayImage; +use servicepoint::{BitVec, Bitmap, PIXEL_HEIGHT}; type GrayHistogram = [usize; 256]; @@ -21,164 +11,495 @@ struct HistogramCorrection { factor: f32, } -impl LedwandDither { - pub fn new(options: LedwandDitherOptions) -> Self { - let (width, height) = options - .size - .unwrap_or((PIXEL_WIDTH as u32, PIXEL_HEIGHT as u32)); - Self { - tmpbuf: GrayImage::new(width, height), - options, +pub fn histogram_correction(image: &mut GrayImage) { + let histogram = make_histogram(image); + let correction = determine_histogram_correction(image, histogram); + apply_histogram_correction(image, correction) +} + +fn make_histogram(image: &GrayImage) -> GrayHistogram { + let mut histogram = [0; 256]; + for pixel in image.pixels() { + histogram[pixel.0[0] as usize] += 1; + } + histogram +} + +fn determine_histogram_correction( + image: &GrayImage, + histogram: GrayHistogram, +) -> HistogramCorrection { + let adjustment_pixels = image.len() / PIXEL_HEIGHT; + + let mut num_pixels = 0; + let mut brightness = 0; + + let mincut = loop { + num_pixels += histogram[brightness as usize]; + brightness += 1; + if num_pixels >= adjustment_pixels { + break u8::min(brightness, 20); + } + }; + + let minshift = loop { + num_pixels += histogram[brightness as usize]; + brightness += 1; + if num_pixels >= 2 * adjustment_pixels { + break u8::min(brightness, 64); + } + }; + + brightness = u8::MAX; + num_pixels = 0; + let maxshift = loop { + num_pixels += histogram[brightness as usize]; + brightness -= 1; + if num_pixels >= 2 * adjustment_pixels { + break u8::max(brightness, 192); + } + }; + + let pre_offset = -(mincut as f32 / 2.); + let post_offset = -(minshift as f32); + let factor = (255.0 - post_offset) / maxshift as f32; + HistogramCorrection { + pre_offset, + post_offset, + factor, + } +} + +fn apply_histogram_correction(image: &mut GrayImage, correction: HistogramCorrection) { + for pixel in image.pixels_mut() { + let pixel = &mut pixel.0[0]; + let value = + (*pixel as f32 + correction.pre_offset) * correction.factor + correction.post_offset; + *pixel = value.clamp(0f32, u8::MAX as f32) as u8; + } +} + +pub fn median_brightness(image: &GrayImage) -> u8 { + let histogram = make_histogram(image); + let midpoint = image.len() / 2; + + debug_assert_eq!( + image.len(), + histogram.iter().copied().map(usize::from).sum() + ); + + let mut num_pixels = 0; + for brightness in u8::MIN..=u8::MAX { + num_pixels += histogram[brightness as usize]; + if num_pixels >= midpoint { + return brightness; } } - pub fn histogram_correction(image: &mut GrayImage) { - let histogram = Self::make_histogram(image); - let correction = Self::determine_histogram_correction(image, histogram); - Self::apply_histogram_correction(image, correction) + unreachable!("Somehow less pixels where counted in the histogram than exist in the image") +} + +pub fn blur(source: &GrayImage, destination: &mut GrayImage) { + assert_eq!(source.len(), destination.len()); + + copy_border(source, destination); + blur_inner_pixels(source, destination); +} + +pub fn sharpen(source: &GrayImage, destination: &mut GrayImage) { + assert_eq!(source.len(), destination.len()); + + copy_border(source, destination); + sharpen_inner_pixels(source, destination); +} + +fn copy_border(source: &GrayImage, destination: &mut GrayImage) { + let last_row = source.height() - 1; + for x in 0..source.width() { + destination[(x, 0)] = source[(x, 0)]; + destination[(x, last_row)] = source[(x, last_row)]; } - - fn make_histogram(image: &GrayImage) -> GrayHistogram { - let mut histogram = [0; 256]; - for pixel in image.pixels() { - histogram[pixel.0[0] as usize] += 1; - } - histogram + let last_col = source.width() - 1; + for y in 0..source.height() { + destination[(0, y)] = source[(0, y)]; + destination[(last_col, y)] = source[(last_col, y)]; } +} - fn determine_histogram_correction( - image: &GrayImage, - histogram: GrayHistogram, - ) -> HistogramCorrection { - let adjustment_pixels = image.len() / PIXEL_HEIGHT; - - let mut num_pixels = 0; - let mut brightness = 0; - - let mincut = loop { - num_pixels += histogram[brightness as usize]; - brightness += 1; - if num_pixels >= adjustment_pixels { - break u8::min(brightness, 20); - } - }; - - let minshift = loop { - num_pixels += histogram[brightness as usize]; - brightness += 1; - if num_pixels >= 2 * adjustment_pixels { - break u8::min(brightness, 64); - } - }; - - brightness = u8::MAX; - num_pixels = 0; - let maxshift = loop { - num_pixels += histogram[brightness as usize]; - brightness -= 1; - if num_pixels >= 2 * adjustment_pixels { - break u8::max(brightness, 192); - } - }; - - let pre_offset = -(mincut as f32 / 2.); - let post_offset = -(minshift as f32); - let factor = (255.0 - post_offset) / maxshift as f32; - HistogramCorrection { - pre_offset, - post_offset, - factor, - } - } - - fn apply_histogram_correction(image: &mut GrayImage, correction: HistogramCorrection) { - for pixel in image.pixels_mut() { - let pixel = &mut pixel.0[0]; - let value = (*pixel as f32 + correction.pre_offset) * correction.factor - + correction.post_offset; - *pixel = value.clamp(0f32, u8::MAX as f32) as u8; - } - } - - pub fn median_brightness(image: &GrayImage) -> u8 { - let histogram = Self::make_histogram(image); - let midpoint = image.len() / 2; - - debug_assert_eq!( - image.len(), - histogram.iter().copied().map(usize::from).sum() - ); - - let mut num_pixels = 0; - for brightness in u8::MIN..=u8::MAX { - num_pixels += histogram[brightness as usize]; - if num_pixels >= midpoint { - return brightness; - } - } - - unreachable!("Somehow less pixels where counted in the histogram than exist in the image") - } - - pub fn blur(source: &GrayImage, destination: &mut GrayImage) { - assert_eq!(source.len(), destination.len()); - - Self::copy_border(source, destination); - Self::blur_inner_pixels(source, destination); - } - - pub fn sharpen(source: &GrayImage, destination: &mut GrayImage) { - assert_eq!(source.len(), destination.len()); - - Self::copy_border(source, destination); - Self::sharpen_inner_pixels(source, destination); - } - - fn copy_border(source: &GrayImage, destination: &mut GrayImage) { - let last_row = source.height() -1; - for x in 0..source.width() { - destination[(x, 0)] = source[(x, 0)]; - destination[(x, last_row)] = source[(x, last_row)]; - } - let last_col = source.width() - 1; - for y in 0..source.height() { - destination[(0, y)] = source[(0, y)]; - destination[(last_col, y)] = source[(last_col, y)]; - } - } - - fn blur_inner_pixels(source: &GrayImage, destination: &mut GrayImage) { - for y in 1..source.height() - 2 { - for x in 1..source.width() - 2 { - let weighted_sum = source.get_pixel(x - 1, y - 1).0[0] as u32 - + source.get_pixel(x, y - 1).0[0] as u32 - + source.get_pixel(x + 1, y - 1).0[0] as u32 - + source.get_pixel(x - 1, y).0[0] as u32 - + 8 * source.get_pixel(x, y).0[0] as u32 - + source.get_pixel(x + 1, y).0[0] as u32 - + source.get_pixel(x - 1, y + 1).0[0] as u32 - + source.get_pixel(x, y + 1).0[0] as u32 - + source.get_pixel(x + 1, y + 1).0[0] as u32; - let blurred = weighted_sum / 16; - destination.get_pixel_mut(x, y).0[0] = blurred.clamp(u8::MIN as u32, u8::MAX as u32) as u8; - } - } - } - - fn sharpen_inner_pixels(source: &GrayImage, destination: &mut GrayImage) { - for y in 1..source.height() - 2 { - for x in 1..source.width() - 2 { - let weighted_sum = -(source.get_pixel(x - 1, y - 1).0[0] as i32) - - source.get_pixel(x, y - 1).0[0] as i32 - - source.get_pixel(x + 1, y - 1).0[0] as i32 - - source.get_pixel(x - 1, y).0[0] as i32 - + 9 * source.get_pixel(x, y).0[0] as i32 - - source.get_pixel(x + 1, y).0[0] as i32 - - source.get_pixel(x - 1, y + 1).0[0] as i32 - - source.get_pixel(x, y + 1).0[0] as i32 - - source.get_pixel(x + 1, y + 1).0[0] as i32; - destination.get_pixel_mut(x, y).0[0] = weighted_sum.clamp(u8::MIN as i32, u8::MAX as i32) as u8; - } +fn blur_inner_pixels(source: &GrayImage, destination: &mut GrayImage) { + for y in 1..source.height() - 2 { + for x in 1..source.width() - 2 { + let weighted_sum = source.get_pixel(x - 1, y - 1).0[0] as u32 + + source.get_pixel(x, y - 1).0[0] as u32 + + source.get_pixel(x + 1, y - 1).0[0] as u32 + + source.get_pixel(x - 1, y).0[0] as u32 + + 8 * source.get_pixel(x, y).0[0] as u32 + + source.get_pixel(x + 1, y).0[0] as u32 + + source.get_pixel(x - 1, y + 1).0[0] as u32 + + source.get_pixel(x, y + 1).0[0] as u32 + + source.get_pixel(x + 1, y + 1).0[0] as u32; + let blurred = weighted_sum / 16; + destination.get_pixel_mut(x, y).0[0] = + blurred.clamp(u8::MIN as u32, u8::MAX as u32) as u8; } } } + +fn sharpen_inner_pixels(source: &GrayImage, destination: &mut GrayImage) { + for y in 1..source.height() - 2 { + for x in 1..source.width() - 2 { + let weighted_sum = -(source.get_pixel(x - 1, y - 1).0[0] as i32) + - source.get_pixel(x, y - 1).0[0] as i32 + - source.get_pixel(x + 1, y - 1).0[0] as i32 + - source.get_pixel(x - 1, y).0[0] as i32 + + 9 * source.get_pixel(x, y).0[0] as i32 + - source.get_pixel(x + 1, y).0[0] as i32 + - source.get_pixel(x - 1, y + 1).0[0] as i32 + - source.get_pixel(x, y + 1).0[0] as i32 + - source.get_pixel(x + 1, y + 1).0[0] as i32; + destination.get_pixel_mut(x, y).0[0] = + weighted_sum.clamp(u8::MIN as i32, u8::MAX as i32) as u8; + } + } +} + +pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { + let width = source.width(); + let height = source.height(); + assert_eq!(width % 8, 0); + + let mut source = source.into_raw(); + let mut destination = BitVec::repeat(false, source.len()); + + for y in 0..height as usize { + let start = y * width as usize; + if y % 2 == 0 { + for x in 0..width as usize { + ostromoukhov_dither_pixel( + &mut source, + &mut destination, + start + x, + width as usize, + y == (height - 1) as usize, + 1, + bias, + ); + } + } else { + for x in (0..width as usize).rev() { + ostromoukhov_dither_pixel( + &mut source, + &mut destination, + start + x, + width as usize, + y == (height - 1) as usize, + -1, + bias, + ); + } + } + } + + Bitmap::from_bitvec(width as usize, destination) +} + +#[inline] +fn ostromoukhov_dither_pixel( + source: &mut [u8], + destination: &mut BitVec, + position: usize, + width: usize, + last_row: bool, + direction: isize, + bias: u8, +) { + let old_pixel = source[position]; + + let destination_value = old_pixel > bias; + destination.set(position, destination_value); + + let error = if destination_value { + 255 - old_pixel + } else { + old_pixel + }; + + let mut diffuse = |to: usize, mat: i16| { + let diffuse_value = source[to] as i16 + mat; + source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8; + }; + + let lookup = if destination_value { + ERROR_DIFFUSION_MATRIX[error as usize].map(move |i| -i) + } else { + ERROR_DIFFUSION_MATRIX[error as usize] + }; + diffuse((position as isize + direction) as usize, lookup[0]); + + if !last_row { + diffuse( + ((position + width) as isize - direction) as usize, + lookup[1], + ); + diffuse(((position + width) as isize) as usize, lookup[2]); + } +} + +const ERROR_DIFFUSION_MATRIX: [[i16; 3]; 256] = [ + [0, 1, 0], + [1, 0, 0], + [1, 0, 1], + [2, 0, 1], + [2, 0, 2], + [3, 0, 2], + [4, 0, 2], + [4, 1, 2], + [5, 1, 2], + [5, 2, 2], + [5, 3, 2], + [6, 3, 2], + [6, 3, 3], + [7, 3, 3], + [7, 4, 3], + [8, 4, 3], + [8, 5, 3], + [9, 5, 3], + [9, 5, 4], + [10, 6, 3], + [10, 6, 4], + [11, 7, 3], + [11, 7, 4], + [11, 8, 4], + [12, 7, 5], + [12, 7, 6], + [12, 7, 7], + [12, 7, 8], + [12, 7, 9], + [13, 7, 9], + [13, 7, 10], + [13, 7, 11], + [13, 7, 12], + [14, 7, 12], + [14, 8, 12], + [15, 8, 12], + [15, 9, 12], + [16, 9, 12], + [16, 10, 12], + [17, 10, 12], + [17, 11, 12], + [18, 12, 11], + [19, 12, 11], + [19, 13, 11], + [20, 13, 11], + [20, 14, 11], + [21, 15, 10], + [22, 15, 10], + [22, 17, 9], + [23, 17, 9], + [24, 18, 8], + [24, 19, 8], + [25, 19, 8], + [26, 20, 7], + [26, 21, 7], + [27, 22, 6], + [28, 23, 5], + [28, 24, 5], + [29, 25, 4], + [30, 26, 3], + [31, 26, 3], + [31, 28, 2], + [32, 28, 2], + [33, 29, 1], + [34, 30, 0], + [33, 31, 1], + [32, 33, 1], + [32, 33, 2], + [31, 34, 3], + [30, 36, 3], + [29, 37, 4], + [29, 37, 5], + [28, 39, 5], + [32, 34, 7], + [37, 29, 8], + [42, 23, 10], + [46, 19, 11], + [51, 13, 12], + [52, 14, 13], + [53, 13, 12], + [53, 14, 13], + [54, 14, 13], + [55, 14, 13], + [55, 14, 13], + [56, 15, 14], + [57, 14, 13], + [56, 15, 15], + [55, 17, 15], + [54, 18, 16], + [53, 20, 16], + [52, 21, 17], + [52, 22, 17], + [51, 24, 17], + [50, 25, 18], + [49, 27, 18], + [47, 29, 19], + [48, 29, 19], + [48, 29, 20], + [49, 29, 20], + [49, 30, 20], + [50, 31, 20], + [50, 31, 20], + [51, 31, 20], + [51, 31, 21], + [52, 31, 21], + [52, 32, 21], + [53, 32, 21], + [53, 32, 22], + [55, 32, 21], + [56, 31, 22], + [58, 31, 21], + [59, 30, 22], + [61, 30, 21], + [62, 29, 22], + [64, 29, 21], + [65, 28, 22], + [67, 28, 21], + [68, 27, 22], + [70, 27, 21], + [71, 26, 22], + [73, 26, 21], + [75, 25, 21], + [76, 25, 21], + [78, 24, 21], + [80, 23, 21], + [81, 23, 21], + [83, 22, 21], + [85, 21, 20], + [85, 22, 21], + [85, 22, 22], + [84, 24, 22], + [84, 24, 23], + [84, 25, 23], + [83, 27, 23], + [83, 28, 23], + [82, 29, 24], + [82, 30, 24], + [81, 31, 25], + [80, 32, 26], + [80, 33, 26], + [79, 35, 26], + [79, 36, 26], + [78, 37, 27], + [77, 38, 28], + [77, 39, 28], + [76, 41, 28], + [75, 42, 29], + [75, 43, 29], + [74, 44, 30], + [74, 45, 30], + [75, 46, 30], + [75, 46, 30], + [76, 46, 30], + [76, 46, 31], + [77, 46, 31], + [77, 47, 31], + [78, 47, 31], + [78, 47, 32], + [79, 47, 32], + [79, 48, 32], + [80, 49, 32], + [83, 46, 32], + [86, 44, 32], + [90, 42, 31], + [93, 40, 31], + [96, 39, 30], + [100, 36, 30], + [103, 35, 29], + [106, 33, 29], + [110, 30, 29], + [113, 29, 28], + [114, 29, 28], + [115, 29, 28], + [115, 29, 28], + [116, 30, 29], + [117, 29, 28], + [117, 30, 29], + [118, 30, 29], + [119, 30, 29], + [109, 43, 27], + [100, 57, 23], + [90, 71, 20], + [80, 85, 17], + [70, 99, 14], + [74, 98, 12], + [78, 97, 10], + [81, 96, 9], + [85, 95, 7], + [89, 94, 5], + [92, 93, 4], + [96, 92, 2], + [100, 91, 0], + [100, 90, 2], + [100, 88, 5], + [100, 87, 7], + [99, 86, 10], + [99, 85, 12], + [99, 84, 14], + [99, 82, 17], + [98, 81, 20], + [98, 80, 22], + [98, 79, 24], + [98, 77, 27], + [98, 76, 29], + [97, 75, 32], + [97, 73, 35], + [97, 72, 37], + [96, 71, 40], + [96, 69, 43], + [96, 67, 46], + [96, 66, 48], + [95, 65, 51], + [95, 63, 54], + [95, 61, 57], + [94, 60, 60], + [94, 58, 63], + [94, 57, 65], + [93, 55, 69], + [93, 54, 71], + [93, 52, 74], + [92, 51, 77], + [92, 49, 80], + [91, 47, 84], + [91, 46, 86], + [93, 49, 82], + [96, 52, 77], + [98, 55, 73], + [101, 58, 68], + [104, 61, 63], + [106, 65, 58], + [109, 68, 53], + [111, 71, 49], + [114, 74, 44], + [116, 78, 39], + [118, 76, 40], + [119, 74, 42], + [120, 73, 43], + [122, 71, 44], + [123, 69, 46], + [124, 67, 48], + [125, 66, 49], + [127, 64, 50], + [128, 62, 52], + [129, 60, 54], + [131, 58, 55], + [132, 57, 56], + [136, 47, 63], + [139, 38, 70], + [143, 29, 76], + [147, 19, 83], + [151, 9, 90], + [154, 0, 97], + [160, 0, 92], + [171, 0, 82], + [183, 0, 71], + [184, 0, 71], +]; diff --git a/src/stream_window.rs b/src/stream_window.rs index 0358c61..6975ebf 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,7 +1,7 @@ use crate::cli::StreamScreenOptions; -use crate::ledwand_dither::{LedwandDither, LedwandDitherOptions}; +use crate::ledwand_dither::*; use image::{ - imageops::{dither, resize, BiLevel, FilterType}, + imageops::{resize, FilterType}, DynamicImage, ImageBuffer, Luma, Rgb, Rgba, }; use log::{error, info, warn}; @@ -11,42 +11,31 @@ use scap::{ frame::Frame, }; use servicepoint::{ - Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, + Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, }; use std::time::Duration; pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { info!("Starting capture with options: {:?}", options); - warn!("this implementation does not drop any frames - set a lower fps or disable dithering if your computer cannot keep up."); let capturer = match start_capture(&options) { Some(value) => value, None => return, }; - let mut bitmap = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT); info!("now starting to stream images"); loop { let mut frame = get_next_frame(&capturer); - LedwandDither::histogram_correction(&mut frame); + histogram_correction(&mut frame); let mut orig = frame.clone(); - LedwandDither::blur(&orig, &mut frame); + blur(&orig, &mut frame); std::mem::swap(&mut frame, &mut orig); - LedwandDither::sharpen(&orig, &mut frame); + sharpen(&orig, &mut frame); - let cutoff = if options.no_dither { - LedwandDither::median_brightness(&frame) - } else { - dither(&mut frame, &BiLevel); - u8::MAX / 2 - }; - - for (mut dest, src) in bitmap.iter_mut().zip(frame.pixels()) { - *dest = src.0[0] > cutoff; - } + let bitmap = ostromoukhov_dither(frame, u8::MAX / 2); connection .send(Command::BitmapLinearWin( From 70cc466253dde2a4a9dc8fd7646101ee056b916f Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Fri, 28 Feb 2025 18:49:29 +0100 Subject: [PATCH 17/36] add options to disable steps --- src/cli.rs | 16 ++++++++++++++++ src/stream_window.rs | 28 ++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 1ee5783..8de65ef 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -121,4 +121,20 @@ pub struct StreamScreenOptions { help = "Show mouse pointer in video feed" )] pub pointer: bool, + + #[arg(long, help = "Disable histogram correction")] + pub no_hist: bool, + + #[arg(long, help = "Disable blur")] + pub no_blur: bool, + + #[arg(long, help = "Disable sharpening")] + pub no_sharp: bool, + + #[arg( + long, + help = "Disable dithering. + Brightness will be adjusted so that around half of the pixels are on." + )] + pub no_dither: bool, } diff --git a/src/stream_window.rs b/src/stream_window.rs index 6975ebf..bfa9806 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -10,9 +10,7 @@ use scap::{ frame::convert_bgra_to_rgb, frame::Frame, }; -use servicepoint::{ - Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, -}; +use servicepoint::{Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH}; use std::time::Duration; pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { @@ -27,15 +25,29 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { loop { let mut frame = get_next_frame(&capturer); - histogram_correction(&mut frame); + if !options.no_hist { + histogram_correction(&mut frame); + } let mut orig = frame.clone(); - blur(&orig, &mut frame); - std::mem::swap(&mut frame, &mut orig); - sharpen(&orig, &mut frame); + if !options.no_blur { + blur(&orig, &mut frame); + std::mem::swap(&mut frame, &mut orig); + } - let bitmap = ostromoukhov_dither(frame, u8::MAX / 2); + if !options.no_sharp { + sharpen(&orig, &mut frame); + std::mem::swap(&mut frame, &mut orig); + } + + let bitmap = if 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) + } else { + ostromoukhov_dither(orig, u8::MAX / 2) + }; connection .send(Command::BitmapLinearWin( From ae1571bcd188cb2519f99ef671fce01a6697d55a Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Fri, 28 Feb 2025 22:46:33 +0100 Subject: [PATCH 18/36] update README, version, cargo update --- Cargo.lock | 174 ++++++++++++++++++++++++++++++++++++++--------------- Cargo.toml | 2 +- README.md | 30 +++++---- src/cli.rs | 3 +- 4 files changed, 147 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 31e39ae..19ef7a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" [[package]] name = "arbitrary" @@ -134,9 +134,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" +checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" dependencies = [ "arrayvec", ] @@ -251,9 +251,9 @@ checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" [[package]] name = "cc" -version = "1.2.14" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "jobserver", "libc", @@ -304,9 +304,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" dependencies = [ "clap_builder", "clap_derive", @@ -314,9 +314,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.30" +version = "4.5.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" dependencies = [ "anstream", "anstyle", @@ -559,9 +559,9 @@ checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" [[package]] name = "either" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" [[package]] name = "env_filter" @@ -618,9 +618,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.35" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" dependencies = [ "crc32fast", "miniz_oxide", @@ -772,7 +772,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "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", ] [[package]] @@ -952,9 +964,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libdbus-sys" @@ -1025,9 +1037,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "loop9" @@ -1071,9 +1083,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" dependencies = [ "adler2", "simd-adler32", @@ -1322,7 +1334,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -1390,8 +1402,19 @@ 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.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", ] [[package]] @@ -1401,7 +1424,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.2", ] [[package]] @@ -1410,7 +1443,17 @@ 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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a509b1a2ffbe92afab0e55c8fd99dea1c280e8171bd2d88682bb20bc41cbc2c" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.21", ] [[package]] @@ -1439,8 +1482,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", @@ -1485,9 +1528,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" dependencies = [ "bitflags 2.8.0", ] @@ -1560,7 +1603,7 @@ dependencies = [ "dbus", "objc", "pipewire", - "rand", + "rand 0.8.5", "screencapturekit", "screencapturekit-sys", "sysinfo", @@ -1600,18 +1643,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -1643,7 +1686,7 @@ dependencies = [ [[package]] name = "servicepoint-cli" -version = "0.2.1" +version = "0.3.0" dependencies = [ "clap", "env_logger", @@ -1856,17 +1899,16 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ - "byteorder", "bytes", "data-encoding", "http", "httparse", "log", - "rand", + "rand 0.9.0", "sha1", "thiserror 2.0.11", "utf-8", @@ -1874,15 +1916,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "unicode-segmentation" @@ -1943,6 +1985,15 @@ 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" @@ -2201,13 +2252,22 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" 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" @@ -2233,7 +2293,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "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", ] [[package]] @@ -2247,6 +2316,17 @@ 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 8076574..54edfe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "servicepoint-cli" description = "A command line interface for the ServicePoint display." -version = "0.2.1" +version = "0.3.0" edition = "2021" rust-version = "1.80.0" publish = true diff --git a/README.md b/README.md index 439cf6d..90ab17a 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,10 @@ cargo run -- Usage: servicepoint-cli [OPTIONS] Commands: - reset-everything [aliases: r] - pixels [aliases: p] - brightness [aliases: b] - stream [aliases: s] + reset-everything Reset both pixels and brightness [aliases: r] + pixels Commands for manipulating pixels [aliases: p] + brightness Commands for manipulating the brightness [aliases: b] + stream Continuously send data to the display [aliases: s] help Print this message or the help of the given subcommand(s) Options: @@ -59,52 +59,58 @@ Usage: servicepoint-cli stream Commands: stdin Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` screen Stream the default 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. - help Print this message or the help of the given subcommand(s) ``` #### Screen ``` +Stream the default 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 stream screen [OPTIONS] Options: - -n, --no-dither Disable dithering -p, --pointer Show mouse pointer in video feed - -h, --help Print help + --no-hist Disable histogram correction + --no-blur Disable blur + --no-sharp Disable sharpening + --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. ``` #### Stdin ``` +Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` + Usage: servicepoint-cli stream stdin [OPTIONS] Options: - -s, --slow - -h, --help Print help + -s, --slow Wait for a short amount of time before sending the next line ``` ### Brightness ``` +Commands for manipulating the brightness + Usage: servicepoint-cli brightness Commands: max Reset brightness to the default (max) level [aliases: r, reset] set Set one brightness for the whole screen [aliases: s] min Set brightness to lowest possible level. - help Print this message or the help of the given subcommand(s) ``` ### Pixels ``` +Commands for manipulating pixels + Usage: servicepoint-cli pixels Commands: - off Reset all pixels to the default (off) state [aliases: r, reset] + off Reset all pixels to the default (off) state [aliases: r, reset, clear] invert Invert the state of all pixels [aliases: i] on Set all pixels to the on state - help Print this message or the help of the given subcommand(s) ``` ## Contributing diff --git a/src/cli.rs b/src/cli.rs index 8de65ef..6830f98 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -133,8 +133,7 @@ pub struct StreamScreenOptions { #[arg( long, - help = "Disable dithering. - Brightness will be adjusted so that around half of the pixels are on." + help = "Disable dithering. Brightness will be adjusted so that around half of the pixels are on." )] pub no_dither: bool, } From 117e6a8bf7f3986dd089423a7860d42590a87ea3 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Fri, 28 Feb 2025 22:47:24 +0100 Subject: [PATCH 19/36] update flake --- flake.lock | 12 ++++++------ flake.nix | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index abacd0c..e99b4ca 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1736429655, - "narHash": "sha256-BwMekRuVlSB9C0QgwKMICiJ5EVbLGjfe4qyueyNQyGI=", + "lastModified": 1739824009, + "narHash": "sha256-fcNrCMUWVLMG3gKC5M9CBqVOAnJtyRvGPxptQFl5mVg=", "owner": "nix-community", "repo": "naersk", - "rev": "0621e47bd95542b8e1ce2ee2d65d6a1f887a13ce", + "rev": "e5130d37369bfa600144c2424270c96f0ef0e11d", "type": "github" }, "original": { @@ -37,11 +37,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1736549401, - "narHash": "sha256-ibkQrMHxF/7TqAYcQE+tOnIsSEzXmMegzyBWza6uHKM=", + "lastModified": 1740603184, + "narHash": "sha256-t+VaahjQAWyA+Ctn2idyo1yxRIYpaDxMgHkgCNiMJa4=", "owner": "nixos", "repo": "nixpkgs", - "rev": "1dab772dd4a68a7bba5d9460685547ff8e17d899", + "rev": "f44bd8ca21e026135061a0a57dcf3d0775b67a49", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 8bf4958..405734a 100644 --- a/flake.nix +++ b/flake.nix @@ -103,6 +103,7 @@ cargo-expand ]; }) + pkgs.cargo-flamegraph ]; LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (builtins.concatMap (d: d.buildInputs) inputsFrom)}"; RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; From 19f24f933107d25398f44450cab256596d192485 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 1 Mar 2025 11:18:42 +0100 Subject: [PATCH 20/36] extract image processing --- src/cli.rs | 6 ++++ src/image_processing.rs | 66 +++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/stream_window.rs | 58 +++++++++--------------------------- 4 files changed, 87 insertions(+), 44 deletions(-) create mode 100644 src/image_processing.rs diff --git a/src/cli.rs b/src/cli.rs index 6830f98..db64ca7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -122,6 +122,12 @@ pub struct StreamScreenOptions { )] pub pointer: bool, + #[transparent] + pub image_processing: ImageProcessingOptions, +} + +#[derive(clap::Parser, std::fmt::Debug, Clone)] +pub struct ImageProcessingOptions { #[arg(long, help = "Disable histogram correction")] pub no_hist: bool, diff --git a/src/image_processing.rs b/src/image_processing.rs new file mode 100644 index 0000000..f36bdc9 --- /dev/null +++ b/src/image_processing.rs @@ -0,0 +1,66 @@ +use crate::cli::ImageProcessingOptions; +use crate::ledwand_dither::{ + blur, histogram_correction, median_brightness, ostromoukhov_dither, sharpen, +}; +use image::imageops::{resize, FilterType}; +use image::{imageops, DynamicImage, ImageBuffer, Luma}; +use servicepoint::{Bitmap, PIXEL_HEIGHT, PIXEL_WIDTH}; + +pub struct ImageProcessingPipeline { + options: ImageProcessingOptions, +} + +impl ImageProcessingPipeline { + pub fn new(options: ImageProcessingOptions) -> Self { + Self { options } + } + + pub fn process(&self, frame: DynamicImage) -> Bitmap { + let frame = Self::resize_grayscale(&frame); + let frame = self.grayscale_processing(frame); + self.grayscale_to_bitmap(frame) + } + + fn resize_grayscale(frame: &DynamicImage) -> ImageBuffer, Vec> { + let frame = imageops::grayscale(&frame); + let frame = resize( + &frame, + PIXEL_WIDTH as u32, + PIXEL_HEIGHT as u32, + FilterType::Nearest, + ); + frame + } + + fn grayscale_processing( + &self, + mut frame: ImageBuffer, Vec>, + ) -> ImageBuffer, Vec> { + if !self.options.no_hist { + histogram_correction(&mut frame); + } + + let mut orig = frame.clone(); + + if !self.options.no_blur { + blur(&orig, &mut frame); + std::mem::swap(&mut frame, &mut orig); + } + + if !self.options.no_sharp { + sharpen(&orig, &mut frame); + std::mem::swap(&mut frame, &mut orig); + } + orig + } + + fn grayscale_to_bitmap(&self, mut orig: ImageBuffer, Vec>) -> Bitmap { + 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) + } else { + ostromoukhov_dither(orig, u8::MAX / 2) + } + } +} diff --git a/src/main.rs b/src/main.rs index cd7937c..2a86e33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use servicepoint::{Brightness, Connection}; mod brightness; mod cli; +mod image_processing; mod ledwand_dither; mod pixels; mod stream_stdin; diff --git a/src/stream_window.rs b/src/stream_window.rs index bfa9806..da8d866 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,5 +1,8 @@ -use crate::cli::StreamScreenOptions; -use crate::ledwand_dither::*; +use crate::{ + cli::{ImageProcessingOptions, StreamScreenOptions}, + image_processing::ImageProcessingPipeline, + ledwand_dither::*, +}; use image::{ imageops::{resize, FilterType}, DynamicImage, ImageBuffer, Luma, Rgb, Rgba, @@ -10,45 +13,26 @@ use scap::{ frame::convert_bgra_to_rgb, frame::Frame, }; -use servicepoint::{Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH}; +use servicepoint::{ + Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, + TILE_HEIGHT, TILE_SIZE, +}; use std::time::Duration; pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { info!("Starting capture with options: {:?}", options); - let capturer = match start_capture(&options) { Some(value) => value, None => return, }; + let pipeline = ImageProcessingPipeline::new(options.image_processing); + info!("now starting to stream images"); loop { - let mut frame = get_next_frame(&capturer); - - if !options.no_hist { - histogram_correction(&mut frame); - } - - let mut orig = frame.clone(); - - if !options.no_blur { - blur(&orig, &mut frame); - std::mem::swap(&mut frame, &mut orig); - } - - if !options.no_sharp { - sharpen(&orig, &mut frame); - std::mem::swap(&mut frame, &mut orig); - } - - let bitmap = if 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) - } else { - ostromoukhov_dither(orig, u8::MAX / 2) - }; - + let frame = capturer.get_next_frame().expect("failed to capture frame"); + let frame = frame_to_image(frame); + let bitmap = pipeline.process(frame); connection .send(Command::BitmapLinearWin( Origin::ZERO, @@ -59,20 +43,6 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { } } -/// returns next frame from the capturer, resized and grayscale -fn get_next_frame(capturer: &Capturer) -> ImageBuffer, Vec> { - let frame = capturer.get_next_frame().expect("failed to capture frame"); - let frame = frame_to_image(frame); - let frame = frame.grayscale().to_luma8(); - - resize( - &frame, - PIXEL_WIDTH as u32, - PIXEL_HEIGHT as u32, - FilterType::Nearest, - ) -} - fn start_capture(options: &StreamScreenOptions) -> Option { if !scap::is_supported() { error!("platform not supported by scap"); From b1c3ac85381fcf12f6add59b1a1dd03e935da9ac Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 1 Mar 2025 11:51:08 +0100 Subject: [PATCH 21/36] add send image command --- README.md | 26 +++++++++++++++++++++++--- src/cli.rs | 31 +++++++++++++++++++++++-------- src/image_processing.rs | 16 +++++++++------- src/main.rs | 5 ++++- src/pixels.rs | 28 +++++++++++++++++++++++++--- src/stream_window.rs | 30 ++++++++++++++++-------------- 6 files changed, 100 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 90ab17a..6cf3ca7 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,8 @@ Options: ### Stream ``` +Continuously send data to the display + Usage: servicepoint-cli stream Commands: @@ -108,9 +110,27 @@ Commands for manipulating pixels Usage: servicepoint-cli pixels Commands: - off Reset all pixels to the default (off) state [aliases: r, reset, clear] - invert Invert the state of all pixels [aliases: i] - on Set all pixels to the on state + off Reset all pixels to the default (off) state [aliases: r, reset, clear] + flip Invert the state of all pixels [aliases: f] + on Set all pixels to the on state + image Send an image file (e.g. jpeg or png) to the display. [aliases: i] +``` + +#### Image + +``` +Send an image file (e.g. jpeg or png) to the display. + +Usage: servicepoint-cli pixels image [OPTIONS] + +Arguments: + + +Options: + --no-hist Disable histogram correction + --no-blur Disable blur + --no-sharp Disable sharpening + --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. ``` ## Contributing diff --git a/src/cli.rs b/src/cli.rs index db64ca7..664916d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -57,10 +57,20 @@ pub enum PixelCommand { about = "Reset all pixels to the default (off) state" )] Off, - #[command(visible_alias = "i", about = "Invert the state of all pixels")] - Invert, + #[command(visible_alias = "f", about = "Invert the state of all pixels")] + Flip, #[command(about = "Set all pixels to the on state")] On, + #[command( + visible_alias = "i", + about = "Send an image file (e.g. jpeg or png) to the display." + )] + Image { + #[command(flatten)] + send_image_options: SendImageOptions, + #[command(flatten)] + image_processing_options: ImageProcessingOptions, + }, } #[derive(clap::Parser, std::fmt::Debug)] @@ -91,7 +101,7 @@ pub enum Protocol { #[derive(clap::Parser, std::fmt::Debug)] #[clap(about = "Continuously send data to the display")] pub enum StreamCommand { - #[clap( + #[command( about = "Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin`" )] Stdin { @@ -103,12 +113,14 @@ pub enum StreamCommand { )] slow: bool, }, - #[clap(about = "Stream the default source to the display. \ + #[command(about = "Stream the default 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.")] Screen { #[command(flatten)] - options: StreamScreenOptions, + stream_options: StreamScreenOptions, + #[command(flatten)] + image_processing: ImageProcessingOptions, }, } @@ -121,9 +133,6 @@ pub struct StreamScreenOptions { help = "Show mouse pointer in video feed" )] pub pointer: bool, - - #[transparent] - pub image_processing: ImageProcessingOptions, } #[derive(clap::Parser, std::fmt::Debug, Clone)] @@ -143,3 +152,9 @@ pub struct ImageProcessingOptions { )] pub no_dither: bool, } + +#[derive(clap::Parser, std::fmt::Debug, Clone)] +pub struct SendImageOptions { + #[arg()] + pub file_name: String, +} diff --git a/src/image_processing.rs b/src/image_processing.rs index f36bdc9..2b3b1b7 100644 --- a/src/image_processing.rs +++ b/src/image_processing.rs @@ -1,9 +1,11 @@ -use crate::cli::ImageProcessingOptions; -use crate::ledwand_dither::{ - blur, histogram_correction, median_brightness, ostromoukhov_dither, sharpen, +use crate::{ + cli::ImageProcessingOptions, + ledwand_dither::{blur, histogram_correction, median_brightness, ostromoukhov_dither, sharpen}, +}; +use image::{ + imageops::{resize, FilterType}, + DynamicImage, ImageBuffer, Luma, }; -use image::imageops::{resize, FilterType}; -use image::{imageops, DynamicImage, ImageBuffer, Luma}; use servicepoint::{Bitmap, PIXEL_HEIGHT, PIXEL_WIDTH}; pub struct ImageProcessingPipeline { @@ -22,7 +24,7 @@ impl ImageProcessingPipeline { } fn resize_grayscale(frame: &DynamicImage) -> ImageBuffer, Vec> { - let frame = imageops::grayscale(&frame); + let frame = frame.grayscale().to_luma8(); let frame = resize( &frame, PIXEL_WIDTH as u32, @@ -54,7 +56,7 @@ impl ImageProcessingPipeline { orig } - fn grayscale_to_bitmap(&self, mut orig: ImageBuffer, Vec>) -> Bitmap { + fn grayscale_to_bitmap(&self, orig: ImageBuffer, Vec>) -> Bitmap { if self.options.no_dither { let cutoff = median_brightness(&orig); let bits = orig.iter().map(move |x| x > &cutoff).collect(); diff --git a/src/main.rs b/src/main.rs index 2a86e33..a23a130 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,10 @@ pub fn execute_mode(mode: Mode, connection: Connection) { Mode::Brightness { brightness_command } => brightness(&connection, brightness_command), Mode::Stream { stream_command } => match stream_command { StreamCommand::Stdin { slow } => stream_stdin(connection, slow), - StreamCommand::Screen { options } => stream_window(&connection, options), + StreamCommand::Screen { + stream_options, + image_processing, + } => stream_window(&connection, stream_options, image_processing), }, } } diff --git a/src/pixels.rs b/src/pixels.rs index 4e6e3cf..ea0242e 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -1,12 +1,17 @@ -use crate::cli::PixelCommand; +use crate::cli::{ImageProcessingOptions, PixelCommand, SendImageOptions}; +use crate::image_processing::ImageProcessingPipeline; use log::info; -use servicepoint::{BitVec, Command, CompressionCode, Connection, PIXEL_COUNT}; +use servicepoint::{BitVec, Command, CompressionCode, Connection, Origin, PIXEL_COUNT}; pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { match pixel_command { PixelCommand::Off => pixels_off(connection), - PixelCommand::Invert => pixels_invert(connection), + PixelCommand::Flip => pixels_invert(connection), PixelCommand::On => pixels_on(connection), + PixelCommand::Image { + image_processing_options: processing_options, + send_image_options: image_options, + } => pixels_image(connection, image_options, processing_options), } } @@ -32,3 +37,20 @@ pub(crate) fn pixels_off(connection: &Connection) { .expect("failed to clear pixels"); info!("reset pixels"); } + +fn pixels_image( + connection: &Connection, + options: SendImageOptions, + processing_options: ImageProcessingOptions, +) { + let image = image::open(&options.file_name).expect("failed to open image file"); + let pipeline = ImageProcessingPipeline::new(processing_options); + let bitmap = pipeline.process(image); + connection + .send(Command::BitmapLinearWin( + Origin::ZERO, + bitmap, + CompressionCode::default(), + )) + .expect("failed to send image command"); +} diff --git a/src/stream_window.rs b/src/stream_window.rs index da8d866..c89368c 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,12 +1,6 @@ -use crate::{ - cli::{ImageProcessingOptions, StreamScreenOptions}, - image_processing::ImageProcessingPipeline, - ledwand_dither::*, -}; -use image::{ - imageops::{resize, FilterType}, - DynamicImage, ImageBuffer, Luma, Rgb, Rgba, -}; +use crate::cli::{ImageProcessingOptions, StreamScreenOptions}; +use crate::image_processing::ImageProcessingPipeline; +use image::{DynamicImage, ImageBuffer, Rgb, Rgba}; use log::{error, info, warn}; use scap::{ capturer::{Capturer, Options}, @@ -14,19 +8,26 @@ use scap::{ frame::Frame, }; use servicepoint::{ - Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, - TILE_HEIGHT, TILE_SIZE, + Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, TILE_HEIGHT, + TILE_SIZE, }; use std::time::Duration; -pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { +const SPACER_HEIGHT: usize = TILE_SIZE / 2; +const PIXEL_HEIGHT_INCLUDING_SPACERS: usize = SPACER_HEIGHT * (TILE_HEIGHT - 1) + PIXEL_HEIGHT; + +pub fn stream_window( + connection: &Connection, + options: StreamScreenOptions, + processing_options: ImageProcessingOptions, +) { info!("Starting capture with options: {:?}", options); let capturer = match start_capture(&options) { Some(value) => value, None => return, }; - let pipeline = ImageProcessingPipeline::new(options.image_processing); + let pipeline = ImageProcessingPipeline::new(processing_options); info!("now starting to stream images"); loop { @@ -57,10 +58,11 @@ fn start_capture(options: &StreamScreenOptions) -> Option { } } + // all options are more like a suggestion let mut capturer = Capturer::build(Options { fps: FRAME_PACING.div_duration_f32(Duration::from_secs(1)) as u32, show_cursor: options.pointer, - output_type: scap::frame::FrameType::BGR0, // this is more like a suggestion + output_type: scap::frame::FrameType::BGR0, ..Default::default() }) .expect("failed to create screen capture"); From 0521e103ec68848f851663a880cacc54d4880be8 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 1 Mar 2025 12:53:16 +0100 Subject: [PATCH 22/36] remove spacers in image processing --- src/cli.rs | 3 +++ src/image_processing.rs | 60 +++++++++++++++++++++++++++++++++++------ src/pixels.rs | 1 + src/stream_window.rs | 14 ++++------ 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 664916d..0f8dc8a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -151,6 +151,9 @@ pub struct ImageProcessingOptions { help = "Disable dithering. Brightness will be adjusted so that around half of the pixels are on." )] pub no_dither: bool, + + #[arg(long, help = "Do not remove the spacers from the image.")] + pub no_spacers: bool, } #[derive(clap::Parser, std::fmt::Debug, Clone)] diff --git a/src/image_processing.rs b/src/image_processing.rs index 2b3b1b7..4c06e68 100644 --- a/src/image_processing.rs +++ b/src/image_processing.rs @@ -6,32 +6,57 @@ use image::{ imageops::{resize, FilterType}, DynamicImage, ImageBuffer, Luma, }; -use servicepoint::{Bitmap, PIXEL_HEIGHT, PIXEL_WIDTH}; +use log::{debug, trace}; +use servicepoint::{Bitmap, Grid, PIXEL_HEIGHT, PIXEL_WIDTH, TILE_HEIGHT, TILE_SIZE}; +use std::time::Instant; +#[derive(Debug)] pub struct ImageProcessingPipeline { options: ImageProcessingOptions, } +const SPACER_HEIGHT: usize = TILE_SIZE / 2; +const PIXEL_HEIGHT_INCLUDING_SPACERS: usize = SPACER_HEIGHT * (TILE_HEIGHT - 1) + PIXEL_HEIGHT; + impl ImageProcessingPipeline { pub fn new(options: ImageProcessingOptions) -> Self { + debug!("Creating image pipeline: {:?}", options); Self { options } } pub fn process(&self, frame: DynamicImage) -> Bitmap { - let frame = Self::resize_grayscale(&frame); + let start_time = Instant::now(); + + let frame = self.resize_grayscale(frame); let frame = self.grayscale_processing(frame); - self.grayscale_to_bitmap(frame) + let mut result = self.grayscale_to_bitmap(frame); + + if !self.options.no_spacers { + result = Self::remove_spacers(result); + } + + trace!("image processing took {:?}", start_time.elapsed()); + result } - fn resize_grayscale(frame: &DynamicImage) -> ImageBuffer, Vec> { + fn resize_grayscale(&self, frame: DynamicImage) -> ImageBuffer, Vec> { + // TODO: keep aspect ratio + // TODO: make it work for non-maximum sizes + let frame = frame.grayscale().to_luma8(); - let frame = resize( + + let target_height = if self.options.no_spacers { + PIXEL_HEIGHT + } else { + PIXEL_HEIGHT_INCLUDING_SPACERS + }; + + resize( &frame, PIXEL_WIDTH as u32, - PIXEL_HEIGHT as u32, + target_height as u32, FilterType::Nearest, - ); - frame + ) } fn grayscale_processing( @@ -65,4 +90,23 @@ impl ImageProcessingPipeline { ostromoukhov_dither(orig, u8::MAX / 2) } } + + fn remove_spacers(bitmap: Bitmap) -> Bitmap { + let mut result = Bitmap::max_sized(); + + let mut source_y = 0; + for result_y in 0..result.height() { + if result_y != 0 && result_y % TILE_SIZE == 0 { + source_y += 4; + } + + for x in 0..result.width() { + result.set(x, result_y, bitmap.get(x, source_y)); + } + + source_y += 1; + } + + result + } } diff --git a/src/pixels.rs b/src/pixels.rs index ea0242e..a4fece0 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -53,4 +53,5 @@ fn pixels_image( CompressionCode::default(), )) .expect("failed to send image command"); + info!("sent image to display"); } diff --git a/src/stream_window.rs b/src/stream_window.rs index c89368c..e66cf26 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,5 +1,7 @@ -use crate::cli::{ImageProcessingOptions, StreamScreenOptions}; -use crate::image_processing::ImageProcessingPipeline; +use crate::{ + cli::{ImageProcessingOptions, StreamScreenOptions}, + image_processing::ImageProcessingPipeline, +}; use image::{DynamicImage, ImageBuffer, Rgb, Rgba}; use log::{error, info, warn}; use scap::{ @@ -7,15 +9,9 @@ use scap::{ frame::convert_bgra_to_rgb, frame::Frame, }; -use servicepoint::{ - Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, TILE_HEIGHT, - TILE_SIZE, -}; +use servicepoint::{Command, CompressionCode, Connection, Origin, FRAME_PACING}; use std::time::Duration; -const SPACER_HEIGHT: usize = TILE_SIZE / 2; -const PIXEL_HEIGHT_INCLUDING_SPACERS: usize = SPACER_HEIGHT * (TILE_HEIGHT - 1) + PIXEL_HEIGHT; - pub fn stream_window( connection: &Connection, options: StreamScreenOptions, From a1fa13b6e557590c9c66e2f78f4a6f214f174ce9 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Mar 2025 01:26:09 +0100 Subject: [PATCH 23/36] fast resize, now higher quality; keep aspect ratio --- Cargo.lock | 30 ++++++++++ Cargo.toml | 1 + src/image_processing.rs | 126 ++++++++++++++++++++++++++++------------ src/ledwand_dither.rs | 30 +++++----- src/pixels.rs | 2 +- src/stream_window.rs | 31 +++++++--- 6 files changed, 160 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19ef7a5..b9ad7ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -557,6 +557,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "document-features" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +dependencies = [ + "litrs", +] + [[package]] name = "either" version = "1.14.0" @@ -607,6 +616,20 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fast_image_resize" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55264ccc579fc127eebf6c6c1841d0c160d79a44c8f6f97047b7bc4a9c0d1a5" +dependencies = [ + "bytemuck", + "cfg-if", + "document-features", + "image", + "num-traits", + "thiserror 1.0.69", +] + [[package]] name = "fdeflate" version = "0.3.7" @@ -1025,6 +1048,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + [[package]] name = "lock_api" version = "0.4.12" @@ -1690,6 +1719,7 @@ version = "0.3.0" dependencies = [ "clap", "env_logger", + "fast_image_resize", "image", "log", "scap", diff --git a/Cargo.toml b/Cargo.toml index 54edfe6..a8cd60f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,4 @@ env_logger = "0.11" log = "0.4" scap = "0.0.8" image = "0.25.5" +fast_image_resize = { version = "5.1.2", features = ["image"] } diff --git a/src/image_processing.rs b/src/image_processing.rs index 4c06e68..bcc3104 100644 --- a/src/image_processing.rs +++ b/src/image_processing.rs @@ -2,29 +2,39 @@ use crate::{ cli::ImageProcessingOptions, ledwand_dither::{blur, histogram_correction, median_brightness, ostromoukhov_dither, sharpen}, }; -use image::{ - imageops::{resize, FilterType}, - DynamicImage, ImageBuffer, Luma, -}; +use fast_image_resize::{ResizeOptions, Resizer}; +use image::{DynamicImage, GrayImage}; use log::{debug, trace}; use servicepoint::{Bitmap, Grid, PIXEL_HEIGHT, PIXEL_WIDTH, TILE_HEIGHT, TILE_SIZE}; -use std::time::Instant; +use std::{default::Default, time::Instant}; #[derive(Debug)] pub struct ImageProcessingPipeline { options: ImageProcessingOptions, + resizer: Resizer, + render_size: (usize, usize), } const SPACER_HEIGHT: usize = TILE_SIZE / 2; -const PIXEL_HEIGHT_INCLUDING_SPACERS: usize = SPACER_HEIGHT * (TILE_HEIGHT - 1) + PIXEL_HEIGHT; impl ImageProcessingPipeline { pub fn new(options: ImageProcessingOptions) -> Self { debug!("Creating image pipeline: {:?}", options); - Self { options } + + let spacers_height = if options.no_spacers { + 0 + } else { + SPACER_HEIGHT * (TILE_HEIGHT - 1) + }; + + Self { + options, + resizer: Resizer::new(), + render_size: (PIXEL_WIDTH, PIXEL_HEIGHT + spacers_height), + } } - pub fn process(&self, frame: DynamicImage) -> Bitmap { + pub fn process(&mut self, frame: DynamicImage) -> Bitmap { let start_time = Instant::now(); let frame = self.resize_grayscale(frame); @@ -35,34 +45,31 @@ impl ImageProcessingPipeline { result = Self::remove_spacers(result); } - trace!("image processing took {:?}", start_time.elapsed()); + trace!("pipeline took {:?}", start_time.elapsed()); result } - fn resize_grayscale(&self, frame: DynamicImage) -> ImageBuffer, Vec> { - // TODO: keep aspect ratio - // TODO: make it work for non-maximum sizes + fn resize_grayscale(&mut self, frame: DynamicImage) -> GrayImage { + let start_time = Instant::now(); - let frame = frame.grayscale().to_luma8(); + let (scaled_width, scaled_height) = self.fit_size((frame.width(), frame.height())); + let mut dst_image = DynamicImage::new(scaled_width, scaled_height, frame.color()); - let target_height = if self.options.no_spacers { - PIXEL_HEIGHT - } else { - PIXEL_HEIGHT_INCLUDING_SPACERS - }; + self.resizer + .resize(&frame, &mut dst_image, &ResizeOptions::default()) + .expect("image resize failed"); - resize( - &frame, - PIXEL_WIDTH as u32, - target_height as u32, - FilterType::Nearest, - ) + trace!("resizing took {:?}", start_time.elapsed()); + + let start_time = Instant::now(); + let result = dst_image.into_luma8(); + trace!("grayscale took {:?}", start_time.elapsed()); + + result } - fn grayscale_processing( - &self, - mut frame: ImageBuffer, Vec>, - ) -> ImageBuffer, Vec> { + fn grayscale_processing(&self, mut frame: GrayImage) -> GrayImage { + let start_time = Instant::now(); if !self.options.no_hist { histogram_correction(&mut frame); } @@ -78,35 +85,78 @@ impl ImageProcessingPipeline { sharpen(&orig, &mut frame); std::mem::swap(&mut frame, &mut orig); } + + trace!("image processing took {:?}", start_time.elapsed()); orig } - fn grayscale_to_bitmap(&self, orig: ImageBuffer, Vec>) -> Bitmap { - if self.options.no_dither { + fn grayscale_to_bitmap(&self, orig: GrayImage) -> Bitmap { + let start_time = Instant::now(); + 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) } else { ostromoukhov_dither(orig, u8::MAX / 2) - } + }; + trace!("bitmap conversion took {:?}", start_time.elapsed()); + result } - fn remove_spacers(bitmap: Bitmap) -> Bitmap { - let mut result = Bitmap::max_sized(); + fn remove_spacers(source: Bitmap) -> Bitmap { + let start_time = Instant::now(); + + let full_tile_rows_with_spacers = source.height() / (TILE_SIZE + SPACER_HEIGHT); + let remaining_pixel_rows = source.height() % (TILE_SIZE + SPACER_HEIGHT); + let total_spacer_height = full_tile_rows_with_spacers * SPACER_HEIGHT + + remaining_pixel_rows.saturating_sub(TILE_SIZE); + let height_without_spacers = source.height() - total_spacer_height; + trace!( + "spacers take up {total_spacer_height}, resulting in height {height_without_spacers}" + ); + + let mut result = Bitmap::new(source.width(), height_without_spacers); let mut source_y = 0; for result_y in 0..result.height() { - if result_y != 0 && result_y % TILE_SIZE == 0 { - source_y += 4; - } - for x in 0..result.width() { - result.set(x, result_y, bitmap.get(x, source_y)); + result.set(x, result_y, source.get(x, source_y)); } + if result_y != 0 && result_y % TILE_SIZE == 0 { + source_y += SPACER_HEIGHT; + } source_y += 1; } + trace!("removing spacers took {:?}", start_time.elapsed()); + result + } + + fn fit_size(&self, source: (u32, u32)) -> (u32, u32) { + let (source_width, source_height) = source; + let (target_width, target_height) = self.render_size; + debug_assert_eq!(target_width % TILE_SIZE, 0); + + let width_scale = target_width as f32 / source_width as f32; + let height_scale = target_height as f32 / source_height as f32; + let scale = f32::min(width_scale, height_scale); + + let height = (source_height as f32 * scale) as u32; + let mut width = (source_width as f32 * scale) as u32; + + if width % TILE_SIZE as u32 != 0 { + // because we do not have many pixels, round up even if it is a worse fit + width += 8 - width % 8; + } + + let result = (width, height); + trace!( + "scaling {:?} to {:?} to fit {:?}", + source, + result, + self.render_size + ); result } } diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index be1091e..6dbf370 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -174,11 +174,11 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { for y in 0..height as usize { let start = y * width as usize; if y % 2 == 0 { - for x in 0..width as usize { + for x in start..start + width as usize { ostromoukhov_dither_pixel( &mut source, &mut destination, - start + x, + x, width as usize, y == (height - 1) as usize, 1, @@ -186,11 +186,11 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { ); } } else { - for x in (0..width as usize).rev() { + for x in (start..start + width as usize).rev() { ostromoukhov_dither_pixel( &mut source, &mut destination, - start + x, + x, width as usize, y == (height - 1) as usize, -1, @@ -213,17 +213,9 @@ fn ostromoukhov_dither_pixel( direction: isize, bias: u8, ) { - let old_pixel = source[position]; - - let destination_value = old_pixel > bias; + let (destination_value, error) = gray_to_bit(source[position], bias); destination.set(position, destination_value); - - let error = if destination_value { - 255 - old_pixel - } else { - old_pixel - }; - + let mut diffuse = |to: usize, mat: i16| { let diffuse_value = source[to] as i16 + mat; source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8; @@ -245,6 +237,16 @@ fn ostromoukhov_dither_pixel( } } +fn gray_to_bit(old_pixel: u8, bias: u8) -> (bool, u8) { + let destination_value = old_pixel > bias; + let error = if destination_value { + 255 - old_pixel + } else { + old_pixel + }; + (destination_value, error) +} + const ERROR_DIFFUSION_MATRIX: [[i16; 3]; 256] = [ [0, 1, 0], [1, 0, 0], diff --git a/src/pixels.rs b/src/pixels.rs index a4fece0..c92be07 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -44,7 +44,7 @@ fn pixels_image( processing_options: ImageProcessingOptions, ) { let image = image::open(&options.file_name).expect("failed to open image file"); - let pipeline = ImageProcessingPipeline::new(processing_options); + let mut pipeline = ImageProcessingPipeline::new(processing_options); let bitmap = pipeline.process(image); connection .send(Command::BitmapLinearWin( diff --git a/src/stream_window.rs b/src/stream_window.rs index e66cf26..f8e41ec 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -3,14 +3,14 @@ use crate::{ image_processing::ImageProcessingPipeline, }; use image::{DynamicImage, ImageBuffer, Rgb, Rgba}; -use log::{error, info, warn}; +use log::{debug, error, info, trace, warn}; use scap::{ capturer::{Capturer, Options}, frame::convert_bgra_to_rgb, frame::Frame, }; use servicepoint::{Command, CompressionCode, Connection, Origin, FRAME_PACING}; -use std::time::Duration; +use std::time::{Duration, Instant}; pub fn stream_window( connection: &Connection, @@ -23,20 +23,27 @@ pub fn stream_window( None => return, }; - let pipeline = ImageProcessingPipeline::new(processing_options); + let mut pipeline = ImageProcessingPipeline::new(processing_options); info!("now starting to stream images"); loop { - let frame = capturer.get_next_frame().expect("failed to capture frame"); + let start = Instant::now(); + + let frame = capture_frame(&capturer); let frame = frame_to_image(frame); let bitmap = pipeline.process(frame); + + trace!("bitmap ready to send in: {:?}", start.elapsed()); + connection .send(Command::BitmapLinearWin( Origin::ZERO, bitmap.clone(), - CompressionCode::Uncompressed, + CompressionCode::default(), )) .expect("failed to send frame to display"); + + debug!("frame time: {:?}", start.elapsed()); } } @@ -66,8 +73,16 @@ fn start_capture(options: &StreamScreenOptions) -> Option { Some(capturer) } +fn capture_frame(capturer: &Capturer) -> Frame { + let start_time = Instant::now(); + let result = capturer.get_next_frame().expect("failed to capture frame"); + trace!("capture took: {:?}", start_time.elapsed()); + result +} + fn frame_to_image(frame: Frame) -> DynamicImage { - match frame { + let start_time = Instant::now(); + let result = match frame { Frame::BGRx(frame) => bgrx_to_rgb(frame.width, frame.height, frame.data), Frame::RGBx(frame) => DynamicImage::from( ImageBuffer::, _>::from_raw( @@ -84,7 +99,9 @@ fn frame_to_image(frame: Frame) -> DynamicImage { ), Frame::BGRA(frame) => bgrx_to_rgb(frame.width, frame.height, frame.data), Frame::YUVFrame(_) | Frame::XBGR(_) => panic!("unsupported frame format"), - } + }; + trace!("conversion to image took: {:?}", start_time.elapsed()); + result } fn bgrx_to_rgb(width: i32, height: i32, data: Vec) -> DynamicImage { From 0ac6b77ed088b10d0035e25e3f900865b268dcd7 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Mar 2025 13:46:06 +0100 Subject: [PATCH 24/36] keep aspect is optional --- README.md | 22 ++++++++++------- src/cli.rs | 3 +++ src/image_processing.rs | 54 ++++++++++++++++++++++++----------------- src/ledwand_dither.rs | 2 +- 4 files changed, 49 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 6cf3ca7..873fd0f 100644 --- a/README.md +++ b/README.md @@ -71,11 +71,13 @@ Stream the default source to the display. On Linux Wayland, this pops up a scree Usage: servicepoint-cli stream screen [OPTIONS] Options: - -p, --pointer Show mouse pointer in video feed - --no-hist Disable histogram correction - --no-blur Disable blur - --no-sharp Disable sharpening - --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. + -p, --pointer Show mouse pointer in video feed + --no-hist Disable histogram correction + --no-blur Disable blur + --no-sharp Disable sharpening + --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. + --no-spacers Do not remove the spacers from the image. + --no-aspect Do not keep aspect ratio when resizing. ``` #### Stdin @@ -127,10 +129,12 @@ Arguments: Options: - --no-hist Disable histogram correction - --no-blur Disable blur - --no-sharp Disable sharpening - --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. + --no-hist Disable histogram correction + --no-blur Disable blur + --no-sharp Disable sharpening + --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. + --no-spacers Do not remove the spacers from the image. + --no-aspect Do not keep aspect ratio when resizing. ``` ## Contributing diff --git a/src/cli.rs b/src/cli.rs index 0f8dc8a..a919664 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -154,6 +154,9 @@ pub struct ImageProcessingOptions { #[arg(long, help = "Do not remove the spacers from the image.")] pub no_spacers: bool, + + #[arg(long, help = "Do not keep aspect ratio when resizing.")] + pub no_aspect: bool, } #[derive(clap::Parser, std::fmt::Debug, Clone)] diff --git a/src/image_processing.rs b/src/image_processing.rs index bcc3104..4e6baa8 100644 --- a/src/image_processing.rs +++ b/src/image_processing.rs @@ -12,7 +12,7 @@ use std::{default::Default, time::Instant}; pub struct ImageProcessingPipeline { options: ImageProcessingOptions, resizer: Resizer, - render_size: (usize, usize), + render_size: (u32, u32), } const SPACER_HEIGHT: usize = TILE_SIZE / 2; @@ -21,16 +21,17 @@ impl ImageProcessingPipeline { pub fn new(options: ImageProcessingOptions) -> Self { debug!("Creating image pipeline: {:?}", options); - let spacers_height = if options.no_spacers { - 0 - } else { - SPACER_HEIGHT * (TILE_HEIGHT - 1) - }; + let height = PIXEL_HEIGHT + + if options.no_spacers { + 0 + } else { + SPACER_HEIGHT * (TILE_HEIGHT - 1) + }; Self { options, resizer: Resizer::new(), - render_size: (PIXEL_WIDTH, PIXEL_HEIGHT + spacers_height), + render_size: (PIXEL_WIDTH as u32, height as u32), } } @@ -52,7 +53,11 @@ impl ImageProcessingPipeline { fn resize_grayscale(&mut self, frame: DynamicImage) -> GrayImage { let start_time = Instant::now(); - let (scaled_width, scaled_height) = self.fit_size((frame.width(), frame.height())); + let (scaled_width, scaled_height) = if self.options.no_aspect { + self.render_size + } else { + self.calc_scaled_size_keep_aspect((frame.width(), frame.height())) + }; let mut dst_image = DynamicImage::new(scaled_width, scaled_height, frame.color()); self.resizer @@ -106,20 +111,13 @@ impl ImageProcessingPipeline { fn remove_spacers(source: Bitmap) -> Bitmap { let start_time = Instant::now(); - let full_tile_rows_with_spacers = source.height() / (TILE_SIZE + SPACER_HEIGHT); - let remaining_pixel_rows = source.height() % (TILE_SIZE + SPACER_HEIGHT); - let total_spacer_height = full_tile_rows_with_spacers * SPACER_HEIGHT - + remaining_pixel_rows.saturating_sub(TILE_SIZE); - let height_without_spacers = source.height() - total_spacer_height; - trace!( - "spacers take up {total_spacer_height}, resulting in height {height_without_spacers}" - ); - - let mut result = Bitmap::new(source.width(), height_without_spacers); + let width = source.width(); + let result_height = Self::calc_height_without_spacers(source.height()); + let mut result = Bitmap::new(width, result_height); let mut source_y = 0; - for result_y in 0..result.height() { - for x in 0..result.width() { + for result_y in 0..result_height { + for x in 0..width { result.set(x, result_y, source.get(x, source_y)); } @@ -133,10 +131,22 @@ impl ImageProcessingPipeline { result } - fn fit_size(&self, source: (u32, u32)) -> (u32, u32) { + fn calc_height_without_spacers(height: usize) -> usize { + let full_tile_rows_with_spacers = height / (TILE_SIZE + SPACER_HEIGHT); + let remaining_pixel_rows = height % (TILE_SIZE + SPACER_HEIGHT); + let total_spacer_height = full_tile_rows_with_spacers * SPACER_HEIGHT + + remaining_pixel_rows.saturating_sub(TILE_SIZE); + let height_without_spacers = height - total_spacer_height; + trace!( + "spacers take up {total_spacer_height}, resulting in final height {height_without_spacers}" + ); + height_without_spacers + } + + fn calc_scaled_size_keep_aspect(&self, source: (u32, u32)) -> (u32, u32) { let (source_width, source_height) = source; let (target_width, target_height) = self.render_size; - debug_assert_eq!(target_width % TILE_SIZE, 0); + debug_assert_eq!(target_width % TILE_SIZE as u32, 0); let width_scale = target_width as f32 / source_width as f32; let height_scale = target_height as f32 / source_height as f32; diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index 6dbf370..d0e4b43 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -215,7 +215,7 @@ fn ostromoukhov_dither_pixel( ) { let (destination_value, error) = gray_to_bit(source[position], bias); destination.set(position, destination_value); - + let mut diffuse = |to: usize, mat: i16| { let diffuse_value = source[to] as i16 + mat; source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8; From 11d9ac0bcb773a613c642437cc2f51a06462b6a6 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Mar 2025 14:09:04 +0100 Subject: [PATCH 25/36] restructure cli --- README.md | 117 ++++++++++++++++++++++---------------------- src/cli.rs | 31 ++++++------ src/main.rs | 14 ++---- src/pixels.rs | 11 ++++- src/stream_stdin.rs | 8 +-- src/text.rs | 7 +++ 6 files changed, 100 insertions(+), 88 deletions(-) create mode 100644 src/text.rs diff --git a/README.md b/README.md index 873fd0f..be1dab7 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ 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] - stream Continuously send data to the display [aliases: s] + text Commands for sending text to the screen [aliases: t] help Print this message or the help of the given subcommand(s) Options: @@ -51,59 +51,6 @@ Options: -V, --version Print version ``` -### Stream - -``` -Continuously send data to the display - -Usage: servicepoint-cli stream - -Commands: - stdin Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` - screen Stream the default 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. -``` - -#### Screen - -``` -Stream the default 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 stream screen [OPTIONS] - -Options: - -p, --pointer Show mouse pointer in video feed - --no-hist Disable histogram correction - --no-blur Disable blur - --no-sharp Disable sharpening - --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. - --no-spacers Do not remove the spacers from the image. - --no-aspect Do not keep aspect ratio when resizing. -``` - -#### Stdin - -``` -Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin` - -Usage: servicepoint-cli stream stdin [OPTIONS] - -Options: - -s, --slow Wait for a short amount of time before sending the next line -``` - -### Brightness - -``` -Commands for manipulating the brightness - -Usage: servicepoint-cli brightness - -Commands: - max Reset brightness to the default (max) level [aliases: r, reset] - set Set one brightness for the whole screen [aliases: s] - min Set brightness to lowest possible level. -``` - ### Pixels ``` @@ -112,10 +59,11 @@ Commands for manipulating pixels Usage: servicepoint-cli pixels Commands: - off Reset all pixels to the default (off) state [aliases: r, reset, clear] - flip Invert the state of all pixels [aliases: f] - on Set all pixels to the on state - image Send an image file (e.g. jpeg or png) to the display. [aliases: i] + off Reset all pixels to the default (off) state [aliases: r, reset, clear] + flip Invert the state of all pixels [aliases: f] + on Set all pixels to the on state + image Send an image file (e.g. jpeg or png) to the display. [aliases: i] + screen 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. [aliases: s] ``` #### Image @@ -137,6 +85,59 @@ Options: --no-aspect Do not keep aspect ratio when resizing. ``` +#### Screen + +``` +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] + +Options: + -p, --pointer Show mouse pointer in video feed + --no-hist Disable histogram correction + --no-blur Disable blur + --no-sharp Disable sharpening + --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. + --no-spacers Do not remove the spacers from the image. + --no-aspect Do not keep aspect ratio when resizing. +``` + +### Brightness + +``` +Commands for manipulating the brightness + +Usage: servicepoint-cli brightness + +Commands: + max Reset brightness to the default (max) level [aliases: r, reset] + set Set one brightness for the whole screen [aliases: s] + min Set brightness to lowest possible level. +``` + +### 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 stream stdin` + +Usage: servicepoint-cli stream stdin [OPTIONS] + +Options: + -s, --slow Wait for a short amount of time before sending the next line +``` + + ## Contributing If you have ideas on how to improve the code, add features or improve documentation feel free to open a pull request. diff --git a/src/cli.rs b/src/cli.rs index a919664..365ff5a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -40,10 +40,10 @@ pub enum Mode { #[clap(subcommand)] brightness_command: BrightnessCommand, }, - #[command(visible_alias = "s")] - Stream { + #[command(visible_alias = "t")] + Text { #[clap(subcommand)] - stream_command: StreamCommand, + text_command: TextCommand, }, } @@ -71,6 +71,18 @@ pub enum PixelCommand { #[command(flatten)] image_processing_options: ImageProcessingOptions, }, + #[command( + visible_alias = "s", + about = "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." + )] + Screen { + #[command(flatten)] + stream_options: StreamScreenOptions, + #[command(flatten)] + image_processing: ImageProcessingOptions, + }, } #[derive(clap::Parser, std::fmt::Debug)] @@ -99,8 +111,8 @@ pub enum Protocol { } #[derive(clap::Parser, std::fmt::Debug)] -#[clap(about = "Continuously send data to the display")] -pub enum StreamCommand { +#[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`" )] @@ -113,15 +125,6 @@ pub enum StreamCommand { )] slow: bool, }, - #[command(about = "Stream the default 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.")] - Screen { - #[command(flatten)] - stream_options: StreamScreenOptions, - #[command(flatten)] - image_processing: ImageProcessingOptions, - }, } #[derive(clap::Parser, std::fmt::Debug, Clone)] diff --git a/src/main.rs b/src/main.rs index a23a130..4ca1df4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ use crate::{ brightness::{brightness, brightness_set}, - cli::{Cli, Mode, Protocol, StreamCommand}, + cli::{Cli, Mode, Protocol}, pixels::{pixels, pixels_off}, - stream_stdin::stream_stdin, - stream_window::stream_window, + text::text }; use clap::Parser; use log::debug; @@ -16,6 +15,7 @@ mod ledwand_dither; mod pixels; mod stream_stdin; mod stream_window; +mod text; fn main() { let cli = Cli::parse(); @@ -36,13 +36,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::Stream { stream_command } => match stream_command { - StreamCommand::Stdin { slow } => stream_stdin(connection, slow), - StreamCommand::Screen { - stream_options, - image_processing, - } => stream_window(&connection, stream_options, image_processing), - }, + Mode::Text { text_command} => text(&connection, text_command), } } diff --git a/src/pixels.rs b/src/pixels.rs index c92be07..da1aa6d 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -1,5 +1,8 @@ -use crate::cli::{ImageProcessingOptions, PixelCommand, SendImageOptions}; -use crate::image_processing::ImageProcessingPipeline; +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}; @@ -12,6 +15,10 @@ pub(crate) fn pixels(connection: &Connection, pixel_command: PixelCommand) { image_processing_options: processing_options, send_image_options: image_options, } => pixels_image(connection, image_options, processing_options), + PixelCommand::Screen { + stream_options, + image_processing, + } => stream_window(connection, stream_options, image_processing), } } diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index 82109ba..0bd7c04 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -2,7 +2,7 @@ use log::warn; use servicepoint::*; use std::thread::sleep; -pub(crate) fn stream_stdin(connection: Connection, slow: bool) { +pub(crate) fn stream_stdin(connection: &Connection, 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, @@ -13,14 +13,14 @@ pub(crate) fn stream_stdin(connection: Connection, slow: bool) { app.run() } -struct App { - connection: Connection, +struct App<'t> { + connection: &'t Connection, mirror: CharGrid, y: usize, slow: bool, } -impl App { +impl App<'_> { fn run(&mut self) { self.connection .send(Command::Clear) diff --git a/src/text.rs b/src/text.rs new file mode 100644 index 0000000..247b9ad --- /dev/null +++ b/src/text.rs @@ -0,0 +1,7 @@ +use servicepoint::Connection; +use crate::cli::TextCommand; +use crate::stream_stdin::stream_stdin; + +pub fn text(connection: &Connection, command: TextCommand) { + match command { TextCommand::Stdin { slow } => stream_stdin(connection, slow), } +} From 6eee677ed4ae63fde651eaf29c9fc6a120b9c83c Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 2 Mar 2025 15:06:39 +0100 Subject: [PATCH 26/36] send stdin as UTF instead of CP437 --- src/stream_stdin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream_stdin.rs b/src/stream_stdin.rs index 0bd7c04..b8b6cfb 100644 --- a/src/stream_stdin.rs +++ b/src/stream_stdin.rs @@ -63,9 +63,9 @@ impl App<'_> { fn send_mirror(&self) { self.connection - .send(Command::Cp437Data( + .send(Command::Utf8Data( Origin::ZERO, - Cp437Grid::from(&self.mirror), + self.mirror.clone(), )) .expect("couldn't send screen to display"); } From 044583141c1fe8d6e7dbcfee527ffb32f73388aa Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Thu, 1 May 2025 20:42:15 +0200 Subject: [PATCH 27/36] 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 28/36] 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), From 33fa110c65c9fbd3ec4f36b2b9f619f789f8bea9 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 4 May 2025 16:19:25 +0200 Subject: [PATCH 29/36] i didnt say im fancy fixing it there is an off by one somewhere _shrug_ --- src/ledwand_dither.rs | 21 +++++++++++++++++---- src/main.rs | 2 +- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ledwand_dither.rs b/src/ledwand_dither.rs index 95ffe02..c1caed4 100644 --- a/src/ledwand_dither.rs +++ b/src/ledwand_dither.rs @@ -1,6 +1,7 @@ //! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand use image::GrayImage; +use log::debug; use servicepoint::{Bitmap, DisplayBitVec, PIXEL_HEIGHT}; type GrayHistogram = [usize; 256]; @@ -173,6 +174,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { for y in 0..height as usize { let start = y * width as usize; + let last_row = y == (height - 1) as usize; if y % 2 == 0 { for x in start..start + width as usize { ostromoukhov_dither_pixel( @@ -180,7 +182,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { &mut destination, x, width as usize, - y == (height - 1) as usize, + last_row, 1, bias, ); @@ -192,7 +194,7 @@ pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap { &mut destination, x, width as usize, - y == (height - 1) as usize, + last_row, -1, bias, ); @@ -217,8 +219,16 @@ fn ostromoukhov_dither_pixel( destination.set(position, destination_value); let mut diffuse = |to: usize, mat: i16| { - let diffuse_value = source[to] as i16 + mat; - source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8; + match source.get(to) { + None => { + // last row has a out of bounds error on the last pixel + // TODO fix the iter bounds instead of ignoring here + } + Some(val) => { + let diffuse_value = *val as i16 + mat; + source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8; + } + }; }; let lookup = if destination_value { @@ -229,11 +239,14 @@ fn ostromoukhov_dither_pixel( diffuse((position as isize + direction) as usize, lookup[0]); if !last_row { + debug!("begin"); diffuse( ((position + width) as isize - direction) as usize, lookup[1], ); + debug!("mit"); diffuse(((position + width) as isize) as usize, lookup[2]); + debug!("end"); } } diff --git a/src/main.rs b/src/main.rs index a485526..cdae13c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use crate::{ }; use clap::Parser; use log::debug; -use servicepoint::{Brightness, HardResetCommand, UdpSocketExt}; +use servicepoint::{Brightness, HardResetCommand}; mod brightness; mod cli; From 05289581a1dd8d25ecfb23e7a0bd64b48c75d764 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 4 May 2025 16:48:27 +0200 Subject: [PATCH 30/36] update dependencies, bump version --- Cargo.lock | 375 +++++++++++++++++++++++++++++++++++------------------ Cargo.toml | 4 +- README.md | 6 + flake.lock | 12 +- 4 files changed, 266 insertions(+), 131 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 142bca9..143f29b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,9 +85,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.96" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b964d184e89d9b6b67dd2715bc8e74cf3107fb2b529990c90cf517326150bf4" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "arbitrary" @@ -148,7 +148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "annotate-snippets", - "bitflags 2.8.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools", @@ -176,9 +176,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitstream-io" @@ -227,15 +227,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.21.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder-lite" @@ -251,9 +245,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.16" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "jobserver", "libc", @@ -304,9 +298,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.31" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -314,9 +308,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.31" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -326,9 +320,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -437,7 +431,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "core-foundation", "core-graphics-types", "foreign-types", @@ -516,9 +510,9 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.5" +version = "3.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" +checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" dependencies = [ "nix 0.29.0", "windows-sys", @@ -568,9 +562,9 @@ dependencies = [ [[package]] name = "either" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7914353092ddf589ad78f25c5c1c21b7f80b0ff8621e7c814c3485b5306da9d" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "env_filter" @@ -584,14 +578,14 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", "env_filter", - "humantime", + "jiff", "log", ] @@ -618,9 +612,9 @@ dependencies = [ [[package]] name = "fast_image_resize" -version = "5.1.2" +version = "5.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55264ccc579fc127eebf6c6c1841d0c160d79a44c8f6f97047b7bc4a9c0d1a5" +checksum = "e146c782f75f50995dae9ecf9ec189fc9d0d2906318cc6826ea9451717fe52ec" dependencies = [ "bytemuck", "cfg-if", @@ -641,9 +635,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -789,9 +783,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -828,9 +822,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -838,9 +832,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "heck" @@ -865,17 +859,11 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - [[package]] name = "image" -version = "0.25.5" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", "byteorder-lite", @@ -912,9 +900,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.7.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -953,11 +941,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "jobserver" -version = "0.1.32" +name = "jiff" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -987,9 +1000,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.170" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libdbus-sys" @@ -1026,7 +1039,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f3a4b81b2a2d8c7f300643676202debd1b7c929dbf5c9bb89402ea11d19810" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cc", "convert_case", "cookie-factory", @@ -1066,9 +1079,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "loop9" @@ -1112,9 +1125,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -1132,7 +1145,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "libc", ] @@ -1143,7 +1156,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", "cfg-if", "cfg_aliases", "libc", @@ -1265,9 +1278,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "parking_lot" @@ -1317,7 +1330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08e645ba5c45109106d56610b3ee60eb13a6f2beb8b74f8dc8186cf261788dda" dependencies = [ "anyhow", - "bitflags 2.8.0", + "bitflags 2.9.0", "libc", "libspa", "libspa-sys", @@ -1340,9 +1353,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" @@ -1358,19 +1371,34 @@ dependencies = [ ] [[package]] -name = "ppv-lite86" -version = "0.2.20" +name = "portable-atomic" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -1411,9 +1439,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -1477,7 +1505,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -1526,9 +1554,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.11" +version = "0.11.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" dependencies = [ "avif-serialize", "imgref", @@ -1561,11 +1589,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.9" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b568323e98e49e2a0899dcee453dd679fae22d69adf9b11dd508d1549b7e2f" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -1621,9 +1649,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustversion" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "scap" @@ -1676,18 +1704,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -1713,12 +1741,12 @@ dependencies = [ "log", "once_cell", "rust-lzma", - "thiserror 2.0.11", + "thiserror 2.0.12", ] [[package]] name = "servicepoint-cli" -version = "0.3.0" +version = "0.4.0" dependencies = [ "clap", "env_logger", @@ -1773,9 +1801,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "strsim" @@ -1785,9 +1813,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.98" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -1857,11 +1885,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.11", + "thiserror-impl 2.0.12", ] [[package]] @@ -1877,9 +1905,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -1899,9 +1927,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -1911,18 +1939,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", @@ -1944,7 +1972,7 @@ dependencies = [ "log", "rand 0.9.1", "sha1", - "thiserror 2.0.11", + "thiserror 2.0.12", "utf-8", ] @@ -1956,9 +1984,9 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-segmentation" @@ -2135,17 +2163,40 @@ dependencies = [ ] [[package]] -name = "windows-capture" -version = "1.4.2" +name = "windows" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6001b777f61cafce437201de46a019ed7f4afed3b669f02e5ce4e0759164cb3e" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +dependencies = [ + "windows-collections", + "windows-core 0.61.0", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-capture" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d10b4be8b907c7055bc7270dd68d2b920978ffacc1599dcb563a79f0e68d16" dependencies = [ "clap", "ctrlc", "parking_lot", "rayon", - "thiserror 1.0.69", - "windows 0.58.0", + "thiserror 2.0.12", + "windows 0.61.1", + "windows-future", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.0", ] [[package]] @@ -2163,13 +2214,36 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", - "windows-strings", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", "windows-targets", ] +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.2", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -2181,6 +2255,17 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -2192,6 +2277,33 @@ dependencies = [ "syn", ] +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.0", + "windows-link", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -2201,16 +2313,34 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.59.0" @@ -2286,9 +2416,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.3" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" dependencies = [ "memchr", ] @@ -2299,7 +2429,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.8.0", + "bitflags 2.9.0", ] [[package]] @@ -2322,19 +2452,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index f1f2266..7c5315b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "servicepoint-cli" description = "A command line interface for the ServicePoint display." -version = "0.3.0" +version = "0.4.0" edition = "2021" rust-version = "1.80.0" publish = true @@ -18,7 +18,7 @@ env_logger = "0.11" log = "0.4" scap = "0.0.8" image = "0.25.5" -fast_image_resize = { version = "5.1.2", features = ["image"] } +fast_image_resize = { version = "5.1", features = ["image"] } tungstenite = "0.26" [dependencies.servicepoint] diff --git a/README.md b/README.md index 138dc7d..37f94a5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # servicepoint-cli +[![Release](https://git.berlin.ccc.de/servicepoint/servicepoint-cli/badges/release.svg)](https://git.berlin.ccc.de/servicepoint/servicepoint-cli/releases) +[![crates.io](https://img.shields.io/crates/v/servicepoint-cli.svg)](https://crates.io/crates/servicepoint-cli) +[![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint-cli)](https://crates.io/crates/servicepoint-cli) +![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint-cli) +[![CI](https://git.berlin.ccc.de/servicepoint/servicepoint-cli/badges/workflows/rust.yml/badge.svg)](https://git.berlin.ccc.de/servicepoint/servicepoint-cli) + This repository contains a command line interface for the ServicePoint display. To send commands, this uses the [servicepoint crate](https://crates.io/crates/servicepoint). diff --git a/flake.lock b/flake.lock index e99b4ca..578e72d 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1739824009, - "narHash": "sha256-fcNrCMUWVLMG3gKC5M9CBqVOAnJtyRvGPxptQFl5mVg=", + "lastModified": 1745925850, + "narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=", "owner": "nix-community", "repo": "naersk", - "rev": "e5130d37369bfa600144c2424270c96f0ef0e11d", + "rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f", "type": "github" }, "original": { @@ -37,11 +37,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1740603184, - "narHash": "sha256-t+VaahjQAWyA+Ctn2idyo1yxRIYpaDxMgHkgCNiMJa4=", + "lastModified": 1746183838, + "narHash": "sha256-kwaaguGkAqTZ1oK0yXeQ3ayYjs8u/W7eEfrFpFfIDFA=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f44bd8ca21e026135061a0a57dcf3d0775b67a49", + "rev": "bf3287dac860542719fe7554e21e686108716879", "type": "github" }, "original": { From cfe9699997a940f6a4583b632e5a231a95fcb646 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 8 Mar 2025 12:06:13 +0100 Subject: [PATCH 31/36] stream video file --- .github/workflows/rust.yml | 4 +- Cargo.lock | 64 ++++++++++++++++++++++++++++++- Cargo.toml | 1 + README.md | 20 ++++++++++ flake.nix | 9 +++-- src/cli.rs | 10 +++++ src/image_processing.rs | 1 + src/pixels.rs | 77 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 179 insertions(+), 7 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e944336..b84ada5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -26,10 +26,10 @@ jobs: - name: Install rust toolchain run: sudo apt-get install -qy cargo-1.80 rust-1.80-clippy - name: Install system dependencies - run: sudo apt-get install -qy liblzma-dev libpipewire-0.3-dev libclang-dev libdbus-1-dev + run: sudo apt-get install -qy liblzma-dev libpipewire-0.3-dev libclang-dev libdbus-1-dev ffmpeg libavutil-dev libavformat-dev libavfilter-dev libavdevice-dev - name: Run Clippy run: cargo clippy --all-targets --all-features - name: Build - run: cargo build --release --verbose + run: cargo build --release diff --git a/Cargo.lock b/Cargo.lock index 143f29b..0f6ccc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -162,6 +162,24 @@ dependencies = [ "syn", ] +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -633,6 +651,31 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ffmpeg-next" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da02698288e0275e442a47fc12ca26d50daf0d48b15398ba5906f20ac2e2a9f9" +dependencies = [ + "bitflags 2.9.0", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc3234d0a4b2f7d083699d0860c6c9dd83713908771b60f94a96f8704adfe45" +dependencies = [ + "bindgen 0.70.1", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + [[package]] name = "flate2" version = "1.1.1" @@ -842,6 +885,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "http" version = "1.3.1" @@ -1056,7 +1105,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf0d9716420364790e85cbb9d3ac2c950bde16a7dd36f3209b7dfdfc4a24d01f" dependencies = [ - "bindgen", + "bindgen 0.69.5", "cc", "system-deps", ] @@ -1237,6 +1286,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -1346,7 +1405,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "849e188f90b1dda88fe2bfe1ad31fe5f158af2c98f80fb5d13726c44f3f01112" dependencies = [ - "bindgen", + "bindgen 0.69.5", "libspa-sys", "system-deps", ] @@ -1751,6 +1810,7 @@ dependencies = [ "clap", "env_logger", "fast_image_resize", + "ffmpeg-next", "image", "log", "scap", diff --git a/Cargo.toml b/Cargo.toml index 7c5315b..ca24e69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ scap = "0.0.8" image = "0.25.5" fast_image_resize = { version = "5.1", features = ["image"] } tungstenite = "0.26" +ffmpeg-next = "7.1.0" [dependencies.servicepoint] package = "servicepoint" diff --git a/README.md b/README.md index 37f94a5..3876566 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Commands: flip Invert the state of all pixels [aliases: f] on Set all pixels to the on state image Send an image file (e.g. jpeg or png) to the display. [aliases: i] + video Stream a video file (e.g. mp4) to the display. [aliases: v] screen 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. [aliases: s] ``` @@ -91,6 +92,25 @@ Options: --no-aspect Do not keep aspect ratio when resizing. ``` +#### Video file + +```text +Stream a video file (e.g. mp4) to the display. + +Usage: servicepoint-cli pixels video [OPTIONS] + +Arguments: + + +Options: + --no-hist Disable histogram correction + --no-blur Disable blur + --no-sharp Disable sharpening + --no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on. + --no-spacers Do not remove the spacers from the image. + --no-aspect Do not keep aspect ratio when resizing. +``` + #### Screen ```text diff --git a/flake.nix b/flake.nix index 405734a..e8230c9 100644 --- a/flake.nix +++ b/flake.nix @@ -90,8 +90,7 @@ { default = pkgs.mkShell rec { inputsFrom = [ self.packages.${system}.default ]; - packages = [ - pkgs.gdb + packages = with pkgs; [ (pkgs.symlinkJoin { name = "rust-toolchain"; paths = with pkgs; [ @@ -103,7 +102,11 @@ cargo-expand ]; }) - pkgs.cargo-flamegraph + + cargo-flamegraph + gdb + + ffmpeg-headless ]; LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (builtins.concatMap (d: d.buildInputs) inputsFrom)}"; RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; diff --git a/src/cli.rs b/src/cli.rs index 543ba36..c7a4a45 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -74,6 +74,16 @@ pub enum PixelCommand { #[command(flatten)] image_processing_options: ImageProcessingOptions, }, + #[command( + visible_alias = "v", + about = "Stream a video file (e.g. mp4) to the display." + )] + Video { + #[command(flatten)] + send_image_options: SendImageOptions, + #[command(flatten)] + image_processing_options: ImageProcessingOptions, + }, #[command( visible_alias = "s", about = "Stream the default screen capture source to the display. \ diff --git a/src/image_processing.rs b/src/image_processing.rs index 9048dd3..6e27cde 100644 --- a/src/image_processing.rs +++ b/src/image_processing.rs @@ -35,6 +35,7 @@ impl ImageProcessingPipeline { } } + #[must_use] pub fn process(&mut self, frame: DynamicImage) -> Bitmap { let start_time = Instant::now(); diff --git a/src/pixels.rs b/src/pixels.rs index a7aad66..3f9665d 100644 --- a/src/pixels.rs +++ b/src/pixels.rs @@ -4,6 +4,8 @@ use crate::{ stream_window::stream_window, transport::Transport, }; +use ffmpeg_next as ffmpeg; +use image::{DynamicImage, RgbImage}; use log::info; use servicepoint::{ BinaryOperation, BitVecCommand, BitmapCommand, ClearCommand, CompressionCode, DisplayBitVec, @@ -23,6 +25,10 @@ pub(crate) fn pixels(connection: &Transport, pixel_command: PixelCommand) { stream_options, image_processing, } => stream_window(connection, stream_options, image_processing), + PixelCommand::Video { + image_processing_options: processing_options, + send_image_options: image_options, + } => pixels_video(connection, image_options, processing_options), } } @@ -78,3 +84,74 @@ fn pixels_image( .expect("failed to send image command"); info!("sent image to display"); } + +fn pixels_video( + connection: &Transport, + options: SendImageOptions, + processing_options: ImageProcessingOptions, +) { + ffmpeg::init().unwrap(); + + let mut ictx = ffmpeg::format::input(&options.file_name).expect("failed to open video input file"); + + let input = ictx + .streams() + .best(ffmpeg::media::Type::Video) + .ok_or(ffmpeg::Error::StreamNotFound) + .expect("could not get video stream from input file"); + let video_stream_index = input.index(); + + let context_decoder = ffmpeg::codec::context::Context::from_parameters(input.parameters()) + .expect("could not extract video context from parameters"); + let mut decoder = context_decoder.decoder().video() + .expect("failed to create decoder for video stream"); + + let src_width = decoder.width(); + let src_height = decoder.height(); + + let mut scaler = ffmpeg::software::scaling::Context::get( + decoder.format(), + src_width, + src_height, + ffmpeg::format::Pixel::RGB24, + src_width, + src_height, + ffmpeg::software::scaling::Flags::BILINEAR, + ).expect("failed to create scaling context"); + + let mut frame_index = 0; + + let mut processing_pipeline = ImageProcessingPipeline::new(processing_options); + + let mut receive_and_process_decoded_frames = + |decoder: &mut ffmpeg::decoder::Video| -> Result<(), ffmpeg::Error> { + let mut decoded = ffmpeg::util::frame::video::Video::empty(); + let mut rgb_frame = ffmpeg::util::frame::video::Video::empty(); + while decoder.receive_frame(&mut decoded).is_ok() { + scaler.run(&decoded, &mut rgb_frame) + .expect("failed to scale frame"); + + let image = RgbImage::from_raw(src_width, src_height, rgb_frame.data(0).to_owned()) + .expect("could not read rgb data to image"); + let image = DynamicImage::from(image); + let bitmap= processing_pipeline.process(image); + connection.send_command(BitmapCommand::from(bitmap)) + .expect("failed to send image command"); + + frame_index += 1; + } + Ok(()) + }; + + for (stream, packet) in ictx.packets() { + if stream.index() == video_stream_index { + decoder.send_packet(&packet) + .expect("failed to send video packet"); + receive_and_process_decoded_frames(&mut decoder) + .expect("failed to process video packet"); + } + } + decoder.send_eof().expect("failed to send eof"); + receive_and_process_decoded_frames(&mut decoder) + .expect("failed to eof packet"); +} From 30b1711b88fb1de214a42c5155fc6eb2d612fbfe Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 10 May 2025 10:21:39 +0200 Subject: [PATCH 32/36] fix flake --- flake.nix | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index e8230c9..f1a8ebf 100644 --- a/flake.nix +++ b/flake.nix @@ -55,7 +55,6 @@ }; nativeBuildInputs = with pkgs; [ pkg-config - libclang rustPlatform.bindgenHook ]; strictDeps = true; @@ -64,14 +63,13 @@ [ xe xz - clang + ffmpeg-headless ] ++ lib.optionals pkgs.stdenv.isLinux ( with pkgs; [ dbus pipewire - libclang ] ); }; @@ -105,8 +103,6 @@ cargo-flamegraph gdb - - ffmpeg-headless ]; LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (builtins.concatMap (d: d.buildInputs) inputsFrom)}"; RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; From 8e978b087745f8e6989735773c0d676632f25cdc Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 24 May 2025 15:21:06 +0200 Subject: [PATCH 33/36] update dependencies --- Cargo.lock | 285 ++++++++++++++++++++++++++++++++--------------------- Cargo.toml | 7 +- flake.lock | 6 +- 3 files changed, 178 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f6ccc2..0be38da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,12 +74,12 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys", ] @@ -120,9 +120,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "av1-grain" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" dependencies = [ "anyhow", "arrayvec", @@ -148,10 +148,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "annotate-snippets", - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", - "itertools", + "itertools 0.12.1", "lazy_static", "lazycell", "proc-macro2", @@ -168,10 +168,10 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", - "itertools", + "itertools 0.13.0", "proc-macro2", "quote", "regex", @@ -194,9 +194,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitstream-io" @@ -263,9 +263,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.21" +version = "1.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" +checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" dependencies = [ "jobserver", "libc", @@ -297,12 +297,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - [[package]] name = "clang-sys" version = "1.8.1" @@ -316,9 +310,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -326,9 +320,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -449,7 +443,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32eb7c354ae9f6d437a6039099ce7ecd049337a8109b23d73e48e8ffba8e9cd5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation", "core-graphics-types", "foreign-types", @@ -526,16 +520,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctrlc" -version = "3.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c" -dependencies = [ - "nix 0.29.0", - "windows-sys", -] - [[package]] name = "data-encoding" version = "2.9.0" @@ -630,9 +614,9 @@ dependencies = [ [[package]] name = "fast_image_resize" -version = "5.1.3" +version = "5.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e146c782f75f50995dae9ecf9ec189fc9d0d2906318cc6826ea9451717fe52ec" +checksum = "d372ab3252d8f162d858d675a3d88a8c33ba24a6238837c50c8851911c7e89cd" dependencies = [ "bytemuck", "cfg-if", @@ -657,16 +641,16 @@ version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da02698288e0275e442a47fc12ca26d50daf0d48b15398ba5906f20ac2e2a9f9" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "ffmpeg-sys-next", "libc", ] [[package]] name = "ffmpeg-sys-next" -version = "7.1.0" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc3234d0a4b2f7d083699d0860c6c9dd83713908771b60f94a96f8704adfe45" +checksum = "f9e9c75ebd4463de9d8998fb134ba26347fe5faee62fabf0a4b4d41bd500b4ad" dependencies = [ "bindgen 0.70.1", "cc", @@ -837,9 +821,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -983,6 +967,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -991,9 +984,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" +checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" dependencies = [ "jiff-static", "log", @@ -1004,9 +997,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" +checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" dependencies = [ "proc-macro2", "quote", @@ -1019,7 +1012,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -1074,12 +1067,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets", + "windows-targets 0.53.0", ] [[package]] @@ -1088,13 +1081,13 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f3a4b81b2a2d8c7f300643676202debd1b7c929dbf5c9bb89402ea11d19810" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cc", "convert_case", "cookie-factory", "libc", "libspa-sys", - "nix 0.27.1", + "nix", "nom", "system-deps", ] @@ -1194,23 +1187,11 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "libc", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.9.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nom" version = "7.1.3" @@ -1341,6 +1322,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1361,7 +1348,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1389,11 +1376,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08e645ba5c45109106d56610b3ee60eb13a6f2beb8b74f8dc8186cf261788dda" dependencies = [ "anyhow", - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", "libspa", "libspa-sys", - "nix 0.27.1", + "nix", "once_cell", "pipewire-sys", "thiserror 1.0.69", @@ -1573,7 +1560,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -1590,7 +1577,7 @@ dependencies = [ "built", "cfg-if", "interpolate_name", - "itertools", + "itertools 0.12.1", "libc", "libfuzzer-sys", "log", @@ -1652,7 +1639,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -1708,9 +1695,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "scap" @@ -1792,9 +1779,9 @@ dependencies = [ [[package]] name = "servicepoint" -version = "0.14.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6bd5cfa49c73aeecb344680ffbf697abf73e0563a441b93b9723ae43867500f" +checksum = "91a33bff7f9db5008748b23ca0c906c276fe00694390b681f004a55968a42cfe" dependencies = [ "bitvec", "log", @@ -2176,9 +2163,9 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "winapi" @@ -2209,7 +2196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core 0.52.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2219,7 +2206,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ "windows-core 0.58.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2229,7 +2216,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core 0.61.2", "windows-future", "windows-link", "windows-numerics", @@ -2237,12 +2224,10 @@ dependencies = [ [[package]] name = "windows-capture" -version = "1.4.3" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59d10b4be8b907c7055bc7270dd68d2b920978ffacc1599dcb563a79f0e68d16" +checksum = "757c9e7b920233fec91cb314ad92b96853ba2907e3482a193d290d33838e3fc5" dependencies = [ - "clap", - "ctrlc", "parking_lot", "rayon", "thiserror 2.0.12", @@ -2256,7 +2241,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", + "windows-core 0.61.2", ] [[package]] @@ -2265,7 +2250,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2278,30 +2263,31 @@ dependencies = [ "windows-interface 0.58.0", "windows-result 0.2.0", "windows-strings 0.1.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-core" -version = "0.61.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement 0.60.0", "windows-interface 0.59.1", "windows-link", - "windows-result 0.3.2", - "windows-strings 0.4.0", + "windows-result 0.3.4", + "windows-strings 0.4.2", ] [[package]] name = "windows-future" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core 0.61.0", + "windows-core 0.61.2", "windows-link", + "windows-threading", ] [[package]] @@ -2360,7 +2346,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core 0.61.2", "windows-link", ] @@ -2370,14 +2356,14 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-result" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ "windows-link", ] @@ -2389,14 +2375,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result 0.2.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "windows-strings" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ "windows-link", ] @@ -2407,7 +2393,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2416,14 +2402,39 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link", ] [[package]] @@ -2432,42 +2443,84 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2475,10 +2528,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "winnow" -version = "0.7.9" +name = "windows_x86_64_msvc" +version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "winnow" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -2489,7 +2548,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ca24e69..b857c2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,14 +17,13 @@ clap = { version = "4.5", features = ["derive"] } env_logger = "0.11" log = "0.4" scap = "0.0.8" -image = "0.25.5" +image = "0.25" fast_image_resize = { version = "5.1", features = ["image"] } tungstenite = "0.26" -ffmpeg-next = "7.1.0" +ffmpeg-next = "7.1" [dependencies.servicepoint] -package = "servicepoint" -version = "0.14.1" +version = "0.15.0" [profile.release] lto = true # Enable link-time optimization diff --git a/flake.lock b/flake.lock index 578e72d..93740d5 100644 --- a/flake.lock +++ b/flake.lock @@ -37,11 +37,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1746183838, - "narHash": "sha256-kwaaguGkAqTZ1oK0yXeQ3ayYjs8u/W7eEfrFpFfIDFA=", + "lastModified": 1747862697, + "narHash": "sha256-U4HaNZ1W26cbOVm0Eb5OdGSnfQVWQKbLSPrSSa78KC0=", "owner": "nixos", "repo": "nixpkgs", - "rev": "bf3287dac860542719fe7554e21e686108716879", + "rev": "2baa12ff69913392faf0ace833bc54bba297ea95", "type": "github" }, "original": { From 54b95956cb077d301bb45561cfeb4258e5110de5 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 28 Jun 2025 15:11:16 +0200 Subject: [PATCH 34/36] update servicepoint --- Cargo.lock | 18 +++++++++++++++--- Cargo.toml | 4 +--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0be38da..d6517df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -941,6 +941,17 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inherent" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c38228f24186d9cc68c729accb4d413be9eaed6ad07ff79e0270d9e56f3de13" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "interpolate_name" version = "0.2.4" @@ -1779,11 +1790,12 @@ dependencies = [ [[package]] name = "servicepoint" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91a33bff7f9db5008748b23ca0c906c276fe00694390b681f004a55968a42cfe" +checksum = "2800caad491cb44f67e5dd5b8c61ece368eecfe588155d03c7d9864acbad6919" dependencies = [ "bitvec", + "inherent", "log", "once_cell", "rust-lzma", @@ -1792,7 +1804,7 @@ dependencies = [ [[package]] name = "servicepoint-cli" -version = "0.4.0" +version = "0.4.1" dependencies = [ "clap", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index b857c2d..8c14126 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,7 @@ image = "0.25" fast_image_resize = { version = "5.1", features = ["image"] } tungstenite = "0.26" ffmpeg-next = "7.1" - -[dependencies.servicepoint] -version = "0.15.0" +servicepoint = "0.15.1" [profile.release] lto = true # Enable link-time optimization From e8ee8772231bc774b63b9d6797e736220fafc60a Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 28 Jun 2025 15:18:39 +0200 Subject: [PATCH 35/36] update flake to nixos 25.05, cargo update, set version --- Cargo.lock | 264 ++++++++++++++++++++++++++++------------------------- Cargo.toml | 2 +- README.md | 5 - flake.lock | 8 +- flake.nix | 2 +- 5 files changed, 148 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6517df..ec4150a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -19,9 +19,12 @@ dependencies = [ [[package]] name = "aligned-vec" -version = "0.5.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] [[package]] name = "annotate-snippets" @@ -35,9 +38,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -50,33 +53,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", @@ -114,9 +117,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "av1-grain" @@ -134,9 +137,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +checksum = "19135c0c7a60bfee564dbe44ab5ce0557c6bf3884e5291a50be76a15640c4fbd" dependencies = [ "arrayvec", ] @@ -239,15 +242,15 @@ checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" [[package]] name = "byteorder-lite" @@ -263,9 +266,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.24" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -293,9 +296,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "clang-sys" @@ -310,9 +313,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.38" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -320,9 +323,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -332,9 +335,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", @@ -344,9 +347,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "cocoa" @@ -386,9 +389,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "convert_case" @@ -506,9 +509,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -591,6 +594,26 @@ dependencies = [ "log", ] +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -662,9 +685,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -816,7 +839,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -833,9 +856,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" dependencies = [ "color_quant", "weezl", @@ -859,9 +882,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "heck" @@ -871,9 +894,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.9" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "http" @@ -917,9 +940,9 @@ dependencies = [ [[package]] name = "image-webp" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" dependencies = [ "byteorder-lite", "quick-error", @@ -933,9 +956,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -995,9 +1018,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "log", @@ -1008,9 +1031,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", @@ -1029,9 +1052,9 @@ dependencies = [ [[package]] name = "jpeg-decoder" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" [[package]] name = "lazy_static" @@ -1053,9 +1076,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libdbus-sys" @@ -1078,12 +1101,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-targets 0.53.2", ] [[package]] @@ -1122,9 +1145,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1166,9 +1189,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minimal-lexical" @@ -1178,9 +1201,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -1280,9 +1303,9 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", @@ -1341,9 +1364,9 @@ checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -1351,9 +1374,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -1429,9 +1452,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -1462,18 +1485,18 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", "syn", @@ -1505,9 +1528,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radium" @@ -1611,9 +1634,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.12" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" dependencies = [ "avif-serialize", "imgref", @@ -1646,9 +1669,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags 2.9.1", ] @@ -1781,9 +1804,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -1851,18 +1874,15 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "strsim" @@ -1872,9 +1892,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -1986,9 +2006,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", @@ -1998,18 +2018,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", @@ -2073,9 +2093,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "v_frame" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" dependencies = [ "aligned-vec", "num-traits", @@ -2102,9 +2122,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -2223,9 +2243,9 @@ dependencies = [ [[package]] name = "windows" -version = "0.61.1" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", "windows-core 0.61.2", @@ -2243,7 +2263,7 @@ dependencies = [ "parking_lot", "rayon", "thiserror 2.0.12", - "windows 0.61.1", + "windows 0.61.3", "windows-future", ] @@ -2348,9 +2368,9 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-numerics" @@ -2426,9 +2446,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -2547,9 +2567,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -2583,18 +2603,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", @@ -2618,9 +2638,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +checksum = "7384255a918371b5af158218d131530f694de9ad3815ebdd0453a940485cb0fa" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index 8c14126..5b118a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "servicepoint-cli" description = "A command line interface for the ServicePoint display." -version = "0.4.0" +version = "0.4.1" edition = "2021" rust-version = "1.80.0" publish = true diff --git a/README.md b/README.md index 3876566..80b84d5 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,9 @@ If you have set your PATH to include the ~/.cargo/bin, you can now run `servicep ## Running with nix ```shell -# from CCCB Forgejo nix run git+https://git.berlin.ccc.de/servicepoint/servicepoint-cli.git -- - -# from GitHub mirror -nix run github:kaesaecracker/servicepoint-cli -- ``` - ## Running a debug build ```shell diff --git a/flake.lock b/flake.lock index 93740d5..e507dfb 100644 --- a/flake.lock +++ b/flake.lock @@ -37,16 +37,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1747862697, - "narHash": "sha256-U4HaNZ1W26cbOVm0Eb5OdGSnfQVWQKbLSPrSSa78KC0=", + "lastModified": 1750969886, + "narHash": "sha256-zW/OFnotiz/ndPFdebpo3X0CrbVNf22n4DjN2vxlb58=", "owner": "nixos", "repo": "nixpkgs", - "rev": "2baa12ff69913392faf0ace833bc54bba297ea95", + "rev": "a676066377a2fe7457369dd37c31fd2263b662f4", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-24.11", + "ref": "nixos-25.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index f1a8ebf..5d0c6d3 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Flake for command line interface of the ServicePoint display."; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11"; + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; nix-filter.url = "github:numtide/nix-filter"; naersk = { url = "github:nix-community/naersk"; From 07a5fbca27ec941c841ad93f2ac65bc529225a51 Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 13 Sep 2025 13:36:44 +0200 Subject: [PATCH 36/36] nix flake update, add overlay and nixosModule --- flake.lock | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ flake.nix | 16 +++++++++++++--- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index e507dfb..4999493 100644 --- a/flake.lock +++ b/flake.lock @@ -1,17 +1,40 @@ { "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "naersk", + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1752475459, + "narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=", + "owner": "nix-community", + "repo": "fenix", + "rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "naersk": { "inputs": { + "fenix": "fenix", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1745925850, - "narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=", + "lastModified": 1752689277, + "narHash": "sha256-uldUBFkZe/E7qbvxa3mH1ItrWZyT6w1dBKJQF/3ZSsc=", "owner": "nix-community", "repo": "naersk", - "rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f", + "rev": "0e72363d0938b0208d6c646d10649164c43f4d64", "type": "github" }, "original": { @@ -37,11 +60,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1750969886, - "narHash": "sha256-zW/OFnotiz/ndPFdebpo3X0CrbVNf22n4DjN2vxlb58=", + "lastModified": 1757545623, + "narHash": "sha256-mCxPABZ6jRjUQx3bPP4vjA68ETbPLNz9V2pk9tO7pRQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "a676066377a2fe7457369dd37c31fd2263b662f4", + "rev": "8cd5ce828d5d1d16feff37340171a98fc3bf6526", "type": "github" }, "original": { @@ -57,6 +80,23 @@ "nix-filter": "nix-filter", "nixpkgs": "nixpkgs" } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1752428706, + "narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "591e3b7624be97e4443ea7b5542c191311aa141d", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 5d0c6d3..0586565 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,7 @@ }; outputs = - inputs@{ + { self, nixpkgs, naersk, @@ -42,7 +42,7 @@ naersk' = pkgs.callPackage naersk { }; in rec { - servicepoint-cli = naersk'.buildPackage rec { + servicepoint-cli = naersk'.buildPackage { src = nix-filter.lib.filter { root = ./.; include = [ @@ -80,6 +80,14 @@ legacyPackages = packages; + nixosModules.default = { + nixpkgs.overlays = [ self.overlays.default ]; + }; + + overlays.default = final: prev: { + servicepoint-cli = self.legacyPackages."${prev.system}".servicepoint-cli; + }; + devShells = forAllSystems ( { pkgs, @@ -106,10 +114,12 @@ ]; LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath (builtins.concatMap (d: d.buildInputs) inputsFrom)}"; RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}"; + RUST_LOG = "all"; + RUST_BACKTRACE = "1"; }; } ); - formatter = forAllSystems ({ pkgs, ... }: pkgs.nixfmt-rfc-style); + formatter = forAllSystems ({ pkgs, ... }: pkgs.nixfmt-tree); }; }