Add support for binary pixels PB

This commit is contained in:
timvisee
2023-12-28 18:57:45 +01:00
parent bf9819e7d8
commit a9a2895b62
5 changed files with 91 additions and 38 deletions

View File

@@ -23,7 +23,8 @@ impl<'a: 'b, 'b> ArgHandler<'a> {
.help("The host to pwn \"host:port\"") .help("The host to pwn \"host:port\"")
.required(true) .required(true)
.index(1), .index(1),
).arg( )
.arg(
Arg::with_name("image") Arg::with_name("image")
.short("i") .short("i")
.long("image") .long("image")
@@ -34,7 +35,8 @@ impl<'a: 'b, 'b> ArgHandler<'a> {
.multiple(true) .multiple(true)
.display_order(1) .display_order(1)
.takes_value(true), .takes_value(true),
).arg( )
.arg(
Arg::with_name("width") Arg::with_name("width")
.short("w") .short("w")
.long("width") .long("width")
@@ -42,7 +44,8 @@ impl<'a: 'b, 'b> ArgHandler<'a> {
.help("Draw width (def: screen width)") .help("Draw width (def: screen width)")
.display_order(2) .display_order(2)
.takes_value(true), .takes_value(true),
).arg( )
.arg(
Arg::with_name("height") Arg::with_name("height")
.short("h") .short("h")
.long("height") .long("height")
@@ -50,21 +53,24 @@ impl<'a: 'b, 'b> ArgHandler<'a> {
.help("Draw height (def: screen height)") .help("Draw height (def: screen height)")
.display_order(3) .display_order(3)
.takes_value(true), .takes_value(true),
).arg( )
.arg(
Arg::with_name("x") Arg::with_name("x")
.short("x") .short("x")
.value_name("PIXELS") .value_name("PIXELS")
.help("Draw X offset (def: 0)") .help("Draw X offset (def: 0)")
.display_order(4) .display_order(4)
.takes_value(true), .takes_value(true),
).arg( )
.arg(
Arg::with_name("y") Arg::with_name("y")
.short("y") .short("y")
.value_name("PIXELS") .value_name("PIXELS")
.help("Draw Y offset (def: 0)") .help("Draw Y offset (def: 0)")
.display_order(5) .display_order(5)
.takes_value(true), .takes_value(true),
).arg( )
.arg(
Arg::with_name("count") Arg::with_name("count")
.short("c") .short("c")
.long("count") .long("count")
@@ -74,7 +80,8 @@ impl<'a: 'b, 'b> ArgHandler<'a> {
.help("Number of concurrent threads (def: CPUs)") .help("Number of concurrent threads (def: CPUs)")
.display_order(6) .display_order(6)
.takes_value(true), .takes_value(true),
).arg( )
.arg(
Arg::with_name("fps") Arg::with_name("fps")
.short("r") .short("r")
.long("fps") .long("fps")
@@ -82,7 +89,16 @@ impl<'a: 'b, 'b> ArgHandler<'a> {
.help("Frames per second with multiple images (def: 1)") .help("Frames per second with multiple images (def: 1)")
.display_order(7) .display_order(7)
.takes_value(true), .takes_value(true),
).get_matches(); )
.arg(
Arg::with_name("binary")
.short("b")
.long("binary")
.help("Use binary mode to set pixels (PB)")
.display_order(7)
.takes_value(false),
)
.get_matches();
// Instantiate // Instantiate
ArgHandler { matches } ArgHandler { matches }
@@ -147,4 +163,9 @@ impl<'a: 'b, 'b> ArgHandler<'a> {
.map(|fps| fps.parse::<u32>().expect("Invalid frames per second rate")) .map(|fps| fps.parse::<u32>().expect("Invalid frames per second rate"))
.unwrap_or(DEFAULT_IMAGE_FPS) .unwrap_or(DEFAULT_IMAGE_FPS)
} }
/// Whether to use binary mode.
pub fn binary(&self) -> bool {
self.matches.is_present("binary")
}
} }

View File

@@ -3,10 +3,10 @@
/// Represents a color with RGB values from 0 to 255. /// Represents a color with RGB values from 0 to 255.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Color { pub struct Color {
r: u8, pub(crate) r: u8,
g: u8, pub(crate) g: u8,
b: u8, pub(crate) b: u8,
a: u8, pub(crate) a: u8,
} }
impl Color { impl Color {
@@ -14,7 +14,7 @@ impl Color {
/// ///
/// The color channels must be between 0 and 255. /// The color channels must be between 0 and 255.
pub fn from(r: u8, g: u8, b: u8, a: u8) -> Color { pub fn from(r: u8, g: u8, b: u8, a: u8) -> Color {
Color { r, g, b, a} Color { r, g, b, a }
} }
/// Get a hexadecimal representation of the color, /// Get a hexadecimal representation of the color,

View File

@@ -32,8 +32,8 @@ fn start<'a>(arg_handler: &ArgHandler<'a>) {
println!("Starting... (use CTRL+C to stop)"); println!("Starting... (use CTRL+C to stop)");
// Gather facts about the host // Gather facts about the host
let screen_size = gather_host_facts(&arg_handler) let screen_size =
.expect("Failed to gather facts about pixelflut server"); gather_host_facts(&arg_handler).expect("Failed to gather facts about pixelflut server");
// Determine the size to use // Determine the size to use
let size = arg_handler.size(Some(screen_size)); let size = arg_handler.size(Some(screen_size));
@@ -44,6 +44,7 @@ fn start<'a>(arg_handler: &ArgHandler<'a>) {
arg_handler.count(), arg_handler.count(),
size, size,
arg_handler.offset(), arg_handler.offset(),
arg_handler.binary(),
); );
// Load the image manager // Load the image manager
@@ -56,7 +57,7 @@ fn start<'a>(arg_handler: &ArgHandler<'a>) {
/// Gather important facts about the host. /// Gather important facts about the host.
fn gather_host_facts(arg_handler: &ArgHandler) -> Result<(u32, u32), Error> { fn gather_host_facts(arg_handler: &ArgHandler) -> Result<(u32, u32), Error> {
// Set up a client, and get the screen size // Set up a client, and get the screen size
let size = Client::connect(arg_handler.host().to_string())?.read_screen_size()?; let size = Client::connect(arg_handler.host().to_string(), false)?.read_screen_size()?;
// Print status // Print status
println!("Gathered screen size: {}x{}", size.0, size.1); println!("Gathered screen size: {}x{}", size.0, size.1);

View File

@@ -1,6 +1,6 @@
use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::mpsc::{self, Receiver, Sender};
use std::thread::sleep;
use std::thread; use std::thread;
use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use image::DynamicImage; use image::DynamicImage;
@@ -21,7 +21,13 @@ pub struct Canvas {
impl Canvas { impl Canvas {
/// Create a new pixelflut canvas. /// Create a new pixelflut canvas.
pub fn new(host: &str, painter_count: usize, size: (u32, u32), offset: (u32, u32)) -> Canvas { pub fn new(
host: &str,
painter_count: usize,
size: (u32, u32),
offset: (u32, u32),
binary: bool,
) -> Canvas {
// Initialize the object // Initialize the object
let mut canvas = Canvas { let mut canvas = Canvas {
host: host.to_string(), host: host.to_string(),
@@ -35,14 +41,14 @@ impl Canvas {
println!("Starting painter threads..."); println!("Starting painter threads...");
// Spawn some painters // Spawn some painters
canvas.spawn_painters(); canvas.spawn_painters(binary);
// Return the canvas // Return the canvas
canvas canvas
} }
/// Spawn the painters for this canvas /// Spawn the painters for this canvas
fn spawn_painters(&mut self) { fn spawn_painters(&mut self, binary: bool) {
// Spawn some painters // Spawn some painters
for i in 0..self.painter_count { for i in 0..self.painter_count {
// Determine the slice width // Determine the slice width
@@ -52,12 +58,12 @@ impl Canvas {
let painter_area = Rect::from((i as u32) * width, 0, width, self.size.1); let painter_area = Rect::from((i as u32) * width, 0, width, self.size.1);
// Spawn the painter // Spawn the painter
self.spawn_painter(painter_area); self.spawn_painter(painter_area, binary);
} }
} }
/// Spawn a single painter in a thread. /// Spawn a single painter in a thread.
fn spawn_painter(&mut self, area: Rect) { fn spawn_painter(&mut self, area: Rect, binary: bool) {
// Get the host that will be used // Get the host that will be used
let host = self.host.to_string(); let host = self.host.to_string();
@@ -76,12 +82,12 @@ impl Canvas {
// The painting loop // The painting loop
'paint: loop { 'paint: loop {
// Connect // Connect
let client = match Client::connect(host.clone()) { let client = match Client::connect(host.clone(), binary) {
Ok(client) => client, Ok(client) => client,
Err(e) => { Err(e) => {
eprintln!("Painter failed to connect: {}", e); eprintln!("Painter failed to connect: {}", e);
break 'paint; break 'paint;
}, }
}; };
painter.set_client(Some(client)); painter.set_client(Some(client));

View File

@@ -27,33 +27,57 @@ const PIX_SERVER_SIZE_REGEX: &str = r"^(?i)\s*SIZE\s+([[:digit:]]+)\s+([[:digit:
/// to the pixelflut panel. /// to the pixelflut panel.
pub struct Client { pub struct Client {
stream: BufStream<TcpStream>, stream: BufStream<TcpStream>,
/// Whether to use binary mode (PB) instead of (PX).
binary: bool,
} }
impl Client { impl Client {
/// Create a new client instance. /// Create a new client instance.
pub fn new(stream: TcpStream) -> Client { pub fn new(stream: TcpStream, binary: bool) -> Client {
Client { Client {
stream: BufStream::new(stream), stream: BufStream::new(stream),
binary,
} }
} }
/// Create a new client instane from the given host, and connect to it. /// Create a new client instane from the given host, and connect to it.
pub fn connect(host: String) -> Result<Client, Error> { pub fn connect(host: String, binary: bool) -> Result<Client, Error> {
// Create a new stream, and instantiate the client // Create a new stream, and instantiate the client
Ok(Client::new(create_stream(host)?)) Ok(Client::new(create_stream(host)?, binary))
} }
/// Write a pixel to the given stream. /// Write a pixel to the given stream.
pub fn write_pixel(&mut self, x: u32, y: u32, color: Color) -> Result<(), Error> { pub fn write_pixel(&mut self, x: u32, y: u32, color: Color) -> Result<(), Error> {
// Write the command to set a pixel if self.binary {
self.write_command(&format!("PX {} {} {}", x, y, color.as_hex())) self.write_command(
&[
b'P',
b'B',
x as u8,
(x >> 8) as u8,
y as u8,
(y >> 8) as u8,
color.r,
color.g,
color.b,
color.a,
],
false,
)
} else {
self.write_command(
format!("PX {} {} {}", x, y, color.as_hex()).as_bytes(),
true,
)
}
} }
/// Read the size of the screen. /// Read the size of the screen.
pub fn read_screen_size(&mut self) -> Result<(u32, u32), Error> { pub fn read_screen_size(&mut self) -> Result<(u32, u32), Error> {
// Read the screen size // Read the screen size
let data = self let data = self
.write_read_command("SIZE".into()) .write_read_command(b"SIZE")
.expect("Failed to read screen size"); .expect("Failed to read screen size");
// Build a regex to parse the screen size // Build a regex to parse the screen size
@@ -77,26 +101,27 @@ impl Client {
} }
/// Write the given command to the given stream. /// Write the given command to the given stream.
fn write_command(&mut self, cmd: &str) -> Result<(), Error> { fn write_command(&mut self, cmd: &[u8], newline: bool) -> Result<(), Error> {
// Write the pixels and a new line // Write the pixels and a new line
self.stream.write_all(cmd.as_bytes())?; self.stream.write_all(cmd)?;
self.stream.write_all(b"\n")?; if newline {
self.stream.write_all(b"\n")?;
}
// Flush, make sure to clear the send buffer // Flush, make sure to clear the send buffer
// TODO: only flush each 100 pixels? // TODO: only flush each 100 pixels?
// TODO: make flushing configurable? // TODO: make flushing configurable?
// TODO: make buffer size configurable? // TODO: make buffer size configurable?
self.stream self.stream.flush()?;
.flush()?;
// Everything seems to be ok // Everything seems to be ok
Ok(()) Ok(())
} }
/// Write the given command to the given stream, and read the output. /// Write the given command to the given stream, and read the output.
fn write_read_command(&mut self, cmd: &str) -> Result<String, Error> { fn write_read_command(&mut self, cmd: &[u8]) -> Result<String, Error> {
// Write the command // Write the command
self.write_command(cmd)?; self.write_command(cmd, true)?;
// Flush the pipe, ensure the command is actually sent // Flush the pipe, ensure the command is actually sent
self.stream.flush()?; self.stream.flush()?;
@@ -114,7 +139,7 @@ impl Client {
impl Drop for Client { impl Drop for Client {
/// Nicely drop the connection when the client is disconnected. /// Nicely drop the connection when the client is disconnected.
fn drop(&mut self) { fn drop(&mut self) {
let _ = self.write_command("\nQUIT".into()); let _ = self.write_command(b"\nQUIT", true);
} }
} }