Compare commits
	
		
			3 commits
		
	
	
		
			30b887dc03
			...
			31280abff6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						31280abff6 | ||
| 
							 | 
						ef19ab8b3f | ||
| 
							 | 
						9e299f69f6 | 
					 9 changed files with 1929 additions and 12 deletions
				
			
		
							
								
								
									
										1626
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1626
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -3,7 +3,7 @@ name = "servicepoint-cli"
 | 
				
			||||||
description = "A command line interface for the ServicePoint display."
 | 
					description = "A command line interface for the ServicePoint display."
 | 
				
			||||||
version = "0.1.0"
 | 
					version = "0.1.0"
 | 
				
			||||||
edition = "2021"
 | 
					edition = "2021"
 | 
				
			||||||
rust-version = "1.78.0"
 | 
					rust-version = "1.80.0"
 | 
				
			||||||
publish = true
 | 
					publish = true
 | 
				
			||||||
resolver = "2"
 | 
					resolver = "2"
 | 
				
			||||||
readme = "README.md"
 | 
					readme = "README.md"
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,8 @@ keywords = ["cccb", "cccb-servicepoint", "cli"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dependencies]
 | 
					[dependencies]
 | 
				
			||||||
servicepoint = { version = "0.13.0", features = ["protocol_websocket"] }
 | 
					servicepoint = { version = "0.13.0", features = ["protocol_websocket"] }
 | 
				
			||||||
clap = { version = "4.5", features = ["derive"]}
 | 
					clap = { version = "4.5", features = ["derive"] }
 | 
				
			||||||
env_logger = "0.11"
 | 
					env_logger = "0.11"
 | 
				
			||||||
log = "0.4"
 | 
					log = "0.4"
 | 
				
			||||||
 | 
					scap = "0.0.8"
 | 
				
			||||||
 | 
					image = "0.25.5"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										64
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										64
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
					@ -31,13 +31,75 @@ cd servicepoint-cli
 | 
				
			||||||
cargo run -- <args>
 | 
					cargo run -- <args>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Usage: servicepoint-cli [OPTIONS] <COMMAND>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 <DESTINATION>  ip:port of the servicepoint display [default: 127.0.0.1:2342]
 | 
				
			||||||
 | 
					  -t, --transport <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 <COMMAND>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 <COMMAND>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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 subcommands:
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Usage: servicepoint-cli pixels <COMMAND>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Commands:
 | 
				
			||||||
 | 
					  reset  [aliases: r]
 | 
				
			||||||
 | 
					  help   Print this message or the help of the given subcommand(s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Options:
 | 
				
			||||||
 | 
					  -h, --help  Print help
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Contributing
 | 
					## Contributing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you have ideas on how to improve the code, add features or improve documentation feel free to open a pull request.
 | 
					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.
 | 
					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.
 | 
					All creatures welcome.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										19
									
								
								flake.nix
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								flake.nix
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  description = "Flake for servicepoint-cli";
 | 
					  description = "Flake for command line interface of the ServicePoint display.";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  inputs = {
 | 
					  inputs = {
 | 
				
			||||||
    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
 | 
					    nixpkgs.url = "github:nixos/nixpkgs/nixos-24.11";
 | 
				
			||||||
| 
						 | 
					@ -55,12 +55,25 @@
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            nativeBuildInputs = with pkgs; [
 | 
					            nativeBuildInputs = with pkgs; [
 | 
				
			||||||
              pkg-config
 | 
					              pkg-config
 | 
				
			||||||
 | 
					              libclang
 | 
				
			||||||
 | 
					              rustPlatform.bindgenHook
 | 
				
			||||||
            ];
 | 
					            ];
 | 
				
			||||||
            strictDeps = true;
 | 
					            strictDeps = true;
 | 
				
			||||||
            buildInputs = with pkgs; [
 | 
					            buildInputs =
 | 
				
			||||||
 | 
					              with pkgs;
 | 
				
			||||||
 | 
					              [
 | 
				
			||||||
                xe
 | 
					                xe
 | 
				
			||||||
                xz
 | 
					                xz
 | 
				
			||||||
            ];
 | 
					                clang
 | 
				
			||||||
 | 
					              ]
 | 
				
			||||||
 | 
					              ++ lib.optionals pkgs.stdenv.isLinux (
 | 
				
			||||||
 | 
					                with pkgs;
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                  dbus
 | 
				
			||||||
 | 
					                  pipewire
 | 
				
			||||||
 | 
					                  libclang
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					              );
 | 
				
			||||||
          };
 | 
					          };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          default = servicepoint-cli;
 | 
					          default = servicepoint-cli;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										19
									
								
								src/cli.rs
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								src/cli.rs
									
										
									
									
									
								
							| 
						 | 
					@ -1,3 +1,5 @@
 | 
				
			||||||
 | 
					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)]
 | 
				
			||||||
