diff --git a/CHANGELOG.md b/CHANGELOG.md index ccb25ac3..7578db3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased ] +### Added +- Clear command for anime `asusctl anime --clear` will clear the display +### Changed +- Make rog-anime more error tolerent. Remove various asserts and return errors instead +- Return error if a pixel-gif is larger than the anime-display dimensions + ## [4.2.1] - 2022-07-18 ### Added - Add panel overdrive support (autodetects if supported) diff --git a/Cargo.lock b/Cargo.lock index b1f0e822..d6f3aef3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1029,6 +1029,7 @@ dependencies = [ "serde_derive", "sysfs-class", "udev", + "zbus", "zvariant", ] diff --git a/asusctl/examples/anime-diag-png.rs b/asusctl/examples/anime-diag-png.rs index 25c617a7..889701ec 100644 --- a/asusctl/examples/anime-diag-png.rs +++ b/asusctl/examples/anime-diag-png.rs @@ -25,7 +25,7 @@ fn main() -> Result<(), Box> { client .proxies() .anime() - .write(matrix.into_data_buffer(anime_type)) + .write(matrix.into_data_buffer(anime_type)?) .unwrap(); Ok(()) diff --git a/asusctl/examples/anime-diag.rs b/asusctl/examples/anime-diag.rs index 9874b1bc..5599ce9f 100644 --- a/asusctl/examples/anime-diag.rs +++ b/asusctl/examples/anime-diag.rs @@ -29,7 +29,7 @@ fn main() { client .proxies() .anime() - .write(matrix.into_data_buffer(anime_type)) + .write(matrix.into_data_buffer(anime_type).unwrap()) .unwrap(); sleep(Duration::from_millis(300)); } diff --git a/asusctl/examples/anime-grid.rs b/asusctl/examples/anime-grid.rs index ea532899..a5cb8306 100644 --- a/asusctl/examples/anime-grid.rs +++ b/asusctl/examples/anime-grid.rs @@ -1,5 +1,6 @@ use rog_anime::{usb::get_anime_type, AnimeDataBuffer, AnimeGrid}; use rog_dbus::RogDbusClientBlocking; +use std::convert::TryFrom; // In usable data: // Top row start at 1, ends at 32 @@ -39,7 +40,7 @@ fn main() { } } - let matrix = ::from(matrix); + let matrix = ::try_from(matrix).unwrap(); client.proxies().anime().write(matrix).unwrap(); } diff --git a/asusctl/examples/anime-png.rs b/asusctl/examples/anime-png.rs index 99c24656..42cee160 100644 --- a/asusctl/examples/anime-png.rs +++ b/asusctl/examples/anime-png.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::{env, error::Error, path::Path, process::exit}; use rog_anime::{ @@ -32,7 +33,7 @@ fn main() -> Result<(), Box> { client .proxies() .anime() - .write(::from(&matrix)) + .write(::try_from(&matrix)?) .unwrap(); Ok(()) diff --git a/asusctl/examples/anime-spinning.rs b/asusctl/examples/anime-spinning.rs index 0beb53ca..eed3faa5 100644 --- a/asusctl/examples/anime-spinning.rs +++ b/asusctl/examples/anime-spinning.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::{ env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration, }; @@ -41,7 +42,7 @@ fn main() -> Result<(), Box> { client .proxies() .anime() - .write(::from(&matrix)) + .write(::try_from(&matrix)?) .unwrap(); sleep(Duration::from_micros(500)); } diff --git a/asusctl/src/anime_cli.rs b/asusctl/src/anime_cli.rs index 928d1586..63968052 100644 --- a/asusctl/src/anime_cli.rs +++ b/asusctl/src/anime_cli.rs @@ -16,6 +16,8 @@ pub struct AnimeCommand { pub boot_enable: Option, #[options(meta = "", help = "set global AniMe brightness value")] pub brightness: Option, + #[options(help = "clear the display")] + pub clear: bool, #[options(command)] pub command: Option, } diff --git a/asusctl/src/main.rs b/asusctl/src/main.rs index ed1d6c6a..dcccc417 100644 --- a/asusctl/src/main.rs +++ b/asusctl/src/main.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::process::Command; use std::thread::sleep; use std::{env::args, path::Path}; @@ -27,8 +28,6 @@ mod aura_cli; mod cli_opts; mod profiles_cli; -const CONFIG_ADVICE: &str = "A config file need to be removed so a new one can be generated"; - fn main() -> Result<(), Box> { let args: Vec = args().skip(1).collect(); @@ -82,15 +81,14 @@ fn main() -> Result<(), Box> { } fn print_error_help(err: Box, supported: Option<&SupportedFunctions>) { - if do_diagnose("asusd") { - println!("\nError: {}\n", err); - print_versions(); + check_service("asusd"); + println!("\nError: {}\n", err); + print_versions(); + println!(); + print_laptop_info(); + if let Some(supported) = supported { println!(); - print_laptop_info(); - if let Some(supported) = supported { - println!(); - println!("Supported laptop functions:\n\n{}", supported); - } + println!("Supported laptop functions:\n\n{}", supported); } } @@ -115,7 +113,7 @@ fn print_laptop_info() { println!("Board name: {}", board_name.trim()); } -fn do_diagnose(name: &str) -> bool { +fn check_service(name: &str) -> bool { if name != "asusd" && !check_systemd_unit_enabled(name) { println!( "\n\x1b[0;31m{} is not enabled, enable it with `systemctl enable {}\x1b[0m", @@ -128,13 +126,6 @@ fn do_diagnose(name: &str) -> bool { name, name ); return true; - } else { - println!("\nSome error happened (sorry)"); - println!( - "Please use `systemctl status {}` and `journalctl -b -u {}` for more information", - name, name - ); - println!("{}", CONFIG_ADVICE); } false } @@ -239,6 +230,13 @@ fn handle_anime( verify_brightness(bright); dbus.proxies().anime().set_brightness(bright)? } + if cmd.clear { + let anime_type = get_anime_type()?; + let data = vec![0u8; anime_type.data_length()]; + let tmp = AnimeDataBuffer::from_vec(anime_type, data)?; + dbus.proxies().anime().write(tmp)?; + } + if let Some(action) = cmd.command.as_ref() { let anime_type = get_anime_type()?; match action { @@ -263,7 +261,7 @@ fn handle_anime( dbus.proxies() .anime() - .write(::from(&matrix))?; + .write(::try_from(&matrix)?)?; } AnimeActions::PixelImage(image) => { if image.help_requested() || image.path.is_empty() { @@ -284,7 +282,7 @@ fn handle_anime( dbus.proxies() .anime() - .write(matrix.into_data_buffer(anime_type))?; + .write(matrix.into_data_buffer(anime_type)?)?; } AnimeActions::Gif(gif) => { if gif.help_requested() || gif.path.is_empty() { diff --git a/daemon/src/ctrl_anime/mod.rs b/daemon/src/ctrl_anime/mod.rs index fed5a296..86e4c64c 100644 --- a/daemon/src/ctrl_anime/mod.rs +++ b/daemon/src/ctrl_anime/mod.rs @@ -18,6 +18,7 @@ use rusb::{Device, DeviceHandle}; use smol::{stream::StreamExt, Executor}; use std::{ cell::RefCell, + convert::TryFrom, error::Error, sync::{Arc, Mutex, MutexGuard}, thread::sleep, @@ -167,7 +168,14 @@ impl CtrlAnime { inner .try_lock() .map(|lock| { - lock.write_data_buffer(frame); + lock.write_data_buffer(frame) + .map_err(|err| { + warn!( + "rog_anime::run_animation:callback {}", + err + ); + }) + .ok(); false // Don't exit yet }) .map_err(|err| { @@ -183,6 +191,8 @@ impl CtrlAnime { once = false; if let Ok(lock) = inner.try_lock() { lock.write_data_buffer(image.as_ref().clone()) + .map_err(|e| error!("{}", e)) + .ok(); } } ActionData::Pause(duration) => sleep(*duration), @@ -201,9 +211,16 @@ impl CtrlAnime { } // Clear the display on exit if let Ok(lock) = inner.try_lock() { - let data = - AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()]); - lock.write_data_buffer(data); + if let Ok(data) = + AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()]) + .map_err(|e| error!("{}", e)) + { + lock.write_data_buffer(data) + .map_err(|err| { + warn!("rog_anime::run_animation:callback {}", err); + }) + .ok(); + } } // Loop ended, set the atmonics thread_running.store(false, Ordering::SeqCst); @@ -254,7 +271,7 @@ impl CtrlAnime { /// Write only a data packet. This will modify the leds brightness using the /// global brightness set in config. - fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) { + fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) -> Result<(), RogError> { for led in buffer.data_mut()[7..].iter_mut() { let mut bright = *led as f32 * self.config.brightness; if bright > 254.0 { @@ -262,11 +279,12 @@ impl CtrlAnime { } *led = bright as u8; } - let data = AnimePacketType::from(buffer); + let data = AnimePacketType::try_from(buffer)?; for row in data.iter() { self.write_bytes(row); } self.write_bytes(&pkt_for_flush()); + Ok(()) } fn do_initialization(&self) { diff --git a/daemon/src/ctrl_anime/zbus.rs b/daemon/src/ctrl_anime/zbus.rs index bb10c0d5..f0a92b15 100644 --- a/daemon/src/ctrl_anime/zbus.rs +++ b/daemon/src/ctrl_anime/zbus.rs @@ -1,6 +1,7 @@ use std::sync::{Arc, Mutex}; use async_trait::async_trait; +use log::warn; use rog_anime::{ usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on}, AnimeDataBuffer, AnimePowerStates, @@ -27,14 +28,18 @@ impl crate::ZbusAdd for CtrlAnimeZbus { #[dbus_interface(name = "org.asuslinux.Daemon")] impl CtrlAnimeZbus { /// Writes a data stream of length. Will force system thread to exit until it is restarted - fn write(&self, input: AnimeDataBuffer) { + fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> { 'outer: loop { if let Ok(lock) = self.0.try_lock() { lock.thread_exit.store(true, Ordering::SeqCst); - lock.write_data_buffer(input); + lock.write_data_buffer(input).map_err(|err| { + warn!("rog_anime::run_animation:callback {}", err); + err + })?; break 'outer; } } + Ok(()) } /// Set the global AniMe brightness diff --git a/daemon/src/error.rs b/daemon/src/error.rs index 67853b50..963cab63 100644 --- a/daemon/src/error.rs +++ b/daemon/src/error.rs @@ -1,3 +1,4 @@ +use rog_anime::error::AnimeError; use rog_profiles::error::ProfileError; use std::convert::From; use std::fmt; @@ -26,6 +27,7 @@ pub enum RogError { AuraEffectNotSupported, NoAuraKeyboard, NoAuraNode, + Anime(AnimeError), } impl fmt::Display for RogError { @@ -54,6 +56,7 @@ impl fmt::Display for RogError { RogError::AuraEffectNotSupported => write!(f, "Aura effect not supported"), RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"), RogError::NoAuraNode => write!(f, "No Aura keyboard node found"), + RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets), } } } @@ -66,6 +69,12 @@ impl From for RogError { } } +impl From for RogError { + fn from(err: AnimeError) -> Self { + RogError::Anime(err) + } +} + impl From for RogError { fn from(err: zbus::Error) -> Self { RogError::Zbus(err) diff --git a/rog-anime/Cargo.toml b/rog-anime/Cargo.toml index 55786864..aea7a9ae 100644 --- a/rog-anime/Cargo.toml +++ b/rog-anime/Cargo.toml @@ -14,7 +14,7 @@ exclude = ["data"] [features] default = ["dbus", "detect"] -dbus = ["zvariant"] +dbus = ["zvariant", "zbus"] detect = ["udev", "sysfs-class"] [dependencies] @@ -29,6 +29,7 @@ serde_derive = "^1.0" glam = { version = "0.20.5", features = ["serde"] } zvariant = { version = "^3.0", optional = true } +zbus = { version = "^2.2", optional = true } udev = { version = "^0.6", optional = true } sysfs-class = { version = "^0.1", optional = true } \ No newline at end of file diff --git a/rog-anime/src/data.rs b/rog-anime/src/data.rs index af72ae03..57d03577 100644 --- a/rog-anime/src/data.rs +++ b/rog-anime/src/data.rs @@ -1,4 +1,5 @@ use std::{ + convert::TryFrom, thread::sleep, time::{Duration, Instant}, }; @@ -8,7 +9,10 @@ use serde_derive::{Deserialize, Serialize}; #[cfg(feature = "dbus")] use zvariant::Type; -use crate::{error::AnimeError, AnimTime, AnimeGif}; +use crate::{ + error::{AnimeError, Result}, + AnimTime, AnimeGif, +}; /// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2` const BLOCK_START: usize = 7; @@ -102,20 +106,25 @@ impl AnimeDataBuffer { /// # Panics /// Will panic if the vector length is not `ANIME_DATA_LEN` #[inline] - pub fn from_vec(anime: AnimeType, data: Vec) -> Self { - assert_eq!(data.len(), anime.data_length()); + pub fn from_vec(anime: AnimeType, data: Vec) -> Result { + if data.len() != anime.data_length() { + return Err(AnimeError::DataBufferLength); + } - Self { data, anime } + Ok(Self { data, anime }) } } /// The two packets to be written to USB pub type AnimePacketType = Vec<[u8; 640]>; -impl From for AnimePacketType { - #[inline] - fn from(anime: AnimeDataBuffer) -> Self { - assert_eq!(anime.data.len(), anime.anime.data_length()); +impl TryFrom for AnimePacketType { + type Error = AnimeError; + + fn try_from(anime: AnimeDataBuffer) -> std::result::Result { + if anime.data.len() != anime.anime.data_length() { + return Err(AnimeError::DataBufferLength); + } let mut buffers = match anime.anime { AnimeType::GA401 => vec![[0; 640]; 2], @@ -131,7 +140,7 @@ impl From for AnimePacketType { if matches!(anime.anime, AnimeType::GA402) { buffers[2][..7].copy_from_slice(&USB_PREFIX3); } - buffers + Ok(buffers) } } @@ -140,8 +149,8 @@ impl From for AnimePacketType { /// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early. pub fn run_animation( frames: &AnimeGif, - callback: &dyn Fn(AnimeDataBuffer) -> Result, -) -> Result<(), AnimeError> { + callback: &dyn Fn(AnimeDataBuffer) -> Result, +) -> Result<()> { let mut count = 0; let start = Instant::now(); diff --git a/rog-anime/src/diagonal.rs b/rog-anime/src/diagonal.rs index e89aeb40..5a32c34a 100644 --- a/rog-anime/src/diagonal.rs +++ b/rog-anime/src/diagonal.rs @@ -1,6 +1,10 @@ use std::{path::Path, time::Duration}; -use crate::{data::AnimeDataBuffer, error::AnimeError, AnimeType}; +use crate::{ + data::AnimeDataBuffer, + error::{AnimeError, Result}, + AnimeType, +}; /// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images) #[derive(Debug, Clone)] @@ -40,7 +44,7 @@ impl AnimeDiagonal { duration: Option, bright: f32, anime_type: AnimeType, - ) -> Result { + ) -> Result { let data = std::fs::read(path)?; let data = std::io::Cursor::new(data); let decoder = png_pong::Decoder::new(data)?.into_steps(); @@ -125,7 +129,7 @@ impl AnimeDiagonal { /// Convert to a data buffer that can be sent over dbus #[inline] - pub fn into_data_buffer(&self, anime_type: AnimeType) -> AnimeDataBuffer { + pub fn into_data_buffer(&self, anime_type: AnimeType) -> Result { match anime_type { AnimeType::GA401 => self.into_ga401_packets(), AnimeType::GA402 => self.into_ga402_packets(), @@ -134,7 +138,7 @@ impl AnimeDiagonal { /// Do conversion from the nested Vec in AnimeMatrix to the two required /// packets suitable for sending over USB - fn into_ga401_packets(&self) -> AnimeDataBuffer { + fn into_ga401_packets(&self) -> Result { let mut buf = vec![0u8; AnimeType::GA401.data_length()]; buf[1..=32].copy_from_slice(&self.get_row(0, 3, 32)); @@ -196,7 +200,7 @@ impl AnimeDiagonal { AnimeDataBuffer::from_vec(crate::AnimeType::GA401, buf) } - fn into_ga402_packets(&self) -> AnimeDataBuffer { + fn into_ga402_packets(&self) -> Result { let mut buf = vec![0u8; AnimeType::GA402.data_length()]; let mut start_index: usize = 0; @@ -282,7 +286,7 @@ impl AnimeDiagonal { #[cfg(test)] mod tests { - use std::path::PathBuf; + use std::{convert::TryFrom, path::PathBuf}; use crate::{AnimeDiagonal, AnimePacketType, AnimeType}; @@ -374,8 +378,8 @@ mod tests { path.push("test/ga401-diagonal.png"); let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA401).unwrap(); - let data = matrix.into_data_buffer(crate::AnimeType::GA401); - let pkt = AnimePacketType::from(data); + let data = matrix.into_data_buffer(crate::AnimeType::GA401).unwrap(); + let pkt = AnimePacketType::try_from(data).unwrap(); assert_eq!(pkt[0], pkt0_check); assert_eq!(pkt[1], pkt1_check); @@ -509,8 +513,8 @@ mod tests { path.push("test/ga402-diagonal.png"); let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap(); - let data = matrix.into_data_buffer(crate::AnimeType::GA402); - let pkt = AnimePacketType::from(data); + let data = matrix.into_data_buffer(crate::AnimeType::GA402).unwrap(); + let pkt = AnimePacketType::try_from(data).unwrap(); assert_eq!(pkt[0], pkt0_check); assert_eq!(pkt[1], pkt1_check); @@ -654,8 +658,8 @@ mod tests { path.push("test/ga402-diagonal-fullbright.png"); let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap(); - let data = matrix.into_data_buffer(crate::AnimeType::GA402); - let pkt = AnimePacketType::from(data); + let data = matrix.into_data_buffer(crate::AnimeType::GA402).unwrap(); + let pkt = AnimePacketType::try_from(data).unwrap(); assert_eq!(pkt[0], pkt0_check); assert_eq!(pkt[1], pkt1_check); diff --git a/rog-anime/src/error.rs b/rog-anime/src/error.rs index 4fc0721d..08c77bd9 100644 --- a/rog-anime/src/error.rs +++ b/rog-anime/src/error.rs @@ -3,6 +3,8 @@ use png_pong::decode::Error as PngError; use std::error::Error; use std::fmt; +pub type Result = std::result::Result; + #[derive(Debug)] pub enum AnimeError { NoFrames, @@ -17,6 +19,9 @@ pub enum AnimeError { NoDevice, UnsupportedDevice, InvalidBrightness(f32), + DataBufferLength, + PixelGifWidth(usize), + PixelGifHeight(usize), } impl fmt::Display for AnimeError { @@ -36,12 +41,23 @@ impl fmt::Display for AnimeError { AnimeError::Dbus(detail) => write!(f, "{}", detail), AnimeError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error), AnimeError::NoDevice => write!(f, "No AniMe Matrix device found"), + AnimeError::DataBufferLength => write!( + f, + "The data buffer was incorrect length for generating USB packets" + ), AnimeError::UnsupportedDevice => write!(f, "Unsupported AniMe Matrix device found"), AnimeError::InvalidBrightness(bright) => write!( f, "Image brightness must be between 0.0 and 1.0 (inclusive), was {}", bright ), + AnimeError::PixelGifWidth(n) => { + write!(f, "The gif used for pixel-perfect gif is is wider than {n}") + } + AnimeError::PixelGifHeight(n) => write!( + f, + "The gif used for pixel-perfect gif is is taller than {n}" + ), } } } @@ -68,3 +84,10 @@ impl From for AnimeError { AnimeError::Gif(err) } } + +impl From for zbus::fdo::Error { + #[inline] + fn from(err: AnimeError) -> Self { + zbus::fdo::Error::Failed(format!("{}", err)) + } +} diff --git a/rog-anime/src/gif.rs b/rog-anime/src/gif.rs index 1bdafe13..28059320 100644 --- a/rog-anime/src/gif.rs +++ b/rog-anime/src/gif.rs @@ -1,8 +1,10 @@ use glam::Vec2; use serde_derive::{Deserialize, Serialize}; +use std::convert::TryFrom; use std::{fs::File, path::Path, time::Duration}; -use crate::{error::AnimeError, AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel}; +use crate::error::AnimeError; +use crate::{error::Result, AnimeDataBuffer, AnimeDiagonal, AnimeImage, AnimeType, Pixel}; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct AnimeFrame { @@ -93,7 +95,7 @@ impl AnimeGif { duration: AnimTime, brightness: f32, anime_type: AnimeType, - ) -> Result { + ) -> Result { let mut matrix = AnimeDiagonal::new(anime_type, None); let mut decoder = gif::DecodeOptions::new(); @@ -116,13 +118,22 @@ impl AnimeGif { // should be t but not in some gifs? What, ASUS, what? continue; } - matrix.get_mut()[y + frame.top as usize][x + frame.left as usize] = - (px[0] as f32 * brightness) as u8; + let tmp = matrix.get_mut(); + let y = y + frame.top as usize; + if y >= tmp.len() { + return Err(AnimeError::PixelGifHeight(tmp.len())); + } + let x = x + frame.left as usize; + if x >= tmp[y].len() { + return Err(AnimeError::PixelGifWidth(tmp[y].len())); + } + + matrix.get_mut()[y][x] = (px[0] as f32 * brightness) as u8; } } frames.push(AnimeFrame { - data: matrix.into_data_buffer(anime_type), + data: matrix.into_data_buffer(anime_type)?, delay: Duration::from_millis(wait as u64), }); } @@ -136,7 +147,7 @@ impl AnimeGif { anime_type: AnimeType, duration: AnimTime, brightness: f32, - ) -> Result { + ) -> Result { let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?; let mut total = Duration::from_millis(1000); @@ -150,7 +161,7 @@ impl AnimeGif { let frame_count = total.as_millis() / 30; let single = AnimeFrame { - data: image.into_data_buffer(anime_type), + data: image.into_data_buffer(anime_type)?, delay: Duration::from_millis(30), }; let frames = vec![single; frame_count as usize]; @@ -169,7 +180,7 @@ impl AnimeGif { duration: AnimTime, brightness: f32, anime_type: AnimeType, - ) -> Result { + ) -> Result { let mut frames = Vec::new(); let mut decoder = gif::DecodeOptions::new(); @@ -225,7 +236,7 @@ impl AnimeGif { image.update(); frames.push(AnimeFrame { - data: ::from(&image), + data: ::try_from(&image)?, delay: Duration::from_millis(wait as u64), }); } @@ -244,7 +255,7 @@ impl AnimeGif { duration: AnimTime, brightness: f32, anime_type: AnimeType, - ) -> Result { + ) -> Result { let image = AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?; @@ -259,7 +270,7 @@ impl AnimeGif { let frame_count = total.as_millis() / 30; let single = AnimeFrame { - data: ::from(&image), + data: ::try_from(&image)?, delay: Duration::from_millis(30), }; let frames = vec![single; frame_count as usize]; diff --git a/rog-anime/src/grid.rs b/rog-anime/src/grid.rs index f80e84e2..b34a8b46 100644 --- a/rog-anime/src/grid.rs +++ b/rog-anime/src/grid.rs @@ -1,4 +1,7 @@ +use std::convert::TryFrom; + use crate::data::AnimeDataBuffer; +use crate::error::{AnimeError, Result}; use crate::{AnimeImage, AnimeType}; // TODO: Adjust these sizes as WIDTH_GA401 WIDTH_GA402 @@ -87,11 +90,12 @@ impl AnimeGrid { // } } -impl From for AnimeDataBuffer { +impl TryFrom for AnimeDataBuffer { + type Error = AnimeError; + /// Do conversion from the nested Vec in AniMeMatrix to the two required /// packets suitable for sending over USB - #[inline] - fn from(anime: AnimeGrid) -> Self { + fn try_from(anime: AnimeGrid) -> Result { let mut buf = vec![0u8; anime.anime_type.data_length()]; for (idx, pos) in AnimeImage::generate_image_positioning(anime.anime_type) @@ -123,7 +127,7 @@ mod tests { } } - let matrix = ::from(matrix); + let matrix = ::try_from(matrix).unwrap(); let data_cmp = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, diff --git a/rog-anime/src/image.rs b/rog-anime/src/image.rs index 7146d1ea..428b4866 100644 --- a/rog-anime/src/image.rs +++ b/rog-anime/src/image.rs @@ -1,9 +1,13 @@ -use std::path::Path; +use std::{convert::TryFrom, path::Path}; pub use glam::Vec2; use glam::{Mat3, Vec3}; -use crate::{data::AnimeDataBuffer, error::AnimeError, AnimeType}; +use crate::{ + data::AnimeDataBuffer, + error::{AnimeError, Result}, + AnimeType, +}; /// A single greyscale + alpha pixel in the image #[derive(Copy, Clone, Debug)] @@ -83,7 +87,7 @@ impl AnimeImage { pixels: Vec, width: u32, anime_type: AnimeType, - ) -> Result { + ) -> Result { if bright < 0.0 || bright > 1.0 { return Err(AnimeError::InvalidBrightness(bright)); } @@ -388,7 +392,7 @@ impl AnimeImage { translation: Vec2, bright: f32, anime_type: AnimeType, - ) -> Result { + ) -> Result { let data = std::fs::read(path)?; let data = std::io::Cursor::new(data); let decoder = png_pong::Decoder::new(data)?.into_steps(); @@ -484,11 +488,12 @@ impl AnimeImage { } } -impl From<&AnimeImage> for AnimeDataBuffer { +impl TryFrom<&AnimeImage> for AnimeDataBuffer { + type Error = AnimeError; + /// Do conversion from the nested Vec in AnimeDataBuffer to the two required /// packets suitable for sending over USB - #[inline] - fn from(leds: &AnimeImage) -> Self { + fn try_from(leds: &AnimeImage) -> Result { let mut l: Vec = leds .led_pos .iter() @@ -506,7 +511,7 @@ impl From<&AnimeImage> for AnimeDataBuffer { #[cfg(test)] mod tests { - use std::path::PathBuf; + use std::{convert::TryFrom, path::PathBuf}; use crate::{image::*, AnimTime, AnimeGif, AnimePacketType}; @@ -734,8 +739,8 @@ mod tests { ) .unwrap(); matrix._edge_outline(); - let data = AnimeDataBuffer::from(&matrix); - let pkt = AnimePacketType::from(data); + let data = AnimeDataBuffer::try_from(&matrix).unwrap(); + let pkt = AnimePacketType::try_from(data).unwrap(); assert_eq!(pkt[0], pkt0_check); assert_eq!(pkt[1], pkt1_check); @@ -759,6 +764,6 @@ mod tests { ) .unwrap(); matrix.frames()[0].frame(); - let _pkt = AnimePacketType::from(matrix.frames()[0].frame().clone()); + let _pkt = AnimePacketType::try_from(matrix.frames()[0].frame().clone()).unwrap(); } } diff --git a/rog-anime/src/sequencer.rs b/rog-anime/src/sequencer.rs index fa30b3ae..90db9ba0 100644 --- a/rog-anime/src/sequencer.rs +++ b/rog-anime/src/sequencer.rs @@ -1,10 +1,10 @@ -use std::{path::PathBuf, time::Duration}; - use glam::Vec2; use serde_derive::{Deserialize, Serialize}; +use std::convert::TryFrom; +use std::{path::PathBuf, time::Duration}; use crate::{ - error::AnimeError, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, + error::Result, AnimTime, AnimeDataBuffer, AnimeDiagonal, AnimeGif, AnimeImage, AnimeType, }; /// All the possible AniMe actions that can be used. This enum is intended to be @@ -65,10 +65,7 @@ pub enum ActionData { } impl ActionData { - pub fn from_anime_action( - anime_type: AnimeType, - action: &ActionLoader, - ) -> Result { + pub fn from_anime_action(anime_type: AnimeType, action: &ActionLoader) -> Result { let a = match action { ActionLoader::AsusAnimation { file, @@ -87,7 +84,7 @@ impl ActionData { } => match time { AnimTime::Infinite => { let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?; - let data = image.into_data_buffer(anime_type); + let data = image.into_data_buffer(anime_type)?; ActionData::Image(Box::new(data)) } _ => ActionData::Animation(AnimeGif::from_diagonal_png( @@ -147,7 +144,7 @@ impl ActionData { *brightness, anime_type, )?; - let data = ::from(&image); + let data = ::try_from(&image)?; ActionData::Image(Box::new(data)) } _ => ActionData::Animation(AnimeGif::from_png( @@ -180,7 +177,7 @@ impl Sequences { /// Use a base `AnimeAction` to generate the precomputed data and insert in to /// the run buffer #[inline] - pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<(), AnimeError> { + pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<()> { self.0 .insert(index, ActionData::from_anime_action(self.1, action)?); Ok(())