Merge branch 'fluke/errors' into 'main'

Improve error handling in some cases

See merge request asus-linux/asusctl!121
This commit is contained in:
Luke Jones
2022-07-20 09:01:55 +00:00
20 changed files with 189 additions and 93 deletions

View File

@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased ] ## [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 ## [4.2.1] - 2022-07-18
### Added ### Added
- Add panel overdrive support (autodetects if supported) - Add panel overdrive support (autodetects if supported)

1
Cargo.lock generated
View File

@@ -1029,6 +1029,7 @@ dependencies = [
"serde_derive", "serde_derive",
"sysfs-class", "sysfs-class",
"udev", "udev",
"zbus",
"zvariant", "zvariant",
] ]

View File

@@ -25,7 +25,7 @@ fn main() -> Result<(), Box<dyn Error>> {
client client
.proxies() .proxies()
.anime() .anime()
.write(matrix.into_data_buffer(anime_type)) .write(matrix.into_data_buffer(anime_type)?)
.unwrap(); .unwrap();
Ok(()) Ok(())

View File

@@ -29,7 +29,7 @@ fn main() {
client client
.proxies() .proxies()
.anime() .anime()
.write(matrix.into_data_buffer(anime_type)) .write(matrix.into_data_buffer(anime_type).unwrap())
.unwrap(); .unwrap();
sleep(Duration::from_millis(300)); sleep(Duration::from_millis(300));
} }

View File

@@ -1,5 +1,6 @@
use rog_anime::{usb::get_anime_type, AnimeDataBuffer, AnimeGrid}; use rog_anime::{usb::get_anime_type, AnimeDataBuffer, AnimeGrid};
use rog_dbus::RogDbusClientBlocking; use rog_dbus::RogDbusClientBlocking;
use std::convert::TryFrom;
// In usable data: // In usable data:
// Top row start at 1, ends at 32 // Top row start at 1, ends at 32
@@ -39,7 +40,7 @@ fn main() {
} }
} }
let matrix = <AnimeDataBuffer>::from(matrix); let matrix = <AnimeDataBuffer>::try_from(matrix).unwrap();
client.proxies().anime().write(matrix).unwrap(); client.proxies().anime().write(matrix).unwrap();
} }

View File

@@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::{env, error::Error, path::Path, process::exit}; use std::{env, error::Error, path::Path, process::exit};
use rog_anime::{ use rog_anime::{
@@ -32,7 +33,7 @@ fn main() -> Result<(), Box<dyn Error>> {
client client
.proxies() .proxies()
.anime() .anime()
.write(<AnimeDataBuffer>::from(&matrix)) .write(<AnimeDataBuffer>::try_from(&matrix)?)
.unwrap(); .unwrap();
Ok(()) Ok(())

View File

@@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::{ use std::{
env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration, env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration,
}; };
@@ -41,7 +42,7 @@ fn main() -> Result<(), Box<dyn Error>> {
client client
.proxies() .proxies()
.anime() .anime()
.write(<AnimeDataBuffer>::from(&matrix)) .write(<AnimeDataBuffer>::try_from(&matrix)?)
.unwrap(); .unwrap();
sleep(Duration::from_micros(500)); sleep(Duration::from_micros(500));
} }

View File

@@ -16,6 +16,8 @@ pub struct AnimeCommand {
pub boot_enable: Option<bool>, pub boot_enable: Option<bool>,
#[options(meta = "", help = "set global AniMe brightness value")] #[options(meta = "", help = "set global AniMe brightness value")]
pub brightness: Option<f32>, pub brightness: Option<f32>,
#[options(help = "clear the display")]
pub clear: bool,
#[options(command)] #[options(command)]
pub command: Option<AnimeActions>, pub command: Option<AnimeActions>,
} }

View File

@@ -1,3 +1,4 @@
use std::convert::TryFrom;
use std::process::Command; use std::process::Command;
use std::thread::sleep; use std::thread::sleep;
use std::{env::args, path::Path}; use std::{env::args, path::Path};
@@ -27,8 +28,6 @@ mod aura_cli;
mod cli_opts; mod cli_opts;
mod profiles_cli; 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<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = args().skip(1).collect(); let args: Vec<String> = args().skip(1).collect();
@@ -82,15 +81,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) { fn print_error_help(err: Box<dyn std::error::Error>, supported: Option<&SupportedFunctions>) {
if do_diagnose("asusd") { check_service("asusd");
println!("\nError: {}\n", err); println!("\nError: {}\n", err);
print_versions(); print_versions();
println!();
print_laptop_info();
if let Some(supported) = supported {
println!(); println!();
print_laptop_info(); println!("Supported laptop functions:\n\n{}", supported);
if let Some(supported) = supported {
println!();
println!("Supported laptop functions:\n\n{}", supported);
}
} }
} }
@@ -115,7 +113,7 @@ fn print_laptop_info() {
println!("Board name: {}", board_name.trim()); 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) { if name != "asusd" && !check_systemd_unit_enabled(name) {
println!( println!(
"\n\x1b[0;31m{} is not enabled, enable it with `systemctl enable {}\x1b[0m", "\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 name, name
); );
return true; 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 false
} }
@@ -239,6 +230,13 @@ fn handle_anime(
verify_brightness(bright); verify_brightness(bright);
dbus.proxies().anime().set_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() { if let Some(action) = cmd.command.as_ref() {
let anime_type = get_anime_type()?; let anime_type = get_anime_type()?;
match action { match action {
@@ -263,7 +261,7 @@ fn handle_anime(
dbus.proxies() dbus.proxies()
.anime() .anime()
.write(<AnimeDataBuffer>::from(&matrix))?; .write(<AnimeDataBuffer>::try_from(&matrix)?)?;
} }
AnimeActions::PixelImage(image) => { AnimeActions::PixelImage(image) => {
if image.help_requested() || image.path.is_empty() { if image.help_requested() || image.path.is_empty() {
@@ -284,7 +282,7 @@ fn handle_anime(
dbus.proxies() dbus.proxies()
.anime() .anime()
.write(matrix.into_data_buffer(anime_type))?; .write(matrix.into_data_buffer(anime_type)?)?;
} }
AnimeActions::Gif(gif) => { AnimeActions::Gif(gif) => {
if gif.help_requested() || gif.path.is_empty() { if gif.help_requested() || gif.path.is_empty() {

View File

@@ -18,6 +18,7 @@ use rusb::{Device, DeviceHandle};
use smol::{stream::StreamExt, Executor}; use smol::{stream::StreamExt, Executor};
use std::{ use std::{
cell::RefCell, cell::RefCell,
convert::TryFrom,
error::Error, error::Error,
sync::{Arc, Mutex, MutexGuard}, sync::{Arc, Mutex, MutexGuard},
thread::sleep, thread::sleep,
@@ -167,7 +168,14 @@ impl CtrlAnime {
inner inner
.try_lock() .try_lock()
.map(|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 false // Don't exit yet
}) })
.map_err(|err| { .map_err(|err| {
@@ -183,6 +191,8 @@ impl CtrlAnime {
once = false; once = false;
if let Ok(lock) = inner.try_lock() { if let Ok(lock) = inner.try_lock() {
lock.write_data_buffer(image.as_ref().clone()) lock.write_data_buffer(image.as_ref().clone())
.map_err(|e| error!("{}", e))
.ok();
} }
} }
ActionData::Pause(duration) => sleep(*duration), ActionData::Pause(duration) => sleep(*duration),
@@ -201,9 +211,16 @@ impl CtrlAnime {
} }
// Clear the display on exit // Clear the display on exit
if let Ok(lock) = inner.try_lock() { if let Ok(lock) = inner.try_lock() {
let data = if let Ok(data) =
AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()]); AnimeDataBuffer::from_vec(anime_type, vec![0u8; anime_type.data_length()])
lock.write_data_buffer(data); .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 // Loop ended, set the atmonics
thread_running.store(false, Ordering::SeqCst); 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 /// Write only a data packet. This will modify the leds brightness using the
/// global brightness set in config. /// 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() { for led in buffer.data_mut()[7..].iter_mut() {
let mut bright = *led as f32 * self.config.brightness; let mut bright = *led as f32 * self.config.brightness;
if bright > 254.0 { if bright > 254.0 {
@@ -262,11 +279,12 @@ impl CtrlAnime {
} }
*led = bright as u8; *led = bright as u8;
} }
let data = AnimePacketType::from(buffer); let data = AnimePacketType::try_from(buffer)?;
for row in data.iter() { for row in data.iter() {
self.write_bytes(row); self.write_bytes(row);
} }
self.write_bytes(&pkt_for_flush()); self.write_bytes(&pkt_for_flush());
Ok(())
} }
fn do_initialization(&self) { fn do_initialization(&self) {

View File

@@ -1,6 +1,7 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use async_trait::async_trait; use async_trait::async_trait;
use log::warn;
use rog_anime::{ use rog_anime::{
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on}, usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
AnimeDataBuffer, AnimePowerStates, AnimeDataBuffer, AnimePowerStates,
@@ -27,14 +28,18 @@ impl crate::ZbusAdd for CtrlAnimeZbus {
#[dbus_interface(name = "org.asuslinux.Daemon")] #[dbus_interface(name = "org.asuslinux.Daemon")]
impl CtrlAnimeZbus { impl CtrlAnimeZbus {
/// Writes a data stream of length. Will force system thread to exit until it is restarted /// 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 { 'outer: loop {
if let Ok(lock) = self.0.try_lock() { if let Ok(lock) = self.0.try_lock() {
lock.thread_exit.store(true, Ordering::SeqCst); 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; break 'outer;
} }
} }
Ok(())
} }
/// Set the global AniMe brightness /// Set the global AniMe brightness

View File

@@ -1,3 +1,4 @@
use rog_anime::error::AnimeError;
use rog_profiles::error::ProfileError; use rog_profiles::error::ProfileError;
use std::convert::From; use std::convert::From;
use std::fmt; use std::fmt;
@@ -26,6 +27,7 @@ pub enum RogError {
AuraEffectNotSupported, AuraEffectNotSupported,
NoAuraKeyboard, NoAuraKeyboard,
NoAuraNode, NoAuraNode,
Anime(AnimeError),
} }
impl fmt::Display for RogError { impl fmt::Display for RogError {
@@ -54,6 +56,7 @@ impl fmt::Display for RogError {
RogError::AuraEffectNotSupported => write!(f, "Aura effect not supported"), RogError::AuraEffectNotSupported => write!(f, "Aura effect not supported"),
RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"), RogError::NoAuraKeyboard => write!(f, "No supported Aura keyboard"),
RogError::NoAuraNode => write!(f, "No Aura keyboard node found"), RogError::NoAuraNode => write!(f, "No Aura keyboard node found"),
RogError::Anime(deets) => write!(f, "AniMe Matrix error: {}", deets),
} }
} }
} }
@@ -66,6 +69,12 @@ impl From<ProfileError> for RogError {
} }
} }
impl From<AnimeError> for RogError {
fn from(err: AnimeError) -> Self {
RogError::Anime(err)
}
}
impl From<zbus::Error> for RogError { impl From<zbus::Error> for RogError {
fn from(err: zbus::Error) -> Self { fn from(err: zbus::Error) -> Self {
RogError::Zbus(err) RogError::Zbus(err)

View File

@@ -14,7 +14,7 @@ exclude = ["data"]
[features] [features]
default = ["dbus", "detect"] default = ["dbus", "detect"]
dbus = ["zvariant"] dbus = ["zvariant", "zbus"]
detect = ["udev", "sysfs-class"] detect = ["udev", "sysfs-class"]
[dependencies] [dependencies]
@@ -29,6 +29,7 @@ serde_derive = "^1.0"
glam = { version = "0.20.5", features = ["serde"] } glam = { version = "0.20.5", features = ["serde"] }
zvariant = { version = "^3.0", optional = true } zvariant = { version = "^3.0", optional = true }
zbus = { version = "^2.2", optional = true }
udev = { version = "^0.6", optional = true } udev = { version = "^0.6", optional = true }
sysfs-class = { version = "^0.1", optional = true } sysfs-class = { version = "^0.1", optional = true }

View File

@@ -1,4 +1,5 @@
use std::{ use std::{
convert::TryFrom,
thread::sleep, thread::sleep,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@@ -8,7 +9,10 @@ use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zvariant::Type; 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` /// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
const BLOCK_START: usize = 7; const BLOCK_START: usize = 7;
@@ -102,20 +106,25 @@ impl AnimeDataBuffer {
/// # Panics /// # Panics
/// Will panic if the vector length is not `ANIME_DATA_LEN` /// Will panic if the vector length is not `ANIME_DATA_LEN`
#[inline] #[inline]
pub fn from_vec(anime: AnimeType, data: Vec<u8>) -> Self { pub fn from_vec(anime: AnimeType, data: Vec<u8>) -> Result<Self> {
assert_eq!(data.len(), anime.data_length()); 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 /// The two packets to be written to USB
pub type AnimePacketType = Vec<[u8; 640]>; pub type AnimePacketType = Vec<[u8; 640]>;
impl From<AnimeDataBuffer> for AnimePacketType { impl TryFrom<AnimeDataBuffer> for AnimePacketType {
#[inline] type Error = AnimeError;
fn from(anime: AnimeDataBuffer) -> Self {
assert_eq!(anime.data.len(), anime.anime.data_length()); fn try_from(anime: AnimeDataBuffer) -> std::result::Result<Self, Self::Error> {
if anime.data.len() != anime.anime.data_length() {
return Err(AnimeError::DataBufferLength);
}
let mut buffers = match anime.anime { let mut buffers = match anime.anime {
AnimeType::GA401 => vec![[0; 640]; 2], AnimeType::GA401 => vec![[0; 640]; 2],
@@ -131,7 +140,7 @@ impl From<AnimeDataBuffer> for AnimePacketType {
if matches!(anime.anime, AnimeType::GA402) { if matches!(anime.anime, AnimeType::GA402) {
buffers[2][..7].copy_from_slice(&USB_PREFIX3); buffers[2][..7].copy_from_slice(&USB_PREFIX3);
} }
buffers Ok(buffers)
} }
} }
@@ -140,8 +149,8 @@ impl From<AnimeDataBuffer> for AnimePacketType {
/// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early. /// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early.
pub fn run_animation( pub fn run_animation(
frames: &AnimeGif, frames: &AnimeGif,
callback: &dyn Fn(AnimeDataBuffer) -> Result<bool, AnimeError>, callback: &dyn Fn(AnimeDataBuffer) -> Result<bool>,
) -> Result<(), AnimeError> { ) -> Result<()> {
let mut count = 0; let mut count = 0;
let start = Instant::now(); let start = Instant::now();

View File

@@ -1,6 +1,10 @@
use std::{path::Path, time::Duration}; 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) /// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images)
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -40,7 +44,7 @@ impl AnimeDiagonal {
duration: Option<Duration>, duration: Option<Duration>,
bright: f32, bright: f32,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self> {
let data = std::fs::read(path)?; let data = std::fs::read(path)?;
let data = std::io::Cursor::new(data); let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps(); 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 /// Convert to a data buffer that can be sent over dbus
#[inline] #[inline]
pub fn into_data_buffer(&self, anime_type: AnimeType) -> AnimeDataBuffer { pub fn into_data_buffer(&self, anime_type: AnimeType) -> Result<AnimeDataBuffer> {
match anime_type { match anime_type {
AnimeType::GA401 => self.into_ga401_packets(), AnimeType::GA401 => self.into_ga401_packets(),
AnimeType::GA402 => self.into_ga402_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 /// Do conversion from the nested Vec in AnimeMatrix to the two required
/// packets suitable for sending over USB /// packets suitable for sending over USB
fn into_ga401_packets(&self) -> AnimeDataBuffer { fn into_ga401_packets(&self) -> Result<AnimeDataBuffer> {
let mut buf = vec![0u8; AnimeType::GA401.data_length()]; let mut buf = vec![0u8; AnimeType::GA401.data_length()];
buf[1..=32].copy_from_slice(&self.get_row(0, 3, 32)); 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) AnimeDataBuffer::from_vec(crate::AnimeType::GA401, buf)
} }
fn into_ga402_packets(&self) -> AnimeDataBuffer { fn into_ga402_packets(&self) -> Result<AnimeDataBuffer> {
let mut buf = vec![0u8; AnimeType::GA402.data_length()]; let mut buf = vec![0u8; AnimeType::GA402.data_length()];
let mut start_index: usize = 0; let mut start_index: usize = 0;
@@ -282,7 +286,7 @@ impl AnimeDiagonal {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::PathBuf; use std::{convert::TryFrom, path::PathBuf};
use crate::{AnimeDiagonal, AnimePacketType, AnimeType}; use crate::{AnimeDiagonal, AnimePacketType, AnimeType};
@@ -374,8 +378,8 @@ mod tests {
path.push("test/ga401-diagonal.png"); path.push("test/ga401-diagonal.png");
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA401).unwrap(); let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA401).unwrap();
let data = matrix.into_data_buffer(crate::AnimeType::GA401); let data = matrix.into_data_buffer(crate::AnimeType::GA401).unwrap();
let pkt = AnimePacketType::from(data); let pkt = AnimePacketType::try_from(data).unwrap();
assert_eq!(pkt[0], pkt0_check); assert_eq!(pkt[0], pkt0_check);
assert_eq!(pkt[1], pkt1_check); assert_eq!(pkt[1], pkt1_check);
@@ -509,8 +513,8 @@ mod tests {
path.push("test/ga402-diagonal.png"); path.push("test/ga402-diagonal.png");
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap(); let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap();
let data = matrix.into_data_buffer(crate::AnimeType::GA402); let data = matrix.into_data_buffer(crate::AnimeType::GA402).unwrap();
let pkt = AnimePacketType::from(data); let pkt = AnimePacketType::try_from(data).unwrap();
assert_eq!(pkt[0], pkt0_check); assert_eq!(pkt[0], pkt0_check);
assert_eq!(pkt[1], pkt1_check); assert_eq!(pkt[1], pkt1_check);
@@ -654,8 +658,8 @@ mod tests {
path.push("test/ga402-diagonal-fullbright.png"); path.push("test/ga402-diagonal-fullbright.png");
let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap(); let matrix = AnimeDiagonal::from_png(&path, None, 255.0, AnimeType::GA402).unwrap();
let data = matrix.into_data_buffer(crate::AnimeType::GA402); let data = matrix.into_data_buffer(crate::AnimeType::GA402).unwrap();
let pkt = AnimePacketType::from(data); let pkt = AnimePacketType::try_from(data).unwrap();
assert_eq!(pkt[0], pkt0_check); assert_eq!(pkt[0], pkt0_check);
assert_eq!(pkt[1], pkt1_check); assert_eq!(pkt[1], pkt1_check);

View File

@@ -3,6 +3,8 @@ use png_pong::decode::Error as PngError;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
pub type Result<T> = std::result::Result<T, AnimeError>;
#[derive(Debug)] #[derive(Debug)]
pub enum AnimeError { pub enum AnimeError {
NoFrames, NoFrames,
@@ -17,6 +19,9 @@ pub enum AnimeError {
NoDevice, NoDevice,
UnsupportedDevice, UnsupportedDevice,
InvalidBrightness(f32), InvalidBrightness(f32),
DataBufferLength,
PixelGifWidth(usize),
PixelGifHeight(usize),
} }
impl fmt::Display for AnimeError { impl fmt::Display for AnimeError {
@@ -36,12 +41,23 @@ impl fmt::Display for AnimeError {
AnimeError::Dbus(detail) => write!(f, "{}", detail), AnimeError::Dbus(detail) => write!(f, "{}", detail),
AnimeError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error), AnimeError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
AnimeError::NoDevice => write!(f, "No AniMe Matrix device found"), 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::UnsupportedDevice => write!(f, "Unsupported AniMe Matrix device found"),
AnimeError::InvalidBrightness(bright) => write!( AnimeError::InvalidBrightness(bright) => write!(
f, f,
"Image brightness must be between 0.0 and 1.0 (inclusive), was {}", "Image brightness must be between 0.0 and 1.0 (inclusive), was {}",
bright 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<DecodingError> for AnimeError {
AnimeError::Gif(err) AnimeError::Gif(err)
} }
} }
impl From<AnimeError> for zbus::fdo::Error {
#[inline]
fn from(err: AnimeError) -> Self {
zbus::fdo::Error::Failed(format!("{}", err))
}
}

View File

@@ -1,8 +1,10 @@
use glam::Vec2; use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::{fs::File, path::Path, time::Duration}; 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)] #[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AnimeFrame { pub struct AnimeFrame {
@@ -93,7 +95,7 @@ impl AnimeGif {
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self> {
let mut matrix = AnimeDiagonal::new(anime_type, None); let mut matrix = AnimeDiagonal::new(anime_type, None);
let mut decoder = gif::DecodeOptions::new(); let mut decoder = gif::DecodeOptions::new();
@@ -116,13 +118,22 @@ impl AnimeGif {
// should be t but not in some gifs? What, ASUS, what? // should be t but not in some gifs? What, ASUS, what?
continue; continue;
} }
matrix.get_mut()[y + frame.top as usize][x + frame.left as usize] = let tmp = matrix.get_mut();
(px[0] as f32 * brightness) as u8; 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 { frames.push(AnimeFrame {
data: matrix.into_data_buffer(anime_type), data: matrix.into_data_buffer(anime_type)?,
delay: Duration::from_millis(wait as u64), delay: Duration::from_millis(wait as u64),
}); });
} }
@@ -136,7 +147,7 @@ impl AnimeGif {
anime_type: AnimeType, anime_type: AnimeType,
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
) -> Result<Self, AnimeError> { ) -> Result<Self> {
let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?; let image = AnimeDiagonal::from_png(file_name, None, brightness, anime_type)?;
let mut total = Duration::from_millis(1000); let mut total = Duration::from_millis(1000);
@@ -150,7 +161,7 @@ impl AnimeGif {
let frame_count = total.as_millis() / 30; let frame_count = total.as_millis() / 30;
let single = AnimeFrame { let single = AnimeFrame {
data: image.into_data_buffer(anime_type), data: image.into_data_buffer(anime_type)?,
delay: Duration::from_millis(30), delay: Duration::from_millis(30),
}; };
let frames = vec![single; frame_count as usize]; let frames = vec![single; frame_count as usize];
@@ -169,7 +180,7 @@ impl AnimeGif {
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self> {
let mut frames = Vec::new(); let mut frames = Vec::new();
let mut decoder = gif::DecodeOptions::new(); let mut decoder = gif::DecodeOptions::new();
@@ -225,7 +236,7 @@ impl AnimeGif {
image.update(); image.update();
frames.push(AnimeFrame { frames.push(AnimeFrame {
data: <AnimeDataBuffer>::from(&image), data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(wait as u64), delay: Duration::from_millis(wait as u64),
}); });
} }
@@ -244,7 +255,7 @@ impl AnimeGif {
duration: AnimTime, duration: AnimTime,
brightness: f32, brightness: f32,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self> {
let image = let image =
AnimeImage::from_png(file_name, scale, angle, translation, brightness, anime_type)?; 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 frame_count = total.as_millis() / 30;
let single = AnimeFrame { let single = AnimeFrame {
data: <AnimeDataBuffer>::from(&image), data: <AnimeDataBuffer>::try_from(&image)?,
delay: Duration::from_millis(30), delay: Duration::from_millis(30),
}; };
let frames = vec![single; frame_count as usize]; let frames = vec![single; frame_count as usize];

View File

@@ -1,4 +1,7 @@
use std::convert::TryFrom;
use crate::data::AnimeDataBuffer; use crate::data::AnimeDataBuffer;
use crate::error::{AnimeError, Result};
use crate::{AnimeImage, AnimeType}; use crate::{AnimeImage, AnimeType};
// TODO: Adjust these sizes as WIDTH_GA401 WIDTH_GA402 // TODO: Adjust these sizes as WIDTH_GA401 WIDTH_GA402
@@ -87,11 +90,12 @@ impl AnimeGrid {
// } // }
} }
impl From<AnimeGrid> for AnimeDataBuffer { impl TryFrom<AnimeGrid> for AnimeDataBuffer {
type Error = AnimeError;
/// Do conversion from the nested Vec in AniMeMatrix to the two required /// Do conversion from the nested Vec in AniMeMatrix to the two required
/// packets suitable for sending over USB /// packets suitable for sending over USB
#[inline] fn try_from(anime: AnimeGrid) -> Result<Self> {
fn from(anime: AnimeGrid) -> Self {
let mut buf = vec![0u8; anime.anime_type.data_length()]; let mut buf = vec![0u8; anime.anime_type.data_length()];
for (idx, pos) in AnimeImage::generate_image_positioning(anime.anime_type) for (idx, pos) in AnimeImage::generate_image_positioning(anime.anime_type)
@@ -123,7 +127,7 @@ mod tests {
} }
} }
let matrix = <AnimeDataBuffer>::from(matrix); let matrix = <AnimeDataBuffer>::try_from(matrix).unwrap();
let data_cmp = [ 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, 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,

View File

@@ -1,9 +1,13 @@
use std::path::Path; use std::{convert::TryFrom, path::Path};
pub use glam::Vec2; pub use glam::Vec2;
use glam::{Mat3, Vec3}; 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 /// A single greyscale + alpha pixel in the image
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
@@ -83,7 +87,7 @@ impl AnimeImage {
pixels: Vec<Pixel>, pixels: Vec<Pixel>,
width: u32, width: u32,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self> {
if bright < 0.0 || bright > 1.0 { if bright < 0.0 || bright > 1.0 {
return Err(AnimeError::InvalidBrightness(bright)); return Err(AnimeError::InvalidBrightness(bright));
} }
@@ -388,7 +392,7 @@ impl AnimeImage {
translation: Vec2, translation: Vec2,
bright: f32, bright: f32,
anime_type: AnimeType, anime_type: AnimeType,
) -> Result<Self, AnimeError> { ) -> Result<Self> {
let data = std::fs::read(path)?; let data = std::fs::read(path)?;
let data = std::io::Cursor::new(data); let data = std::io::Cursor::new(data);
let decoder = png_pong::Decoder::new(data)?.into_steps(); 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 /// Do conversion from the nested Vec in AnimeDataBuffer to the two required
/// packets suitable for sending over USB /// packets suitable for sending over USB
#[inline] fn try_from(leds: &AnimeImage) -> Result<Self> {
fn from(leds: &AnimeImage) -> Self {
let mut l: Vec<u8> = leds let mut l: Vec<u8> = leds
.led_pos .led_pos
.iter() .iter()
@@ -506,7 +511,7 @@ impl From<&AnimeImage> for AnimeDataBuffer {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::PathBuf; use std::{convert::TryFrom, path::PathBuf};
use crate::{image::*, AnimTime, AnimeGif, AnimePacketType}; use crate::{image::*, AnimTime, AnimeGif, AnimePacketType};
@@ -734,8 +739,8 @@ mod tests {
) )
.unwrap(); .unwrap();
matrix._edge_outline(); matrix._edge_outline();
let data = AnimeDataBuffer::from(&matrix); let data = AnimeDataBuffer::try_from(&matrix).unwrap();
let pkt = AnimePacketType::from(data); let pkt = AnimePacketType::try_from(data).unwrap();
assert_eq!(pkt[0], pkt0_check); assert_eq!(pkt[0], pkt0_check);
assert_eq!(pkt[1], pkt1_check); assert_eq!(pkt[1], pkt1_check);
@@ -759,6 +764,6 @@ mod tests {
) )
.unwrap(); .unwrap();
matrix.frames()[0].frame(); matrix.frames()[0].frame();
let _pkt = AnimePacketType::from(matrix.frames()[0].frame().clone()); let _pkt = AnimePacketType::try_from(matrix.frames()[0].frame().clone()).unwrap();
} }
} }

View File

@@ -1,10 +1,10 @@
use std::{path::PathBuf, time::Duration};
use glam::Vec2; use glam::Vec2;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::convert::TryFrom;
use std::{path::PathBuf, time::Duration};
use crate::{ 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 /// All the possible AniMe actions that can be used. This enum is intended to be
@@ -65,10 +65,7 @@ pub enum ActionData {
} }
impl ActionData { impl ActionData {
pub fn from_anime_action( pub fn from_anime_action(anime_type: AnimeType, action: &ActionLoader) -> Result<ActionData> {
anime_type: AnimeType,
action: &ActionLoader,
) -> Result<ActionData, AnimeError> {
let a = match action { let a = match action {
ActionLoader::AsusAnimation { ActionLoader::AsusAnimation {
file, file,
@@ -87,7 +84,7 @@ impl ActionData {
} => match time { } => match time {
AnimTime::Infinite => { AnimTime::Infinite => {
let image = AnimeDiagonal::from_png(file, None, *brightness, anime_type)?; 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::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::from_diagonal_png( _ => ActionData::Animation(AnimeGif::from_diagonal_png(
@@ -147,7 +144,7 @@ impl ActionData {
*brightness, *brightness,
anime_type, anime_type,
)?; )?;
let data = <AnimeDataBuffer>::from(&image); let data = <AnimeDataBuffer>::try_from(&image)?;
ActionData::Image(Box::new(data)) ActionData::Image(Box::new(data))
} }
_ => ActionData::Animation(AnimeGif::from_png( _ => ActionData::Animation(AnimeGif::from_png(
@@ -180,7 +177,7 @@ impl Sequences {
/// Use a base `AnimeAction` to generate the precomputed data and insert in to /// Use a base `AnimeAction` to generate the precomputed data and insert in to
/// the run buffer /// the run buffer
#[inline] #[inline]
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<(), AnimeError> { pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<()> {
self.0 self.0
.insert(index, ActionData::from_anime_action(self.1, action)?); .insert(index, ActionData::from_anime_action(self.1, action)?);
Ok(()) Ok(())