pub struct Cli {
 | 
					pub struct Cli {
 | 
				
			||||||
| 
						 | 
					@ -36,6 +38,11 @@ pub enum Mode {
 | 
				
			||||||
        #[clap(subcommand)]
 | 
					        #[clap(subcommand)]
 | 
				
			||||||
        brightness_command: BrightnessCommand,
 | 
					        brightness_command: BrightnessCommand,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    #[command(visible_alias = "s")]
 | 
				
			||||||
 | 
					    Stream {
 | 
				
			||||||
 | 
					        #[clap(subcommand)]
 | 
				
			||||||
 | 
					        stream_command: StreamCommand,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(clap::Parser, std::fmt::Debug)]
 | 
					#[derive(clap::Parser, std::fmt::Debug)]
 | 
				
			||||||
| 
						 | 
					@ -62,3 +69,15 @@ pub enum Protocol {
 | 
				
			||||||
    WebSocket,
 | 
					    WebSocket,
 | 
				
			||||||
    Fake,
 | 
					    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,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,6 @@
 | 
				
			||||||
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;
 | 
					use log::info;
 | 
				
			||||||
use servicepoint::{Brightness, Command, Connection};
 | 
					use servicepoint::{Brightness, Command, Connection};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +12,10 @@ pub fn execute_mode(mode: Mode, connection: Connection) {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Mode::Pixels { pixel_command } => pixels(&connection, pixel_command),
 | 
					        Mode::Pixels { pixel_command } => pixels(&connection, pixel_command),
 | 
				
			||||||
        Mode::Brightness { brightness_command } => brightness(&connection, brightness_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),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,8 @@ use servicepoint::Connection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod cli;
 | 
					mod cli;
 | 
				
			||||||
mod execute;
 | 
					mod execute;
 | 
				
			||||||
 | 
					mod stream_stdin;
 | 
				
			||||||
 | 
					mod stream_window;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() {
 | 
					fn main() {
 | 
				
			||||||
    let cli = Cli::parse();
 | 
					    let cli = Cli::parse();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										81
									
								
								src/stream_stdin.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/stream_stdin.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,81 @@
 | 
				
			||||||
 | 
					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.");
 | 
				
			||||||
 | 
					    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::<TILE_WIDTH>() {
 | 
				
			||||||
 | 
					            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");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										112
									
								
								src/stream_window.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								src/stream_window.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,112 @@
 | 
				
			||||||
 | 
					use image::{
 | 
				
			||||||
 | 
					    imageops::{dither, resize, BiLevel, FilterType},
 | 
				
			||||||
 | 
					    DynamicImage, ImageBuffer, Rgb, Rgba,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					use log::{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::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() {
 | 
				
			||||||
 | 
					        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.no_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<Capturer> {
 | 
				
			||||||
 | 
					    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)) 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::<Rgba<_>, _>::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::<Rgb<_>, _>::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<u8>) -> DynamicImage {
 | 
				
			||||||
 | 
					    DynamicImage::from(
 | 
				
			||||||
 | 
					        ImageBuffer::<Rgb<_>, _>::from_raw(width as u32, height as u32, convert_bgra_to_rgb(data))
 | 
				
			||||||
 | 
					            .unwrap(),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue