Stream video files, update servicepoint, fix dithering #3
					 12 changed files with 179 additions and 125 deletions
				
			
		
							
								
								
									
										90
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										90
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -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" | ||||
|  |  | |||
							
								
								
									
										11
									
								
								Cargo.toml
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								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 | ||||
|  |  | |||
|  | @ -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:?}"); | ||||
| } | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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, | ||||
|  |  | |||
							
								
								
									
										31
									
								
								src/main.rs
									
										
									
									
									
								
							
							
						
						
									
										31
									
								
								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), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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"); | ||||
| } | ||||
|  |  | |||
|  | @ -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"); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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()); | ||||
|  |  | |||
							
								
								
									
										10
									
								
								src/text.rs
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								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), | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										51
									
								
								src/transport.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/transport.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<WebSocket<MaybeTlsStream<TcpStream>>>), | ||||
| } | ||||
| 
 | ||||
| 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<T: TryInto<Packet>>(&self, command: T) -> Option<()> | ||||
|     where | ||||
|         <T as TryInto<Packet>>::Error: Debug, | ||||
|     { | ||||
|         match self { | ||||
|             Self::Udp(socket) => socket.send_command(command), | ||||
|             Self::WebSocket(socket) => { | ||||
|                 let bytes: Vec<u8> = 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), | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue