add invert, more help text, ...
choose if pointer is visible to make dithering more stable
This commit is contained in:
		
							parent
							
								
									83baf7b419
								
							
						
					
					
						commit
						b770607893
					
				
					 4 changed files with 125 additions and 59 deletions
				
			
		
							
								
								
									
										40
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										40
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
					@ -57,11 +57,29 @@ Options:
 | 
				
			||||||
Usage: servicepoint-cli stream <COMMAND>
 | 
					Usage: servicepoint-cli stream <COMMAND>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Commands:
 | 
					Commands:
 | 
				
			||||||
  stdin   
 | 
					  stdin   Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin`
 | 
				
			||||||
  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.
 | 
				
			||||||
  help    Print this message or the help of the given subcommand(s)
 | 
					  help    Print this message or the help of the given subcommand(s)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Screen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Usage: servicepoint-cli stream screen [OPTIONS]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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
 | 
					  -h, --help  Print help
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,26 +89,22 @@ Options:
 | 
				
			||||||
Usage: servicepoint-cli brightness <COMMAND>
 | 
					Usage: servicepoint-cli brightness <COMMAND>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Commands:
 | 
					Commands:
 | 
				
			||||||
  reset  [aliases: r]
 | 
					  max   Reset brightness to the default (max) level [aliases: r, reset]
 | 
				
			||||||
  set    [aliases: s]
 | 
					  set   Set one brightness for the whole screen [aliases: s]
 | 
				
			||||||
  min    
 | 
					  min   Set brightness to lowest possible level.
 | 
				
			||||||
  max    
 | 
					 | 
				
			||||||
  help  Print this message or the help of the given subcommand(s)
 | 
					  help  Print this message or the help of the given subcommand(s)
 | 
				
			||||||
 | 
					 | 
				
			||||||
Options:
 | 
					 | 
				
			||||||
  -h, --help  Print help
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Pixels
 | 
					### Pixels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
Usage: servicepoint-cli pixels <COMMAND>
 | 
					Usage: servicepoint-cli pixels <COMMAND>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Commands:
 | 
					Commands:
 | 
				
			||||||
  reset  [aliases: r]
 | 
					  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)
 | 
					  help    Print this message or the help of the given subcommand(s)
 | 
				
			||||||
 | 
					 | 
				
			||||||
Options:
 | 
					 | 
				
			||||||
  -h, --help  Print help
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Contributing
 | 
					## Contributing
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										59
									
								
								src/cli.rs
									
										
									
									
									
								
							
							
						
						
									
										59
									
								
								src/cli.rs
									
										
									
									
									
								
							| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
use crate::stream_window::StreamScreenOptions;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(clap::Parser, std::fmt::Debug)]
 | 
					#[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 {
 | 
					pub struct Cli {
 | 
				
			||||||
    #[arg(
 | 
					    #[arg(
 | 
				
			||||||
        short,
 | 
					        short,
 | 
				
			||||||
| 
						 | 
					@ -26,7 +28,7 @@ pub struct Cli {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(clap::Parser, std::fmt::Debug)]
 | 
					#[derive(clap::Parser, std::fmt::Debug)]
 | 
				
			||||||
pub enum Mode {
 | 
					pub enum Mode {
 | 
				
			||||||
    #[command(visible_alias = "r")]
 | 
					    #[command(visible_alias = "r", about = "Reset both pixels and brightness")]
 | 
				
			||||||
    ResetEverything,
 | 
					    ResetEverything,
 | 
				
			||||||
    #[command(visible_alias = "p")]
 | 
					    #[command(visible_alias = "p")]
 | 
				
			||||||
    Pixels {
 | 
					    Pixels {
 | 
				
			||||||
| 
						 | 
					@ -42,25 +44,40 @@ pub enum Mode {
 | 
				
			||||||
    Stream {
 | 
					    Stream {
 | 
				
			||||||
        #[clap(subcommand)]
 | 
					        #[clap(subcommand)]
 | 
				
			||||||
        stream_command: StreamCommand,
 | 
					        stream_command: StreamCommand,
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(clap::Parser, std::fmt::Debug)]
 | 
					#[derive(clap::Parser, std::fmt::Debug)]
 | 
				
			||||||
 | 
					#[clap(about = "Commands for manipulating pixels")]
 | 
				
			||||||
pub enum PixelCommand {
 | 
					pub enum PixelCommand {
 | 
				
			||||||
    #[command(visible_alias = "r")]
 | 
					    #[command(
 | 
				
			||||||
    Reset,
 | 
					        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)]
 | 
					#[derive(clap::Parser, std::fmt::Debug)]
 | 
				
			||||||
 | 
					#[clap(about = "Commands for manipulating the brightness")]
 | 
				
			||||||
pub enum BrightnessCommand {
 | 
					pub enum BrightnessCommand {
 | 
				
			||||||
    #[command(visible_alias = "r")]
 | 
					    #[command(
 | 
				
			||||||
    Reset,
 | 
					        visible_alias = "r",
 | 
				
			||||||
    #[command(visible_alias = "s")]
 | 
					        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 {
 | 
					    Set {
 | 
				
			||||||
 | 
					        #[arg()]
 | 
				
			||||||
        brightness: u8,
 | 
					        brightness: u8,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    #[command(about = "Set brightness to lowest possible level.")]
 | 
				
			||||||
    Min,
 | 
					    Min,
 | 
				
			||||||
    Max,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(clap::ValueEnum, Clone, Debug)]
 | 
					#[derive(clap::ValueEnum, Clone, Debug)]
 | 
				
			||||||
| 
						 | 
					@ -71,13 +88,33 @@ pub enum Protocol {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(clap::Parser, std::fmt::Debug)]
 | 
					#[derive(clap::Parser, std::fmt::Debug)]
 | 
				
			||||||
 | 
					#[clap(about = "Continuously send data to the display")]
 | 
				
			||||||
pub enum StreamCommand {
 | 
					pub enum StreamCommand {
 | 
				
			||||||
 | 
					    #[clap(
 | 
				
			||||||
 | 
					        about = "Pipe text to the display, example: `journalctl | servicepoint-cli stream stdin`"
 | 
				
			||||||
 | 
					    )]
 | 
				
			||||||
    Stdin {
 | 
					    Stdin {
 | 
				
			||||||
        #[arg(long, short, default_value_t = false)]
 | 
					        #[arg(long, short, default_value_t = false)]
 | 
				
			||||||
        slow: bool,
 | 
					        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 {
 | 
					    Screen {
 | 
				
			||||||
        #[command(flatten)]
 | 
					        #[command(flatten)]
 | 
				
			||||||
        options: StreamScreenOptions,
 | 
					        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,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ use crate::cli::{BrightnessCommand, Mode, PixelCommand, StreamCommand};
 | 
				
			||||||
use crate::stream_stdin::stream_stdin;
 | 
					use crate::stream_stdin::stream_stdin;
 | 
				
			||||||
use crate::stream_window::stream_window;
 | 
					use crate::stream_window::stream_window;
 | 
				
			||||||
use log::info;
 | 
					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) {
 | 
					pub fn execute_mode(mode: Mode, connection: Connection) {
 | 
				
			||||||
    match mode {
 | 
					    match mode {
 | 
				
			||||||
| 
						 | 
					@ -21,15 +21,30 @@ pub fn execute_mode(mode: Mode, connection: Connection) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn pixels(connection: &Connection, pixel_command: PixelCommand) {
 | 
					fn pixels(connection: &Connection, pixel_command: PixelCommand) {
 | 
				
			||||||
    match pixel_command {
 | 
					    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) {
 | 
					fn brightness(connection: &Connection, brightness_command: BrightnessCommand) {
 | 
				
			||||||
    match brightness_command {
 | 
					    match brightness_command {
 | 
				
			||||||
        BrightnessCommand::Reset => brightness_reset(connection),
 | 
					        BrightnessCommand::Max => brightness_reset(connection),
 | 
				
			||||||
        BrightnessCommand::Min => brightness_set(connection, Brightness::MIN),
 | 
					        BrightnessCommand::Min => brightness_set(connection, Brightness::MIN),
 | 
				
			||||||
        BrightnessCommand::Max => brightness_set(connection, Brightness::MAX),
 | 
					 | 
				
			||||||
        BrightnessCommand::Set { brightness } => {
 | 
					        BrightnessCommand::Set { brightness } => {
 | 
				
			||||||
            brightness_set(connection, Brightness::saturating_from(brightness))
 | 
					            brightness_set(connection, Brightness::saturating_from(brightness))
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,9 @@
 | 
				
			||||||
 | 
					use crate::cli::StreamScreenOptions;
 | 
				
			||||||
use image::{
 | 
					use image::{
 | 
				
			||||||
    imageops::{dither, resize, BiLevel, FilterType},
 | 
					    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::{
 | 
					use scap::{
 | 
				
			||||||
    capturer::{Capturer, Options},
 | 
					    capturer::{Capturer, Options},
 | 
				
			||||||
    frame::convert_bgra_to_rgb,
 | 
					    frame::convert_bgra_to_rgb,
 | 
				
			||||||
| 
						 | 
					@ -13,34 +14,19 @@ use servicepoint::{
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use std::time::Duration;
 | 
					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) {
 | 
					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,
 | 
					        Some(value) => value,
 | 
				
			||||||
        None => return,
 | 
					        None => return,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut bitmap = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | 
					    let mut bitmap = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
 | 
				
			||||||
 | 
					    info!("now starting to stream images");
 | 
				
			||||||
    loop {
 | 
					    loop {
 | 
				
			||||||
        let frame = capturer.get_next_frame().expect("failed to capture frame");
 | 
					        let frame = get_next_frame(&capturer, options.no_dither);
 | 
				
			||||||
        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()) {
 | 
					        for (mut dest, src) in bitmap.iter_mut().zip(frame.pixels()) {
 | 
				
			||||||
            *dest = src.0[0] > u8::MAX / 2;
 | 
					            *dest = src.0[0] > u8::MAX / 2;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -55,7 +41,24 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn start_capture() -> Option<Capturer> {
 | 
					fn get_next_frame(capturer: &Capturer, no_dither: bool) -> ImageBuffer<Luma<u8>, Vec<u8>> {
 | 
				
			||||||
 | 
					    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<Capturer> {
 | 
				
			||||||
    if !scap::is_supported() {
 | 
					    if !scap::is_supported() {
 | 
				
			||||||
        error!("platform not supported by scap");
 | 
					        error!("platform not supported by scap");
 | 
				
			||||||
        return None;
 | 
					        return None;
 | 
				
			||||||
| 
						 | 
					@ -71,11 +74,8 @@ fn start_capture() -> Option<Capturer> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut capturer = Capturer::build(Options {
 | 
					    let mut capturer = Capturer::build(Options {
 | 
				
			||||||
        fps: FRAME_PACING.div_duration_f32(Duration::from_secs(1)) as u32,
 | 
					        fps: FRAME_PACING.div_duration_f32(Duration::from_secs(1)) as u32,
 | 
				
			||||||
        target: None,
 | 
					        show_cursor: options.pointer,
 | 
				
			||||||
        show_cursor: true,
 | 
					        output_type: scap::frame::FrameType::BGR0, // this is more like a suggestion
 | 
				
			||||||
        show_highlight: true,
 | 
					 | 
				
			||||||
        excluded_targets: None,
 | 
					 | 
				
			||||||
        output_type: scap::frame::FrameType::BGR0,
 | 
					 | 
				
			||||||
        ..Default::default()
 | 
					        ..Default::default()
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    .expect("failed to create screen capture");
 | 
					    .expect("failed to create screen capture");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue