From 98a571f6556748951bbdaa1412e0d9e762d5aadf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Vis=C3=A9e?= Date: Sat, 30 Dec 2017 23:43:37 +0100 Subject: [PATCH] Spread all components over modules --- src/arg_handler.rs | 135 +++++++++++ src/color.rs | 7 + src/image_manager.rs | 82 +++++++ src/main.rs | 515 ++---------------------------------------- src/painter.rs | 82 +++++++ src/painter_handle.rs | 48 ++++ src/pix_canvas.rs | 140 ++++++++++++ src/pix_client.rs | 75 ++++++ 8 files changed, 585 insertions(+), 499 deletions(-) create mode 100644 src/arg_handler.rs create mode 100644 src/image_manager.rs create mode 100644 src/painter.rs create mode 100644 src/painter_handle.rs create mode 100644 src/pix_canvas.rs create mode 100644 src/pix_client.rs diff --git a/src/arg_handler.rs b/src/arg_handler.rs new file mode 100644 index 0000000..75aa238 --- /dev/null +++ b/src/arg_handler.rs @@ -0,0 +1,135 @@ +extern crate clap; + +use clap::{Arg, ArgMatches, App}; + +use app::*; + + + +/// CLI argument handler. +pub struct ArgHandler<'a> { + matches: ArgMatches<'a>, +} + +impl<'a: 'b, 'b> ArgHandler<'a> { + + /// Parse CLI arguments. + pub fn parse() -> ArgHandler<'a> { + // Handle/parse arguments + let matches = App::new(APP_NAME) + .version(APP_VERSION) + .author(APP_AUTHOR) + .about(APP_ABOUT) + .arg(Arg::with_name("HOST") + .help("The host to pwn \"host:port\"") + .required(true) + .index(1)) + .arg(Arg::with_name("count") + .short("c") + .long("count") + .value_name("COUNT") + .help("Number of simultanious threads (def: 4)") + .takes_value(true)) + .arg(Arg::with_name("image") + .short("i") + .long("images") + .value_name("PATH") + .help("Paths of the images to print") + .required(true) + .multiple(true) + .takes_value(true)) + .arg(Arg::with_name("width") + .short("w") + .long("width") + .value_name("PIXELS") + .help("Drawing width in pixels (def: 1920)") + .takes_value(true)) + .arg(Arg::with_name("height") + .short("h") + .long("height") + .value_name("PIXELS") + .help("Drawing height in pixels (def: 1080)") + .takes_value(true)) + .arg(Arg::with_name("x") + .short("x") + .long("x") + .value_name("PIXELS") + .help("Drawing X offset in pixels (def: 0)") + .takes_value(true)) + .arg(Arg::with_name("y") + .short("y") + .long("y") + .value_name("PIXELS") + .help("Drawing Y offset in pixels (def: 0)") + .takes_value(true)) + .arg(Arg::with_name("fps") + .short("r") + .long("fps") + .value_name("RATE") + .help("Frames per second with multiple images (def: 1)") + .takes_value(true)) + .get_matches(); + + // Instantiate + ArgHandler { + matches, + } + } + + /// Get the host property. + pub fn host(&'a self) -> &'b str { + self.matches.value_of("HOST") + .expect("Please specify a host") + } + + /// Get the thread count. + pub fn count(&self) -> usize { + self.matches.value_of("count") + .unwrap_or(&format!("{}", DEFAULT_THREAD_COUNT)) + .parse::() + .expect("Invalid count specified") + } + + /// Get the image paths. + pub fn image_paths(&'a self) -> Vec<&'b str> { + self.matches.values_of("image") + .expect("Please specify an image paths") + .collect() + } + + /// Get the image size. + pub fn size(&self) -> (u32, u32) { + ( + self.matches.value_of("width") + .unwrap_or(&format!("{}", DEFAULT_IMAGE_WIDTH)) + .parse::() + .expect("Invalid image width"), + self.matches.value_of("height") + .unwrap_or(&format!("{}", DEFAULT_IMAGE_HEIGHT)) + .parse::() + .expect("Invalid image height"), + ) + } + + /// Get the image offset. + pub fn offset(&self) -> (u32, u32) { + ( + self.matches.value_of("x") + .unwrap_or("0") + .parse::() + .expect("Invalid X offset"), + self.matches.value_of("y") + .unwrap_or("0") + .parse::() + .expect("Invalid Y offset"), + ) + } + + /// Get the FPS. + pub fn fps(&self) -> u32 { + self.matches.value_of("fps") + .unwrap_or(&format!("{}", DEFAULT_IMAGE_FPS)) + .parse::() + .expect("Invalid frames per second rate") + } +} diff --git a/src/color.rs b/src/color.rs index 82e2f87..09eec61 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,6 +1,13 @@ /// Color struct. /// /// Represents a color with RGB values from 0 to 255. +#[derive(Copy, Clone)] +pub struct Color { + r: u8, + g: u8, + b: u8, +} + impl Color { /// Constructor. diff --git a/src/image_manager.rs b/src/image_manager.rs new file mode 100644 index 0000000..c4e9457 --- /dev/null +++ b/src/image_manager.rs @@ -0,0 +1,82 @@ +use std::path::Path; + +use image; +use image::{DynamicImage, FilterType}; + +use pix_canvas::PixCanvas; + + + +/// A manager that manages all images to print. +pub struct ImageManager { + images: Vec, + index: isize, +} + +impl ImageManager { + /// Intantiate the image manager. + pub fn from(images: Vec) -> ImageManager { + ImageManager { + images, + index: 0, + } + } + + /// Instantiate the image manager, and load the images from the given paths. + pub fn load(paths: Vec<&str>, size: &(u32, u32)) -> ImageManager { + // Show a status message + println!("Load and process {} image(s)...", paths.len()); + + // Load the images from the paths + let image_manager = ImageManager::from( + paths.iter() + .map(|path| load_image(path, &size)) + .collect() + ); + + // We succeeded + println!("All images have been loaded successfully"); + + image_manager + } + + /// Tick the image + pub fn tick(&mut self, canvas: &mut PixCanvas) { + // Get the image index bound + let bound = self.images.len(); + + // Get the image to use + let image = &mut self.images[ + self.index as usize % bound + ]; + + // Update the image on the canvas + canvas.update_image(image); + + // Increase the index + self.index += 1; + } +} + + + +/// Load the image at the given path, and size it correctly +fn load_image(path: &str, size: &(u32, u32)) -> DynamicImage { + // Create a path instance + let path = Path::new(&path); + + // Check whether the path exists + if !path.is_file() { + panic!("The given path does not exist or is not a file"); + } + + // Load the image + let image = image::open(&path).unwrap(); + + // Resize the image to fit the screen + image.resize_exact( + size.0, + size.1, + FilterType::Gaussian, + ) +} diff --git a/src/main.rs b/src/main.rs index f56fc1e..68c9487 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,154 +1,41 @@ -extern crate bufstream; extern crate clap; extern crate image; mod app; +mod arg_handler; mod color; +mod image_manager; +mod painter; +mod painter_handle; +mod pix_canvas; +mod pix_client; mod rect; -use std::io::Error; -use std::io::prelude::*; -use std::net::TcpStream; -use std::path::Path; -use std::sync::mpsc; -use std::sync::mpsc::{Sender, Receiver}; use std::thread; -use std::thread::JoinHandle; use std::time::Duration; -use bufstream::BufStream; -use clap::{Arg, ArgMatches, App}; -use image::{DynamicImage, FilterType, Pixel}; - -use app::*; -use color::Color; -use rect::Rect; +use arg_handler::ArgHandler; +use image_manager::ImageManager; +use pix_canvas::PixCanvas; /// Main application entrypoint. fn main() { // Parse CLI arguments - let matches = parse_args(); - - // Get the host - let host = matches - .value_of("HOST") - .expect("Please specify a host"); - - // Get the count - let count = matches - .value_of("count") - .unwrap_or(&format!("{}", DEFAULT_THREAD_COUNT)) - .parse::() - .expect("Invalid count specified"); - - // Get the image path - let image_paths = matches - .values_of("image") - .expect("Please specify an image paths") - .collect(); - - // Get the width and height - let width = matches - .value_of("width") - .unwrap_or(&format!("{}", DEFAULT_IMAGE_WIDTH)) - .parse::() - .expect("Invalid image width"); - let height = matches - .value_of("height") - .unwrap_or(&format!("{}", DEFAULT_IMAGE_HEIGHT)) - .parse::() - .expect("Invalid image height"); - - // Get the offset - let offset_x = matches - .value_of("x") - .unwrap_or("0") - .parse::() - .expect("Invalid X offset"); - let offset_y = matches - .value_of("y") - .unwrap_or("0") - .parse::() - .expect("Invalid Y offset"); - - // Get the FPS rate - let fps = matches - .value_of("fps") - .unwrap_or(&format!("{}", DEFAULT_IMAGE_FPS)) - .parse::() - .expect("Invalid frames per second rate"); + let arg_handler = ArgHandler::parse(); // Start start( - host, - image_paths, - fps, - count, - (width, height), - (offset_x, offset_y) + arg_handler.host(), + arg_handler.image_paths(), + arg_handler.fps(), + arg_handler.count(), + arg_handler.size(), + arg_handler.offset(), ); } -/// Parse CLI arguments, return the matches. -fn parse_args<'a>() -> ArgMatches<'a> { - // Handle/parse arguments - App::new(APP_NAME) - .version(APP_VERSION) - .author(APP_AUTHOR) - .about(APP_ABOUT) - .arg(Arg::with_name("HOST") - .help("The host to pwn \"host:port\"") - .required(true) - .index(1)) - .arg(Arg::with_name("count") - .short("c") - .long("count") - .value_name("COUNT") - .help("Number of simultanious threads (def: 4)") - .takes_value(true)) - .arg(Arg::with_name("image") - .short("i") - .long("images") - .value_name("PATH") - .help("Paths of the images to print") - .required(true) - .multiple(true) - .takes_value(true)) - .arg(Arg::with_name("width") - .short("w") - .long("width") - .value_name("PIXELS") - .help("Drawing width in pixels (def: 1920)") - .takes_value(true)) - .arg(Arg::with_name("height") - .short("h") - .long("height") - .value_name("PIXELS") - .help("Drawing height in pixels (def: 1080)") - .takes_value(true)) - .arg(Arg::with_name("x") - .short("x") - .long("x") - .value_name("PIXELS") - .help("Drawing X offset in pixels (def: 0)") - .takes_value(true)) - .arg(Arg::with_name("y") - .short("y") - .long("y") - .value_name("PIXELS") - .help("Drawing Y offset in pixels (def: 0)") - .takes_value(true)) - .arg(Arg::with_name("fps") - .short("r") - .long("fps") - .value_name("RATE") - .help("Frames per second with multiple images (def: 1)") - .takes_value(true)) - .get_matches() -} - /// Start the client. fn start( host: &str, @@ -180,373 +67,3 @@ fn start( ); } } - -/// Create a stream to talk to the pixelflut server. -/// -/// The stream is returned as result. -fn create_stream(host: String) -> Result { - TcpStream::connect(host) -} - -/// Load the image at the given path, and size it correctly -fn load_image(path: &str, size: &(u32, u32)) -> DynamicImage { - // Create a path instance - let path = Path::new(&path); - - // Check whether the path exists - if !path.is_file() { - panic!("The given path does not exist or is not a file"); - } - - // Load the image - let image = image::open(&path).unwrap(); - - // Resize the image to fit the screen - image.resize_exact( - size.0, - size.1, - FilterType::Gaussian, - ) -} - - - -/// A manager that manages all images to print. -struct ImageManager { - images: Vec, - index: isize, -} - -impl ImageManager { - /// Intantiate the image manager. - pub fn from(images: Vec) -> ImageManager { - ImageManager { - images, - index: 0, - } - } - - /// Instantiate the image manager, and load the images from the given paths. - pub fn load(paths: Vec<&str>, size: &(u32, u32)) -> ImageManager { - // Show a status message - println!("Load and process {} image(s)...", paths.len()); - - // Load the images from the paths - let image_manager = ImageManager::from( - paths.iter() - .map(|path| load_image(path, &size)) - .collect() - ); - - // We succeeded - println!("All images have been loaded successfully"); - - image_manager - } - - /// Tick the image - pub fn tick(&mut self, canvas: &mut PixCanvas) { - // Get the image index bound - let bound = self.images.len(); - - // Get the image to use - let image = &mut self.images[ - self.index as usize % bound - ]; - - // Update the image on the canvas - canvas.update_image(image); - - // Increase the index - self.index += 1; - } -} - - - -/// A pixflut instance -struct PixCanvas { - host: String, - painter_count: usize, - painter_handles: Vec, - size: (u32, u32), - offset: (u32, u32), -} - -impl PixCanvas { - /// Create a new pixelflut canvas. - pub fn new( - host: &str, - painter_count: usize, - size: (u32, u32), - offset: (u32, u32), - ) -> PixCanvas { - // Initialize the object - let mut canvas = PixCanvas { - host: host.to_string(), - painter_count, - painter_handles: Vec::with_capacity(painter_count), - size, - offset, - }; - - // Show a status message - println!("Starting painter threads..."); - - // Spawn some painters - canvas.spawn_painters(); - - // Return the canvas - canvas - } - - /// Spawn the painters for this canvas - fn spawn_painters(&mut self) { - // Spawn some painters - for i in 0..self.painter_count { - // Determine the slice width - let width = self.size.0 / (self.painter_count as u32); - - // Define the area to paint per thread - let painter_area = Rect::from( - (i as u32) * width, - 0, - width, - self.size.1, - ); - - // Spawn the painter - self.spawn_painter(painter_area); - } - } - - /// Spawn a single painter in a thread. - fn spawn_painter(&mut self, area: Rect) { - // Get the host that will be used - let host = self.host.to_string(); - - // Redefine the offset to make it usable in the thread - let offset = (self.offset.0, self.offset.1); - - // Create a channel to push new images - let (tx, rx): (Sender, Receiver) - = mpsc::channel(); - - // Create the painter thread - let thread = thread::spawn(move || { - // Create a new stream - let stream = create_stream(host) - .expect("failed to open stream to pixelflut"); - - // Create a new client - let client = PixClient::new(stream); - - // Create a painter - let mut painter = Painter::new( - client, - area, - offset, - None - ); - - // Do some work - loop { - // Work - painter.work() - .expect("Painter failed to perform work"); - - // Update the image to paint - if let Ok(image) = rx.try_recv() { - painter.set_image(image); - } - } - }); - - // Create a new painter handle, pust it to the list - self.painter_handles.push( - PainterHandle::new( - thread, - area, - tx, - ) - ); - } - - // Update the image that is being rendered for all painters. - pub fn update_image(&mut self, image: &mut DynamicImage) { - // Update the image for each specific painter handle - for handle in &self.painter_handles { - handle.update_image(image); - } - } -} - - - -/// A handle for a painter thread. -struct PainterHandle { - #[allow(dead_code)] - thread: JoinHandle, - area: Rect, - image_sender: Sender, -} - -impl PainterHandle { - /// Create a new handle from the given properties. - pub fn new(thread: JoinHandle, area: Rect, image_sender: Sender) -> PainterHandle { - PainterHandle { - thread, - area, - image_sender, - } - } - - /// Push an image update. - pub fn update_image(&self, full_image: &mut DynamicImage) { - // Crop the image to the area - let image = full_image.crop( - self.area.x, - self.area.y, - self.area.w, - self.area.h, - ); - - // Push a new image to the thread - // TODO: return this result - self.image_sender.send(image) - .expect("Failed to send image update to painter"); - } -} - - - -struct Painter { - client: PixClient, - area: Rect, - offset: (u32, u32), - image: Option, -} - -impl Painter { - /// Create a new painter. - pub fn new(client: PixClient, area: Rect, offset: (u32, u32), image: Option) -> Painter { - Painter { - client, - area, - offset, - image, - } - } - - /// Perform work. - /// Paint the whole defined area. - pub fn work(&mut self) -> Result<(), Error> { - // Make sure there is an image - if self.image.is_none() { - // Show a warning - println!("Painter thread has no image yet to paint, waiting..."); - - // Sleep a little - thread::sleep(Duration::from_millis(PAINTER_IMAGE_WAIT_DELAY_MILLIS)); - return Ok(()); - } - - // Get an RGB image - let image = self.image.as_mut().unwrap().to_rgb(); - - // Loop through all the pixels, and set their color - for x in 0..self.area.w { - for y in 0..self.area.h { - // Get the pixel at this location - let pixel = image.get_pixel(x, y); - - // Get the channels - let channels = pixel.channels(); - - // Define the color - let color = Color::from( - channels[0], - channels[1], - channels[2], - ); - - // Set the pixel - self.client.write_pixel( - x + self.area.x + self.offset.0, - y + self.area.y + self.offset.1, - &color, - )?; - } - } - - // Everything seems to be ok - Ok(()) - } - - /// Update the image that should be painted - pub fn set_image(&mut self, image: DynamicImage) { - self.image = Some(image); - } -} - - - -/// A pixelflut client. -/// This client uses a stream to talk to a pixelflut panel. -/// It allows to write pixels to the panel, and read some status. -struct PixClient { - stream: BufStream, -} - -impl PixClient { - /// Create a new client instance. - pub fn new(stream: TcpStream) -> PixClient { - PixClient { - stream: BufStream::new(stream), - } - } - - /// Write a pixel to the given stream. - fn write_pixel(&mut self, x: u32, y: u32, color: &Color) -> Result<(), Error> { - // Write the command to set a pixel - self.write_command( - format!("PX {} {} {}", x, y, color.as_hex()), - ) - } - - // /// Read the size of the screen. - // fn read_screen_size(&mut self) { - // // Read the screen size - // let size = self - // .write_read_command("SIZE".into()) - // .expect("Failed to read screen size"); - // - // // TODO: Remove this after debugging - // println!("Read size: {}", size); - // } - - /// Write the given command to the given stream. - fn write_command(&mut self, cmd: String) -> Result<(), Error> { - // Write the pixels and a new line - self.stream.write(cmd.as_bytes())?; - self.stream.write("\n".as_bytes())?; - - // Everything seems to be ok - Ok(()) - } - - // /// Write the given command to the given stream, and read the output. - // fn write_read_command(&mut self, cmd: String) -> Result { - // // Write the command - // self.write_command(cmd); - // - // // Read the output - // let mut buffer = String::with_capacity(CMD_READ_BUFFER_SIZE); - // println!("Reading line..."); - // self.stream.read_line(&mut buffer)?; - // println!("Done reading"); - // - // // Return the read string - // Ok(buffer) - // } -} diff --git a/src/painter.rs b/src/painter.rs new file mode 100644 index 0000000..4af57a0 --- /dev/null +++ b/src/painter.rs @@ -0,0 +1,82 @@ +use std::io::Error; +use std::thread; +use std::time::Duration; + +use image::{DynamicImage, Pixel}; + +use app::PAINTER_IMAGE_WAIT_DELAY_MILLIS; +use color::Color; +use pix_client::PixClient; +use rect::Rect; + + + +/// A painter that paints on a pixelflut panel. +pub struct Painter { + client: PixClient, + area: Rect, + offset: (u32, u32), + image: Option, +} + +impl Painter { + /// Create a new painter. + pub fn new(client: PixClient, area: Rect, offset: (u32, u32), image: Option) -> Painter { + Painter { + client, + area, + offset, + image, + } + } + + /// Perform work. + /// Paint the whole defined area. + pub fn work(&mut self) -> Result<(), Error> { + // Make sure there is an image + if self.image.is_none() { + // Show a warning + println!("Painter thread has no image yet to paint, waiting..."); + + // Sleep a little + thread::sleep(Duration::from_millis(PAINTER_IMAGE_WAIT_DELAY_MILLIS)); + return Ok(()); + } + + // Get an RGB image + let image = self.image.as_mut().unwrap().to_rgb(); + + // Loop through all the pixels, and set their color + for x in 0..self.area.w { + for y in 0..self.area.h { + // Get the pixel at this location + let pixel = image.get_pixel(x, y); + + // Get the channels + let channels = pixel.channels(); + + // Define the color + let color = Color::from( + channels[0], + channels[1], + channels[2], + ); + + // Set the pixel + self.client.write_pixel( + x + self.area.x + self.offset.0, + y + self.area.y + self.offset.1, + &color, + )?; + } + } + + // Everything seems to be ok + Ok(()) + } + + /// Update the image that should be painted + pub fn set_image(&mut self, image: DynamicImage) { + self.image = Some(image); + } +} diff --git a/src/painter_handle.rs b/src/painter_handle.rs new file mode 100644 index 0000000..bf84511 --- /dev/null +++ b/src/painter_handle.rs @@ -0,0 +1,48 @@ +extern crate image; + +use std::sync::mpsc::Sender; +use std::thread::JoinHandle; + +use image::DynamicImage; + +use rect::Rect; + + + +/// A handle to a painter thread. +/// +/// This also holds a channel to the painter thread, +/// to allow image updates to be pushed to the thread. +pub struct PainterHandle { + #[allow(dead_code)] + thread: JoinHandle, + area: Rect, + image_sender: Sender, +} + +impl PainterHandle { + /// Create a new handle from the given properties. + pub fn new(thread: JoinHandle, area: Rect, image_sender: Sender) -> PainterHandle { + PainterHandle { + thread, + area, + image_sender, + } + } + + /// Push an image update. + pub fn update_image(&self, full_image: &mut DynamicImage) { + // Crop the image to the area + let image = full_image.crop( + self.area.x, + self.area.y, + self.area.w, + self.area.h, + ); + + // Push a new image to the thread + // TODO: return this result + self.image_sender.send(image) + .expect("Failed to send image update to painter"); + } +} diff --git a/src/pix_canvas.rs b/src/pix_canvas.rs new file mode 100644 index 0000000..189cdff --- /dev/null +++ b/src/pix_canvas.rs @@ -0,0 +1,140 @@ +use std::io::Error; +use std::net::TcpStream; +use std::sync::mpsc; +use std::sync::mpsc::{Sender, Receiver}; +use std::thread; + +use image::DynamicImage; + +use painter::Painter; +use painter_handle::PainterHandle; +use pix_client::PixClient; +use rect::Rect; + + + +/// A pixflut instance +pub struct PixCanvas { + host: String, + painter_count: usize, + painter_handles: Vec, + size: (u32, u32), + offset: (u32, u32), +} + +impl PixCanvas { + /// Create a new pixelflut canvas. + pub fn new( + host: &str, + painter_count: usize, + size: (u32, u32), + offset: (u32, u32), + ) -> PixCanvas { + // Initialize the object + let mut canvas = PixCanvas { + host: host.to_string(), + painter_count, + painter_handles: Vec::with_capacity(painter_count), + size, + offset, + }; + + // Show a status message + println!("Starting painter threads..."); + + // Spawn some painters + canvas.spawn_painters(); + + // Return the canvas + canvas + } + + /// Spawn the painters for this canvas + fn spawn_painters(&mut self) { + // Spawn some painters + for i in 0..self.painter_count { + // Determine the slice width + let width = self.size.0 / (self.painter_count as u32); + + // Define the area to paint per thread + let painter_area = Rect::from( + (i as u32) * width, + 0, + width, + self.size.1, + ); + + // Spawn the painter + self.spawn_painter(painter_area); + } + } + + /// Spawn a single painter in a thread. + fn spawn_painter(&mut self, area: Rect) { + // Get the host that will be used + let host = self.host.to_string(); + + // Redefine the offset to make it usable in the thread + let offset = (self.offset.0, self.offset.1); + + // Create a channel to push new images + let (tx, rx): (Sender, Receiver) + = mpsc::channel(); + + // Create the painter thread + let thread = thread::spawn(move || { + // Create a new stream + let stream = create_stream(host) + .expect("failed to open stream to pixelflut"); + + // Create a new client + let client = PixClient::new(stream); + + // Create a painter + let mut painter = Painter::new( + client, + area, + offset, + None + ); + + // Do some work + loop { + // Work + painter.work() + .expect("Painter failed to perform work"); + + // Update the image to paint + if let Ok(image) = rx.try_recv() { + painter.set_image(image); + } + } + }); + + // Create a new painter handle, pust it to the list + self.painter_handles.push( + PainterHandle::new( + thread, + area, + tx, + ) + ); + } + + // Update the image that is being rendered for all painters. + pub fn update_image(&mut self, image: &mut DynamicImage) { + // Update the image for each specific painter handle + for handle in &self.painter_handles { + handle.update_image(image); + } + } +} + + + +/// Create a stream to talk to the pixelflut server. +/// +/// The stream is returned as result. +fn create_stream(host: String) -> Result { + TcpStream::connect(host) +} diff --git a/src/pix_client.rs b/src/pix_client.rs new file mode 100644 index 0000000..9dd2903 --- /dev/null +++ b/src/pix_client.rs @@ -0,0 +1,75 @@ +extern crate bufstream; + +use std::io::Error; +use std::io::prelude::*; +use std::net::TcpStream; + +use self::bufstream::BufStream; + +use color::Color; + + + +/// A pixelflut client. +/// +/// This client uses a stream to talk to a pixelflut panel. +/// It allows to write pixels to the panel, and read some status. +/// +/// The client provides an interface for other logic to easily talk +/// to the pixelflut panel. +pub struct PixClient { + stream: BufStream, +} + +impl PixClient { + /// Create a new client instance. + pub fn new(stream: TcpStream) -> PixClient { + PixClient { + stream: BufStream::new(stream), + } + } + + /// Write a pixel to the given stream. + pub fn write_pixel(&mut self, x: u32, y: u32, color: &Color) -> Result<(), Error> { + // Write the command to set a pixel + self.write_command( + format!("PX {} {} {}", x, y, color.as_hex()), + ) + } + + // /// Read the size of the screen. + // fn read_screen_size(&mut self) { + // // Read the screen size + // let size = self + // .write_read_command("SIZE".into()) + // .expect("Failed to read screen size"); + // + // // TODO: Remove this after debugging + // println!("Read size: {}", size); + // } + + /// Write the given command to the given stream. + fn write_command(&mut self, cmd: String) -> Result<(), Error> { + // Write the pixels and a new line + self.stream.write(cmd.as_bytes())?; + self.stream.write("\n".as_bytes())?; + + // Everything seems to be ok + Ok(()) + } + + // /// Write the given command to the given stream, and read the output. + // fn write_read_command(&mut self, cmd: String) -> Result { + // // Write the command + // self.write_command(cmd); + // + // // Read the output + // let mut buffer = String::with_capacity(CMD_READ_BUFFER_SIZE); + // println!("Reading line..."); + // self.stream.read_line(&mut buffer)?; + // println!("Done reading"); + // + // // Return the read string + // Ok(buffer) + // } +}