diff --git a/Cargo.lock b/Cargo.lock index 6097160..d91bde5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1643,7 +1643,7 @@ dependencies = [ [[package]] name = "servicepoint-cli" -version = "0.2.0" +version = "0.1.0" dependencies = [ "clap", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index 25daf24..7d16ac6 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.1.0" edition = "2021" rust-version = "1.80.0" publish = true diff --git a/README.md b/README.md index 439cf6d..1a8f88b 100644 --- a/README.md +++ b/README.md @@ -57,29 +57,11 @@ Options: 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. + stdin + 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 ``` @@ -89,22 +71,26 @@ Options: 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) + 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: - 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) + reset [aliases: r] + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help ``` ## Contributing diff --git a/src/cli.rs b/src/cli.rs index fa420d6..afa61f1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,9 +1,7 @@ +use crate::stream_window::StreamScreenOptions; + #[derive(clap::Parser, std::fmt::Debug)] -#[clap( - version, - arg_required_else_help = true, - about = "A command line interface for the ServicePoint display." -)] +#[clap(version, arg_required_else_help = true)] pub struct Cli { #[arg( short, @@ -28,7 +26,7 @@ pub struct Cli { #[derive(clap::Parser, std::fmt::Debug)] pub enum Mode { - #[command(visible_alias = "r", about = "Reset both pixels and brightness")] + #[command(visible_alias = "r")] ResetEverything, #[command(visible_alias = "p")] Pixels { @@ -44,40 +42,25 @@ 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", - 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, + #[command(visible_alias = "r")] + Reset, } #[derive(clap::Parser, std::fmt::Debug)] -#[clap(about = "Commands for manipulating the brightness")] pub enum BrightnessCommand { - #[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")] + #[command(visible_alias = "r")] + Reset, + #[command(visible_alias = "s")] Set { - #[arg()] brightness: u8, }, - #[command(about = "Set brightness to lowest possible level.")] Min, + Max, } #[derive(clap::ValueEnum, Clone, Debug)] @@ -88,33 +71,13 @@ 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 9859d3f..74f7db4 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::{BitVec, Brightness, Command, CompressionCode, Connection, PIXEL_COUNT}; +use servicepoint::{Brightness, Command, Connection}; pub fn execute_mode(mode: Mode, connection: Connection) { match mode { @@ -21,30 +21,15 @@ pub fn execute_mode(mode: Mode, connection: Connection) { 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::Reset => pixels_reset(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::Max => brightness_reset(connection), + BrightnessCommand::Reset => 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 e3a626a..a528281 100644 --- a/src/stream_window.rs +++ b/src/stream_window.rs @@ -1,9 +1,8 @@ -use crate::cli::StreamScreenOptions; use image::{ imageops::{dither, resize, BiLevel, FilterType}, - DynamicImage, ImageBuffer, Luma, Rgb, Rgba, + DynamicImage, ImageBuffer, Rgb, Rgba, }; -use log::{error, info, warn}; +use log::{error, warn}; use scap::{ capturer::{Capturer, Options}, frame::convert_bgra_to_rgb, @@ -14,19 +13,34 @@ use servicepoint::{ }; 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."); +#[derive(clap::Parser, std::fmt::Debug, Clone)] +pub struct StreamScreenOptions { + #[arg(long, short, default_value_t = false)] + pub no_dither: bool, +} - let capturer = match start_capture(&options) { +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); - info!("now starting to stream images"); loop { - let frame = get_next_frame(&capturer, options.no_dither); + 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); + } + for (mut dest, src) in bitmap.iter_mut().zip(frame.pixels()) { *dest = src.0[0] > u8::MAX / 2; } @@ -41,24 +55,7 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) { } } -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 { +fn start_capture() -> Option { if !scap::is_supported() { error!("platform not supported by scap"); return None; @@ -74,8 +71,11 @@ fn start_capture(options: &StreamScreenOptions) -> Option { 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 + 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");