anime: prep rog-anime for publish, rename *all* AniMe~ to Anime

This commit is contained in:
Luke D Jones
2021-04-11 10:46:05 +12:00
parent e515741efa
commit 0657c6cc74
29 changed files with 476 additions and 383 deletions

103
Cargo.lock generated
View File

@@ -250,7 +250,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]
[[package]]
@@ -302,7 +302,7 @@ checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]
[[package]]
@@ -328,7 +328,7 @@ dependencies = [
"proc-macro2",
"quote 1.0.9",
"rustversion",
"syn 1.0.68",
"syn 1.0.69",
"synstructure",
]
@@ -343,9 +343,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f55667319111d593ba876406af7c409c0ebb44dc4be6132a783ccf163ea14c1"
checksum = "a9d5813545e459ad3ca1bff9915e9ad7f1a47dc6a91b627ce321d5863b7dd253"
dependencies = [
"futures-channel",
"futures-core",
@@ -358,9 +358,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c2dd2df839b57db9ab69c2c9d8f3e8c81984781937fe2807dc6dcf3b2ad2939"
checksum = "ce79c6a52a299137a6013061e0cf0e688fce5d7f1bc60125f520912fdb29ec25"
dependencies = [
"futures-core",
"futures-sink",
@@ -368,15 +368,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15496a72fabf0e62bdc3df11a59a3787429221dd0710ba8ef163d6f7a9112c94"
checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815"
[[package]]
name = "futures-executor"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891a4b7b96d84d5940084b2a37632dd65deeae662c114ceaa2c879629c9c0ad1"
checksum = "10f6cb7042eda00f0049b1d2080aa4b93442997ee507eb3828e8bd7577f94c9d"
dependencies = [
"futures-core",
"futures-task",
@@ -385,9 +385,9 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59"
checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04"
[[package]]
name = "futures-lite"
@@ -406,33 +406,33 @@ dependencies = [
[[package]]
name = "futures-macro"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea405816a5139fb39af82c2beb921d52143f556038378d6db21183a5c37fbfb7"
checksum = "668c6733a182cd7deb4f1de7ba3bf2120823835b3bcfbeacf7d2c4a773c1bb8b"
dependencies = [
"proc-macro-hack",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]
[[package]]
name = "futures-sink"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85754d98985841b7d4f5e8e6fbfa4a4ac847916893ec511a2917ccd8525b8bb3"
checksum = "5c5629433c555de3d82861a7a4e3794a4c40040390907cfbfd7143a92a426c23"
[[package]]
name = "futures-task"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa189ef211c15ee602667a6fcfe1c1fd9e07d42250d2156382820fba33c9df80"
checksum = "ba7aa51095076f3ba6d9a1f702f74bd05ec65f555d70d2033d55ba8d69f581bc"
[[package]]
name = "futures-util"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1812c7ab8aedf8d6f2701a43e1243acdbcc2b36ab26e2ad421eb99ac963d96d1"
checksum = "3c144ad54d60f23927f0a6b6d816e4271278b64f005ad65e4e35291d2de9c025"
dependencies = [
"futures-channel",
"futures-core",
@@ -471,12 +471,11 @@ dependencies = [
[[package]]
name = "glam"
version = "0.13.1"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70155b56080764b8b758e91e4c63d06da0262c0c939f2cd991cd1382087147df"
checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da"
dependencies = [
"serde",
"spirv-std",
]
[[package]]
@@ -496,7 +495,7 @@ checksum = "915ef07c710d84733522461de2a734d4d62a3fd39a4d4f404c2f385ef8618d05"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]
[[package]]
@@ -551,12 +550,6 @@ version = "0.2.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
[[package]]
name = "libm"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
[[package]]
name = "libudev-sys"
version = "0.1.4"
@@ -703,7 +696,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
"libm",
]
[[package]]
@@ -818,7 +810,7 @@ dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
"version_check",
]
@@ -905,7 +897,7 @@ checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
[[package]]
name = "rog_anime"
version = "1.0.0"
version = "1.0.2"
dependencies = [
"gif",
"glam",
@@ -954,9 +946,9 @@ dependencies = [
[[package]]
name = "rusb"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c470dc7dc6e4710b6f85e9c4aa4650bc742260b39a36328180578db76fa258c1"
checksum = "12f3264859095257507e4c011ab420ff9b2d9cc3349c6c08a1d3a019260bb437"
dependencies = [
"libc",
"libusb1-sys",
@@ -1009,7 +1001,7 @@ checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]
[[package]]
@@ -1031,7 +1023,7 @@ checksum = "2dc6b7951b17b051f3210b063f12cc17320e2fe30ae05b0fe2a3abb068551c76"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]
[[package]]
@@ -1048,7 +1040,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]
[[package]]
@@ -1061,27 +1053,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "spirv-std"
version = "0.4.0-alpha.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6de6f1b80004dfc3e9e02fdf8eb32c663f3b85e3942f39d02b6540ed0d2460dd"
dependencies = [
"num-traits",
"spirv-std-macros",
]
[[package]]
name = "spirv-std-macros"
version = "0.4.0-alpha.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4972082b5236fd57a46cc47fbc315ad78b5ad07b33e51077c688a2fe28d6f2d"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
]
[[package]]
name = "strum"
version = "0.8.0"
@@ -1111,9 +1082,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.68"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
dependencies = [
"proc-macro2",
"quote 1.0.9",
@@ -1137,7 +1108,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701"
dependencies = [
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
"unicode-xid 0.2.1",
]
@@ -1371,7 +1342,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]
[[package]]
@@ -1395,5 +1366,5 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote 1.0.9",
"syn 1.0.68",
"syn 1.0.69",
]

View File

@@ -18,6 +18,6 @@ yansi-term = "^0.1"
[dev-dependencies]
tinybmp = "^0.2.3"
glam = "*"
glam = "0.14.0"
rog_dbus = { path = "../rog-dbus" }
gif = "^0.11.2"

View File

@@ -1,6 +1,6 @@
use std::{env, error::Error, path::Path, process::exit};
use rog_anime::{AniMeDataBuffer, AniMeDiagonal};
use rog_anime::{AnimeDataBuffer, AnimeDiagonal};
use rog_dbus::AuraDbusClient;
fn main() -> Result<(), Box<dyn Error>> {
@@ -14,12 +14,12 @@ fn main() -> Result<(), Box<dyn Error>> {
}
let matrix =
AniMeDiagonal::from_png(Path::new(&args[1]), None, args[2].parse::<f32>().unwrap())?;
AnimeDiagonal::from_png(Path::new(&args[1]), None, args[2].parse::<f32>().unwrap())?;
client
.proxies()
.anime()
.write(<AniMeDataBuffer>::from(&matrix))
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
Ok(())

View File

@@ -1,6 +1,6 @@
use std::{thread::sleep, time::Duration};
use rog_anime::{AniMeDataBuffer, AniMeDiagonal};
use rog_anime::{AnimeDataBuffer, AnimeDiagonal};
use rog_dbus::AuraDbusClient;
// In usable data:
@@ -12,7 +12,7 @@ fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
for step in (2..50).rev() {
let mut matrix = AniMeDiagonal::new(None);
let mut matrix = AnimeDiagonal::new(None);
for c in (0..60).into_iter().step_by(step) {
for i in matrix.get_mut().iter_mut() {
i[c] = 50;
@@ -25,7 +25,7 @@ fn main() {
}
}
let m = <AniMeDataBuffer>::from(&matrix);
let m = <AnimeDataBuffer>::from(&matrix);
client.proxies().anime().write(m).unwrap();
sleep(Duration::from_millis(300));
}

View File

@@ -1,4 +1,4 @@
use rog_anime::{AniMeDataBuffer, AniMeGrid};
use rog_anime::{AnimeDataBuffer, AnimeGrid};
use rog_dbus::AuraDbusClient;
// In usable data:
@@ -8,7 +8,7 @@ use rog_dbus::AuraDbusClient;
fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
let mut matrix = AniMeGrid::new(None);
let mut matrix = AnimeGrid::new(None);
let tmp = matrix.get_mut();
let mut i = 0;
@@ -38,7 +38,7 @@ fn main() {
}
}
let matrix = <AniMeDataBuffer>::from(matrix);
let matrix = <AnimeDataBuffer>::from(matrix);
client.proxies().anime().write(matrix).unwrap();
}

View File

@@ -1,4 +1,4 @@
use rog_anime::AniMeDataBuffer;
use rog_anime::AnimeDataBuffer;
use rog_dbus::AuraDbusClient;
// In usable data:
@@ -6,7 +6,7 @@ use rog_dbus::AuraDbusClient;
fn main() {
let (client, _) = AuraDbusClient::new().unwrap();
let mut matrix = AniMeDataBuffer::new();
let mut matrix = AnimeDataBuffer::new();
matrix.get_mut()[1] = 100; // start = 1
for n in matrix.get_mut()[2..32].iter_mut() {
*n = 250;

View File

@@ -1,7 +1,7 @@
use std::{env, error::Error, path::Path, process::exit};
use rog_anime::{
AniMeDataBuffer, {AniMeImage, Vec2},
AnimeDataBuffer, {AnimeImage, Vec2},
};
use rog_dbus::AuraDbusClient;
@@ -15,7 +15,7 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1);
}
let matrix = AniMeImage::from_png(
let matrix = AnimeImage::from_png(
Path::new(&args[1]),
args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(),
@@ -29,7 +29,7 @@ fn main() -> Result<(), Box<dyn Error>> {
client
.proxies()
.anime()
.write(<AniMeDataBuffer>::from(&matrix))
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
Ok(())

View File

@@ -3,7 +3,7 @@ use std::{
};
use rog_anime::{
AniMeDataBuffer, {AniMeImage, Vec2},
AnimeDataBuffer, {AnimeImage, Vec2},
};
use rog_dbus::AuraDbusClient;
@@ -17,7 +17,7 @@ fn main() -> Result<(), Box<dyn Error>> {
exit(-1);
}
let mut matrix = AniMeImage::from_png(
let mut matrix = AnimeImage::from_png(
Path::new(&args[1]),
args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(),
@@ -38,7 +38,7 @@ fn main() -> Result<(), Box<dyn Error>> {
client
.proxies()
.anime()
.write(<AniMeDataBuffer>::from(&matrix))
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
sleep(Duration::from_micros(500));
}

View File

@@ -3,18 +3,18 @@ use rog_types::error::AuraError;
use std::str::FromStr;
#[derive(Copy, Clone, Debug)]
pub enum AniMeStatusValue {
pub enum AnimeStatusValue {
On,
Off,
}
impl FromStr for AniMeStatusValue {
impl FromStr for AnimeStatusValue {
type Err = AuraError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"on" => Ok(AniMeStatusValue::On),
"off" => Ok(AniMeStatusValue::Off),
"on" => Ok(AnimeStatusValue::On),
"off" => Ok(AnimeStatusValue::Off),
_ => {
print!("Invalid argument, must be one of: on, off");
Err(AuraError::ParseAnime)
@@ -22,17 +22,17 @@ impl FromStr for AniMeStatusValue {
}
}
}
impl From<AniMeStatusValue> for bool {
fn from(value: AniMeStatusValue) -> Self {
impl From<AnimeStatusValue> for bool {
fn from(value: AnimeStatusValue) -> Self {
match value {
AniMeStatusValue::On => true,
AniMeStatusValue::Off => false,
AnimeStatusValue::On => true,
AnimeStatusValue::Off => false,
}
}
}
#[derive(Options)]
pub struct AniMeLeds {
pub struct AnimeLeds {
#[options(help = "print help message")]
help: bool,
#[options(
@@ -44,37 +44,37 @@ pub struct AniMeLeds {
)]
led_brightness: u8,
}
impl AniMeLeds {
impl AnimeLeds {
pub fn led_brightness(&self) -> u8 {
self.led_brightness
}
}
#[derive(Options)]
pub struct AniMeCommand {
pub struct AnimeCommand {
#[options(help = "print help message")]
pub help: bool,
#[options(
meta = "",
help = "turn on/off the panel (accept/reject write requests)"
)]
pub turn: Option<AniMeStatusValue>,
pub turn: Option<AnimeStatusValue>,
#[options(meta = "", help = "turn on/off the panel at boot (with Asus effect)")]
pub boot: Option<AniMeStatusValue>,
pub boot: Option<AnimeStatusValue>,
#[options(command)]
pub command: Option<AniMeActions>,
pub command: Option<AnimeActions>,
}
#[derive(Options)]
pub enum AniMeActions {
pub enum AnimeActions {
#[options(help = "change all leds brightness")]
Leds(AniMeLeds),
Leds(AnimeLeds),
#[options(help = "display an 8bit greyscale png")]
Image(AniMeImage),
Image(AnimeImage),
}
#[derive(Options)]
pub struct AniMeImage {
pub struct AnimeImage {
#[options(help = "print help message")]
pub help: bool,
#[options(meta = "", help = "full path to the png to display")]

View File

@@ -2,9 +2,9 @@ mod anime_cli;
mod aura_cli;
use crate::aura_cli::{LedBrightness, SetAuraBuiltin};
use anime_cli::{AniMeActions, AniMeCommand};
use anime_cli::{AnimeActions, AnimeCommand};
use gumdrop::{Opt, Options};
use rog_anime::{AniMeDataBuffer, AniMeImage, Vec2, ANIME_DATA_LEN};
use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
use rog_dbus::AuraDbusClient;
use rog_types::{
aura_modes::{self, AuraEffect, AuraModeNum},
@@ -49,7 +49,7 @@ enum CliCommand {
#[options(help = "Set the graphics mode")]
Graphics(GraphicsCommand),
#[options(name = "anime", help = "Manage AniMe Matrix")]
AniMe(AniMeCommand),
Anime(AnimeCommand),
#[options(help = "Change bios settings")]
Bios(BiosCommand),
}
@@ -145,7 +145,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Some(CliCommand::LedMode(mode)) => handle_led_mode(&dbus, &supported.keyboard_led, &mode)?,
Some(CliCommand::Profile(cmd)) => handle_profile(&dbus, &supported.fan_cpu_ctrl, &cmd)?,
Some(CliCommand::Graphics(cmd)) => do_gfx(&dbus, &supported.rog_bios_ctrl, cmd)?,
Some(CliCommand::AniMe(cmd)) => {
Some(CliCommand::Anime(cmd)) => {
if (cmd.command.is_none() && cmd.boot.is_none() && cmd.turn.is_none()) || cmd.help {
println!("Missing arg or command\n\n{}", cmd.self_usage());
if let Some(lst) = cmd.self_command_list() {
@@ -160,13 +160,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
if let Some(action) = cmd.command {
match action {
AniMeActions::Leds(anime_leds) => {
let data = AniMeDataBuffer::from_vec(
AnimeActions::Leds(anime_leds) => {
let data = AnimeDataBuffer::from_vec(
[anime_leds.led_brightness(); ANIME_DATA_LEN].to_vec(),
);
dbus.proxies().anime().write(data)?;
}
AniMeActions::Image(image) => {
AnimeActions::Image(image) => {
if image.help_requested() {
println!("Missing arg or command\n\n{}", image.self_usage());
if let Some(lst) = image.self_command_list() {
@@ -175,7 +175,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
std::process::exit(1);
}
let matrix = AniMeImage::from_png(
let matrix = AnimeImage::from_png(
Path::new(&image.path),
image.scale,
image.angle,
@@ -185,7 +185,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
dbus.proxies()
.anime()
.write(<AniMeDataBuffer>::from(&matrix))
.write(<AnimeDataBuffer>::from(&matrix))
.unwrap();
}
}

14
daemon-user/README.md Normal file
View File

@@ -0,0 +1,14 @@
# daemon-user
This crate is for the binary of `asusd-user` and its helper lib.
The purpose of `asusd-user` is to run in userland and provide the user + third-party apps an interface for such things as creating AniMe sequences (and more in future, see todo list).
`asusd-user` should try to be as simple as possible while allowing a decent degree of control.
## TODO
- [ ] CLI for basic settings/interaction
- [ ] RGB keyboard per-key programs
- [ ] User profiles (fan, cpu etc). These would be replacing the system-daemon profiles only when the user is active, otherwise system-daemon defaults to system settings.
- [ ] Audio EQ visualiser - for use with anime + keyboard lighting

View File

@@ -21,7 +21,7 @@ path = "src/daemon.rs"
rog_anime = { path = "../rog-anime" }
rog_types = { path = "../rog-types" }
rog_dbus = { path = "../rog-dbus" }
rusb = "^0.7"
rusb = "^0.8"
udev = "^0.6"
# cli and logging

View File

@@ -1,20 +1,11 @@
const INIT_STR: &str = "ASUS Tech.Inc.";
const PACKET_SIZE: usize = 640;
// Only these two packets must be 17 bytes
const DEV_PAGE: u8 = 0x5e;
// These bytes are in [1] position of the array
const WRITE: u8 = 0xc0;
const INIT: u8 = 0xc2;
const SET: u8 = 0xc3;
const APPLY: u8 = 0xc4;
// Used to turn the panel on and off
// The next byte can be 0x03 for "on" and 0x00 for "off"
const ON_OFF: u8 = 0x04;
use log::{error, info, warn};
use rog_anime::{AniMeDataBuffer, AniMePacketType};
use rog_anime::{
usb::{
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
VENDOR_ID,
},
AnimeDataBuffer, AnimePacketType,
};
use rog_types::supported::AnimeSupportedFunctions;
use rusb::{Device, DeviceHandle};
use std::error::Error;
@@ -28,7 +19,7 @@ impl GetSupported for CtrlAnimeDisplay {
type A = AnimeSupportedFunctions;
fn get_supported() -> Self::A {
AnimeSupportedFunctions(CtrlAnimeDisplay::get_device(0x0b05, 0x193b).is_ok())
AnimeSupportedFunctions(CtrlAnimeDisplay::get_device(VENDOR_ID, PROD_ID).is_ok())
}
}
@@ -36,16 +27,6 @@ pub struct CtrlAnimeDisplay {
handle: DeviceHandle<rusb::GlobalContext>,
}
//AnimatrixWrite
pub trait Dbus {
/// Write a direct stream of data
fn write(&self, input: AniMeDataBuffer);
fn set_on_off(&self, status: bool);
fn set_boot_on_off(&self, status: bool);
}
impl crate::ZbusAdd for CtrlAnimeDisplay {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
@@ -62,30 +43,19 @@ impl crate::ZbusAdd for CtrlAnimeDisplay {
}
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl Dbus for CtrlAnimeDisplay {
impl CtrlAnimeDisplay {
/// Writes a data stream of length
fn write(&self, input: AniMeDataBuffer) {
fn write(&self, input: AnimeDataBuffer) {
self.write_data_buffer(input);
}
fn set_on_off(&self, status: bool) {
let mut buffer = [0u8; PACKET_SIZE];
buffer[0] = DEV_PAGE;
buffer[1] = WRITE;
buffer[2] = ON_OFF;
if status {
buffer[3] = 0x03;
} else {
buffer[3] = 0x00;
}
self.write_bytes(&buffer);
self.write_bytes(&pkt_for_set_on(status));
}
fn set_boot_on_off(&self, status: bool) {
self.do_set_boot(status);
self.do_apply();
fn set_boot_on_off(&self, on: bool) {
self.write_bytes(&pkt_for_set_boot(on));
self.write_bytes(&pkt_for_apply());
}
}
@@ -115,7 +85,6 @@ impl CtrlAnimeDisplay {
Ok(ctrl)
}
#[inline]
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
for device in rusb::devices()?.iter() {
let device_desc = device.device_descriptor()?;
@@ -126,8 +95,6 @@ impl CtrlAnimeDisplay {
Err(rusb::Error::NoDevice)
}
/// Should only be used if the bytes you are writing are verified correct
#[inline]
fn write_bytes(&self, message: &[u8]) {
match self.handle.write_control(
0x21, // request_type
@@ -144,63 +111,18 @@ impl CtrlAnimeDisplay {
},
}
}
#[inline]
fn write_data_buffer(&self, buffer: AniMeDataBuffer) {
let data = AniMePacketType::from(buffer);
fn write_data_buffer(&self, buffer: AnimeDataBuffer) {
let data = AnimePacketType::from(buffer);
for row in data.iter() {
self.write_bytes(row);
}
self.do_flush();
self.write_bytes(&pkt_for_flush());
}
#[inline]
fn do_initialization(&self) {
let mut init = [0; PACKET_SIZE];
init[0] = DEV_PAGE; // This is the USB page we're using throughout
for (idx, byte) in INIT_STR.as_bytes().iter().enumerate() {
init[idx + 1] = *byte
}
self.write_bytes(&init);
// clear the init array and write other init message
for ch in init.iter_mut() {
*ch = 0;
}
init[0] = DEV_PAGE; // write it to be sure?
init[1] = INIT;
self.write_bytes(&init);
}
#[inline]
fn do_flush(&self) {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = WRITE;
flush[2] = 0x03;
self.write_bytes(&flush);
}
#[inline]
fn do_set_boot(&self, status: bool) {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = SET;
flush[2] = 0x01;
flush[3] = if status { 0x00 } else { 0x80 };
self.write_bytes(&flush);
}
#[inline]
fn do_apply(&self) {
let mut flush = [0; PACKET_SIZE];
flush[0] = DEV_PAGE;
flush[1] = APPLY;
flush[2] = 0x01;
flush[3] = 0x80;
self.write_bytes(&flush);
let pkts = pkts_for_init();
self.write_bytes(&pkts[0]);
self.write_bytes(&pkts[1]);
}
}

View File

@@ -62,12 +62,6 @@ impl DbusKbdBacklight {
}
}
trait Dbus {
fn set_led(&mut self, data: String);
fn ledmode(&self) -> String;
fn notify_led(&self, data: &str) -> zbus::Result<()>;
}
impl crate::ZbusAdd for DbusKbdBacklight {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server

View File

@@ -1,6 +0,0 @@
See https://blog.joshwalsh.me/asus-anime-matrix/ for details on how
the diagonal layout works.
`diagonal-template.*` is provided from the website above. It is best to
export the final file to 36px height - no scaling is done in asusd or
rog-anime crate for diagonal displays.

View File

@@ -1,28 +1,31 @@
[package]
name = "rog_anime"
version = "1.0.0"
version = "1.0.2"
license = "MPL-2.0"
readme = "README.md"
authors = ["Luke <luke@ljones.dev>"]
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl"
documentation = "https://docs.rs/rog-anime"
description = "Types useful for translating images and other data for display on the ASUS AniMe Matrix display"
keywords = ["ROG", "ASUS", "AniMe"]
edition = "2018"
exclude = ["data"]
[features]
default = ["dbus"]
dbus = ["zbus", "zvariant", "zvariant_derive"]
[dependencies]
png_pong = "^0.8.0"
pix = "0.13"
gif = "^0.11.2"
serde = "^1.0"
serde_derive = "^1.0"
zbus = "^1.9.1"
zvariant = "^2.5"
zvariant_derive = "^2.5"
glam = { version = "*", features = ["serde"] }
glam = { version = "0.14.0", features = ["serde"] }
[features]
default = ["z"]
z = []
zbus = { version = "^1.9.1", optional = true }
zvariant = { version = "^2.6", optional = true }
zvariant_derive = { version = "^2.6", optional = true }

View File

@@ -1,56 +1,97 @@
# rog-anime
## Features
`rog-anime` is a crate for use with ASUS laptops that have an AniMe matrix display built in to them. The crate can be used with zbus to communicate with the `asusd` daemon from the project this crate is part of, or it can be used standalone to write the data directly to USB by transforming the data to USB HID packets using builtin functions.
`zbus` is enabled by default.
Supported so far is:
- Sequences of data,
- Data can be:
+ Image<scale, rotate, position>
+ ASUS style gif
+ Plain image type gif
+ Pause
- Create USB HID packets for writing to the device
This crate is mostly purpose built for use with [`asus-nb-ctrl`](https://gitlab.com/asus-linux/asus-nb-ctrl) which is a complete daemon and toolset for Linux on ASUS ROG/TUF machines, but can be used in general for example building a new controller for Windows OS.
## Feature enablement
`dbus` is enabled by default - this uses `zvariant` to enable sending some types over dbus interfaces.
## Example
```rust
use std::{env, error::Error, path::Path, process::exit};
use rog_dbus::AuraDbusClient;
use rog_anime::{
anime_data::AniMeDataBuffer,
anime_image::{AnimeImage, Vec2},
use std::{
env, error::Error, f32::consts::PI, path::Path, process::exit, thread::sleep, time::Duration,
};
use rog_anime::{
AniMeDataBuffer, {AniMeImage, Vec2},
};
use rog_dbus::AuraDbusClient;
fn main() -> Result<(), Box<dyn Error>> {
let (client, _) = AuraDbusClient::new().unwrap();
let args: Vec<String> = env::args().into_iter().collect();
if args.len() != 8 {
println!(
"Usage: <filepath> <x scale> <y scale> <angle> <x pos> <y pos> <fineness> <brightness>"
);
println!("e.g, asusctl/examples/doom_large.png 0.9 0.9 0.4 0.0 0.0, 0.8");
println!("All args except path and fineness are floats");
exit(-1);
}
let image = AnimeImage::from_png(
Path::new(&args[1]),
Vec2::new(
args[2].parse::<f32>().unwrap(),
args[3].parse::<f32>().unwrap(),
),
args[4].parse::<f32>().unwrap(),
Vec2::new(
args[5].parse::<f32>().unwrap(),
args[6].parse::<f32>().unwrap(),
),
args[7].parse::<f32>().unwrap(),
let mut image = AniMeImage::from_png(
Path::new("./doom.png"),
0.9, // scale
0.0, // rotation
Vec2::new(0.0, 0.0), // position
0.3, // brightness
)?;
/// This data can also be written direct to the USB device by transforming with
let data = AniMePacketType::from(image);
let data = <AniMeDataBuffer>::from(&image);
client
.proxies()
.anime()
.write(data)
.unwrap();
loop {
image.angle += 0.05;
if image.angle > PI * 2.0 {
image.angle = 0.0
}
image.update();
Ok(())
client
.proxies()
.anime()
.write(<AniMeDataBuffer>::from(&image))
.unwrap();
sleep(Duration::from_micros(500));
}
}
```
```
## Example, USB HID
```rust
let mut image = AniMeImage::from_png(
Path::new("./doom.png"),
0.9, // scale
0.0, // rotation
Vec2::new(0.0, 0.0), // position
0.3, // brightness
)?;
// convert to intermediate packet format
let buffer = <AniMeDataBuffer>::from(&image)
// then to USB HID
let data = AniMePacketType::from(buffer);
// and then write direct
for packet in data.iter() {
write_usb(packet); // some usb call here
}
```
## data
- `data/controller.gif` is an example ASUS diagonally orientated gif.
- `data/diagonal-template.*` are templates for diagonal images or gifs.
See https://blog.joshwalsh.me/asus-anime-matrix/ for details on how
the diagonal layout works.
`diagonal-template.*` is provided from the website above. It is best to
export the final file to 36px height - no scaling is done in asusd or
rog-anime crate for diagonal displays.
## TODO:
- Diagonal font and text
- General font and text
- System info for Linux and Windows
- Audio EQ visual for Linux and Windows
- Time+Date display

View File

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

Before

Width:  |  Height:  |  Size: 323 KiB

After

Width:  |  Height:  |  Size: 323 KiB

View File

@@ -1,5 +1,5 @@
use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "z")]
#[cfg(feature = "dbus")]
use zvariant_derive::Type;
/// The first 7 bytes of a USB packet are accounted for by `USB_PREFIX1` and `USB_PREFIX2`
@@ -16,40 +16,51 @@ const USB_PREFIX2: [u8; 7] = [0x5e, 0xc0, 0x02, 0x74, 0x02, 0x73, 0x02];
/// The minimal serializable data that can be transferred over wire types.
/// Other data structures in `rog_anime` will convert to this.
#[cfg_attr(feature = "z", derive(Type))]
#[cfg_attr(feature = "dbus", derive(Type))]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AniMeDataBuffer(Vec<u8>);
pub struct AnimeDataBuffer(Vec<u8>);
impl Default for AniMeDataBuffer {
impl Default for AnimeDataBuffer {
fn default() -> Self {
Self::new()
}
}
impl AniMeDataBuffer {
impl AnimeDataBuffer {
#[inline]
pub fn new() -> Self {
AniMeDataBuffer(vec![0u8; ANIME_DATA_LEN])
AnimeDataBuffer(vec![0u8; ANIME_DATA_LEN])
}
/// Get the inner data buffer
#[inline]
pub fn get(&self) -> &[u8] {
&self.0
}
/// Get a mutable slice of the inner buffer
#[inline]
pub fn get_mut(&mut self) -> &mut [u8] {
&mut self.0
}
/// Create from a vector of bytes
///
/// # Panics
/// Will panic if the vector length is not `ANIME_DATA_LEN`
#[inline]
pub fn from_vec(input: Vec<u8>) -> Self {
assert_eq!(input.len(), ANIME_DATA_LEN);
Self(input)
}
}
/// The two packets to be written to USB
pub type AniMePacketType = [[u8; 640]; 2];
pub type AnimePacketType = [[u8; 640]; 2];
impl From<AniMeDataBuffer> for AniMePacketType {
impl From<AnimeDataBuffer> for AnimePacketType {
#[inline]
fn from(anime: AniMeDataBuffer) -> Self {
fn from(anime: AnimeDataBuffer) -> Self {
assert!(anime.0.len() == ANIME_DATA_LEN);
let mut buffers = [[0; 640]; 2];
for (idx, chunk) in anime.0.as_slice().chunks(PANE_LEN).enumerate() {

View File

@@ -1,7 +1,7 @@
use std::{path::Path, time::Duration};
use crate::{
data::{AniMeDataBuffer, ANIME_DATA_LEN},
data::{AnimeDataBuffer, ANIME_DATA_LEN},
error::AnimeError,
};
@@ -10,19 +10,22 @@ const HEIGHT: usize = 36;
/// Mostly intended to be used with ASUS gifs, but can be used for other purposes (like images)
#[derive(Debug, Clone)]
pub struct AniMeDiagonal([[u8; WIDTH]; HEIGHT], Option<Duration>);
pub struct AnimeDiagonal([[u8; WIDTH]; HEIGHT], Option<Duration>);
impl Default for AniMeDiagonal {
impl Default for AnimeDiagonal {
#[inline]
fn default() -> Self {
Self::new(None)
}
}
impl AniMeDiagonal {
impl AnimeDiagonal {
#[inline]
pub fn new(duration: Option<Duration>) -> Self {
Self([[0u8; WIDTH]; HEIGHT], duration)
}
#[inline]
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] {
&mut self.0
}
@@ -39,6 +42,7 @@ impl AniMeDiagonal {
/// Generate the base image from inputs. The result can be displayed as is or
/// updated via scale, position, or angle then displayed again after `update()`.
#[inline]
pub fn from_png(
path: &Path,
duration: Option<Duration>,
@@ -50,7 +54,7 @@ impl AniMeDiagonal {
let decoder = png_pong::Decoder::new(data)?.into_steps();
let png_pong::Step { raster, delay: _ } = decoder.last().ok_or(AnimeError::NoFrames)??;
let mut matrix = AniMeDiagonal::new(duration);
let mut matrix = AnimeDiagonal::new(duration);
let width;
match raster {
@@ -70,11 +74,11 @@ impl AniMeDiagonal {
}
}
impl From<&AniMeDiagonal> for AniMeDataBuffer {
/// Do conversion from the nested Vec in AniMeMatrix to the two required
impl From<&AnimeDiagonal> for AnimeDataBuffer {
/// Do conversion from the nested Vec in AnimeMatrix to the two required
/// packets suitable for sending over USB
#[inline]
fn from(anime: &AniMeDiagonal) -> Self {
fn from(anime: &AnimeDiagonal) -> Self {
let mut buf = vec![0u8; ANIME_DATA_LEN];
buf[1..=32].copy_from_slice(&anime.get_row(0, 3, 32));
@@ -133,6 +137,6 @@ impl From<&AniMeDiagonal> for AniMeDataBuffer {
buf[1226..=1234].copy_from_slice(&anime.get_row(50, 0, 9));
buf[1236..=1244].copy_from_slice(&anime.get_row(51, 0, 9));
AniMeDataBuffer::from_vec(buf)
AnimeDataBuffer::from_vec(buf)
}
}

View File

@@ -3,7 +3,7 @@ use png_pong::decode::Error as PngError;
use std::error::Error;
use std::fmt;
#[cfg(feature = "z")]
#[cfg(feature = "dbus")]
use zbus::fdo;
#[derive(Debug)]
@@ -15,12 +15,12 @@ pub enum AnimeError {
Format,
/// The input was incorrect size, expected size is `IncorrectSize(width, height)`
IncorrectSize(u32, u32),
#[cfg(feature = "z")]
#[cfg(feature = "dbus")]
Zbus(fdo::Error)
}
impl fmt::Display for AnimeError {
// This trait requires `fmt` with this exact signature.
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AnimeError::NoFrames => write!(f, "No frames in PNG"),
@@ -33,7 +33,7 @@ impl fmt::Display for AnimeError {
"The input image size is incorrect, expected {}x{}",
width, height
),
#[cfg(feature = "z")]
#[cfg(feature = "dbus")]
AnimeError::Zbus(e) => write!(f, "ZBUS error: {}", e),
}
}
@@ -42,25 +42,29 @@ impl fmt::Display for AnimeError {
impl Error for AnimeError {}
impl From<std::io::Error> for AnimeError {
#[inline]
fn from(err: std::io::Error) -> Self {
AnimeError::Io(err)
}
}
impl From<PngError> for AnimeError {
#[inline]
fn from(err: PngError) -> Self {
AnimeError::Png(err)
}
}
impl From<DecodingError> for AnimeError {
#[inline]
fn from(err: DecodingError) -> Self {
AnimeError::Gif(err)
}
}
#[cfg(feature = "z")]
#[cfg(feature = "dbus")]
impl From<AnimeError> for fdo::Error {
#[inline]
fn from(err: AnimeError) -> Self {
fdo::Error::Failed(format!("{}", err))
}

View File

@@ -2,35 +2,43 @@ use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use std::{fs::File, path::Path, time::Duration};
use crate::{error::AnimeError, AniMeDataBuffer, AniMeDiagonal, AniMeImage, Pixel};
use crate::{error::AnimeError, AnimeDataBuffer, AnimeDiagonal, AnimeImage, Pixel};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AniMeFrame {
pub struct AnimeFrame {
/// Precomputed data for the frame. This can be transferred directly to the
/// the `asusd` daemon over dbus or converted to USB packet with `AniMePacketType::from(buffer)`
data: AniMeDataBuffer,
/// the `asusd` daemon over dbus or converted to USB packet with `AnimePacketType::from(buffer)`
data: AnimeDataBuffer,
delay: Duration,
}
impl AniMeFrame {
pub fn frame(&self) -> &AniMeDataBuffer {
impl AnimeFrame {
/// Get the inner data buffer of the gif frame
#[inline]
pub fn frame(&self) -> &AnimeDataBuffer {
&self.data
}
/// Get the `Duration` of the delay for this frame
#[inline]
pub fn delay(&self) -> Duration {
self.delay
}
}
/// Defines the time or animation cycle count to use for a gif
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
pub enum AnimTime {
/// Time in milliseconds for animation to run
Time(Duration),
/// How many full animation loops to run
Cycles(u32),
/// Run for infinite time
Infinite,
}
impl Default for AnimTime {
#[inline]
fn default() -> Self {
Self::Infinite
}
@@ -39,16 +47,17 @@ impl Default for AnimTime {
/// A gif animation. This is a collection of frames from the gif, and a duration
/// that the animation should be shown for.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AniMeGif(Vec<AniMeFrame>, AnimTime);
pub struct AnimeGif(Vec<AnimeFrame>, AnimTime);
impl AniMeGif {
impl AnimeGif {
/// Create an animation using the 74x36 ASUS gif format
#[inline]
pub fn create_diagonal_gif(
file_name: &Path,
duration: AnimTime,
brightness: f32,
) -> Result<Self, AnimeError> {
let mut matrix = AniMeDiagonal::new(None);
let mut matrix = AnimeDiagonal::new(None);
let mut decoder = gif::DecodeOptions::new();
// Configure the decoder such that it will expand the image to RGBA.
@@ -74,8 +83,8 @@ impl AniMeGif {
}
}
frames.push(AniMeFrame {
data: <AniMeDataBuffer>::from(&matrix),
frames.push(AnimeFrame {
data: <AnimeDataBuffer>::from(&matrix),
delay: Duration::from_millis(wait as u64),
});
}
@@ -84,6 +93,7 @@ impl AniMeGif {
/// Create an animation using a gif of any size. This method must precompute the
/// result.
#[inline]
pub fn create_png_gif(
file_name: &Path,
scale: f32,
@@ -105,7 +115,7 @@ impl AniMeGif {
let width = decoder.width();
let pixels: Vec<Pixel> =
vec![Pixel::default(); (decoder.width() as u32 * decoder.height() as u32) as usize];
let mut image = AniMeImage::new(
let mut image = AnimeImage::new(
Vec2::new(scale, scale),
angle,
translation,
@@ -119,7 +129,7 @@ impl AniMeGif {
if matches!(frame.dispose, gif::DisposalMethod::Background) {
let pixels: Vec<Pixel> =
vec![Pixel::default(); (width as u32 * height as u32) as usize];
image = AniMeImage::new(
image = AnimeImage::new(
Vec2::new(scale, scale),
angle,
translation,
@@ -144,18 +154,22 @@ impl AniMeGif {
}
image.update();
frames.push(AniMeFrame {
data: <AniMeDataBuffer>::from(&image),
frames.push(AnimeFrame {
data: <AnimeDataBuffer>::from(&image),
delay: Duration::from_millis(wait as u64),
});
}
Ok(Self(frames, duration))
}
pub fn frames(&self) -> &[AniMeFrame] {
/// Get a slice of the frames this gif has
#[inline]
pub fn frames(&self) -> &[AnimeFrame] {
&self.0
}
/// Get the time/count for this gif
#[inline]
pub fn duration(&self) -> AnimTime {
self.1
}

View File

@@ -1,6 +1,6 @@
use std::time::Duration;
use crate::data::{AniMeDataBuffer, ANIME_DATA_LEN};
use crate::data::{AnimeDataBuffer, ANIME_DATA_LEN};
use crate::image::LED_IMAGE_POSITIONS;
const WIDTH: usize = 33;
@@ -8,35 +8,45 @@ const HEIGHT: usize = 55;
/// Helper structure for writing images.
///
/// See the examples for ways to write an image to `AniMeMatrix` format.
/// See the examples for ways to write an image to `AnimeMatrix` format.
/// Width = 33
/// height = 55
///
/// **Note:** the columns in each odd row are offset by half a pixel left
#[derive(Debug, Clone)]
pub struct AniMeGrid([[u8; WIDTH]; HEIGHT], Option<Duration>);
pub struct AnimeGrid([[u8; WIDTH]; HEIGHT], Option<Duration>);
impl Default for AniMeGrid {
impl Default for AnimeGrid {
#[inline]
fn default() -> Self {
Self::new(None)
}
}
impl AniMeGrid {
impl AnimeGrid {
#[inline]
pub fn new(duration: Option<Duration>) -> Self {
AniMeGrid([[0u8; WIDTH]; HEIGHT], duration)
AnimeGrid([[0u8; WIDTH]; HEIGHT], duration)
}
/// Set a position in the grid with a brightness value
#[inline]
pub fn set(&mut self, x: usize, y: usize, b: u8) {
self.0[y][x] = b;
}
#[inline]
pub fn get(&self) -> &[[u8; WIDTH]; HEIGHT] {
&self.0
}
#[inline]
pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] {
&mut self.0
}
/// Fill the grid with a value
#[inline]
pub fn fill_with(&mut self, fill: u8) {
for row in self.0.iter_mut() {
for x in row.iter_mut() {
@@ -79,11 +89,11 @@ impl AniMeGrid {
// }
}
impl From<AniMeGrid> for AniMeDataBuffer {
impl From<AnimeGrid> for AnimeDataBuffer {
/// 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 from(anime: AnimeGrid) -> Self {
let mut buf = vec![0u8; ANIME_DATA_LEN];
for (idx, pos) in LED_IMAGE_POSITIONS.iter().enumerate() {
@@ -93,7 +103,7 @@ impl From<AniMeGrid> for AniMeDataBuffer {
buf[idx + 1] = anime.0[y][x];
}
}
AniMeDataBuffer::from_vec(buf)
AnimeDataBuffer::from_vec(buf)
}
}
@@ -103,7 +113,7 @@ mod tests {
#[test]
fn check_data_alignment() {
let mut matrix = AniMeGrid::new(None);
let mut matrix = AnimeGrid::new(None);
{
let tmp = matrix.get_mut();
for row in tmp.iter_mut() {
@@ -112,7 +122,7 @@ mod tests {
}
}
let matrix = <AniMeDataBuffer>::from(matrix);
let matrix = <AnimeDataBuffer>::from(matrix);
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,

View File

@@ -4,12 +4,13 @@ pub use glam::Vec2;
use glam::{Mat3, Vec3};
use crate::{
data::{AniMeDataBuffer, ANIME_DATA_LEN},
data::{AnimeDataBuffer, ANIME_DATA_LEN},
error::AnimeError,
};
const LED_PIXEL_LEN: usize = 1244;
/// A single greyscale + alpha pixel in the image
#[derive(Copy, Clone, Debug)]
pub(crate) struct Pixel {
pub color: u32,
@@ -17,6 +18,7 @@ pub(crate) struct Pixel {
}
impl Default for Pixel {
#[inline]
fn default() -> Self {
Pixel {
color: 0,
@@ -58,10 +60,11 @@ impl Led {
/// Container of `Led`, each of which specifies a position within the image
/// The main use of this is to position and sample colours for the final image
/// to show on AniMe
pub struct AniMeImage {
pub struct AnimeImage {
pub scale: Vec2,
/// Angle in radians
pub angle: f32,
/// Position of the image ont he display
pub translation: Vec2,
/// Brightness of final image, `0.0` = off, `1.0` = full
pub bright: f32,
@@ -69,10 +72,11 @@ pub struct AniMeImage {
led_pos: [Option<Led>; LED_PIXEL_LEN],
/// THe image data for sampling
img_pixels: Vec<Pixel>,
/// width of the image
width: u32,
}
impl AniMeImage {
impl AnimeImage {
pub(crate) const fn new(
scale: Vec2,
angle: f32,
@@ -119,6 +123,7 @@ impl AniMeImage {
36 - (y + 1) / 2
}
/// Physical display width
fn phys_width() -> f32 {
(32.0 - -0.5 + 1.0) * Self::scale_x()
}
@@ -128,10 +133,12 @@ impl AniMeImage {
55
}
/// Physical display height
fn phys_height() -> f32 {
(54.0 + 1.0) * Self::scale_y()
}
/// Find the actual width of the data including the dead pixels
const fn pitch(y: u32) -> u32 {
match y {
0 | 2 | 4 => 33,
@@ -145,12 +152,13 @@ impl AniMeImage {
}
/// Really only used to generate the output for including as a full const in `LED_IMAGE_POSITIONS`
#[inline]
pub fn generate() -> Vec<Option<Led>> {
(0..AniMeImage::height())
(0..AnimeImage::height())
.flat_map(|y| {
(0..AniMeImage::pitch(y)).map(move |l| {
if l < AniMeImage::width(y) {
let x = AniMeImage::first_x(y) + l;
(0..AnimeImage::pitch(y)).map(move |l| {
if l < AnimeImage::width(y) {
let x = AnimeImage::first_x(y) + l;
Some(Led::new(x as f32 - 0.5 * (y % 2) as f32, y as f32))
} else {
None
@@ -163,6 +171,7 @@ impl AniMeImage {
/// Called after setting new angle, position, or scale to refresh the image
/// samples, the result can then been transformed to the appropriate data
/// for displaying
#[inline]
pub fn update(&mut self) {
let width = self.width as i32;
let height = self.img_pixels.len() as i32 / width;
@@ -204,12 +213,13 @@ impl AniMeImage {
}
}
/// Put the render window in place on the image
fn put(&self, bmp_w: f32, bmp_h: f32) -> Mat3 {
// Center of image
let center = Mat3::from_translation(Vec2::new(-0.5 * bmp_w, -0.5 * bmp_h));
// Find the scale required for cleanly showing the image
let h = AniMeImage::phys_height() / bmp_h;
let mut base_scale = AniMeImage::phys_width() / bmp_w;
let h = AnimeImage::phys_height() / bmp_h;
let mut base_scale = AnimeImage::phys_width() / bmp_w;
if base_scale > h {
base_scale = h;
}
@@ -217,8 +227,8 @@ impl AniMeImage {
let cm_from_px = Mat3::from_scale(Vec2::new(base_scale, base_scale));
let led_from_cm = Mat3::from_scale(Vec2::new(
1.0 / AniMeImage::scale_x(),
1.0 / AniMeImage::scale_y(),
1.0 / AnimeImage::scale_x(),
1.0 / AnimeImage::scale_y(),
));
let transform =
@@ -233,6 +243,7 @@ impl AniMeImage {
/// Generate the base image from inputs. The result can be displayed as is or
/// updated via scale, position, or angle then displayed again after `update()`.
#[inline]
pub fn from_png(
path: &Path,
scale: f32,
@@ -261,7 +272,7 @@ impl AniMeImage {
_ => return Err(AnimeError::Format),
};
let mut matrix = AniMeImage::new(
let mut matrix = AnimeImage::new(
Vec2::new(scale, scale),
angle,
translation,
@@ -275,11 +286,11 @@ impl AniMeImage {
}
}
impl From<&AniMeImage> for AniMeDataBuffer {
impl From<&AnimeImage> for AnimeDataBuffer {
/// Do conversion from the nested Vec in AniMeMatrix to the two required
/// packets suitable for sending over USB
#[inline]
fn from(leds: &AniMeImage) -> Self {
fn from(leds: &AnimeImage) -> Self {
let mut l: Vec<u8> = leds
.led_pos
.iter()
@@ -289,7 +300,7 @@ impl From<&AniMeImage> for AniMeDataBuffer {
v.push(0);
v.append(&mut l);
v.append(&mut vec![0u8; 9]);
AniMeDataBuffer::from_vec(v)
AnimeDataBuffer::from_vec(v)
}
}
@@ -1550,7 +1561,7 @@ mod tests {
#[test]
fn led_positions() {
let leds = AniMeImage::generate();
let leds = AnimeImage::generate();
assert_eq!(leds[0], Some(Led(0.0, 0.0, 0)));
assert_eq!(leds[1], Some(Led(1.0, 0.0, 0)));
assert_eq!(leds[2], Some(Led(2.0, 0.0, 0)));
@@ -1579,7 +1590,7 @@ mod tests {
#[test]
fn led_positions_const() {
let leds = AniMeImage::generate();
let leds = AnimeImage::generate();
assert_eq!(leds[1], LED_IMAGE_POSITIONS[1]);
assert_eq!(leds[34], LED_IMAGE_POSITIONS[34]);
assert_eq!(leds[69], LED_IMAGE_POSITIONS[69]);
@@ -1593,44 +1604,44 @@ mod tests {
#[test]
fn row_starts() {
assert_eq!(AniMeImage::first_x(5), 0);
assert_eq!(AniMeImage::first_x(6), 0);
assert_eq!(AniMeImage::first_x(7), 1);
assert_eq!(AniMeImage::first_x(8), 1);
assert_eq!(AniMeImage::first_x(9), 2);
assert_eq!(AniMeImage::first_x(10), 2);
assert_eq!(AniMeImage::first_x(11), 3);
assert_eq!(AnimeImage::first_x(5), 0);
assert_eq!(AnimeImage::first_x(6), 0);
assert_eq!(AnimeImage::first_x(7), 1);
assert_eq!(AnimeImage::first_x(8), 1);
assert_eq!(AnimeImage::first_x(9), 2);
assert_eq!(AnimeImage::first_x(10), 2);
assert_eq!(AnimeImage::first_x(11), 3);
}
#[test]
fn row_widths() {
assert_eq!(AniMeImage::width(5), 33);
assert_eq!(AniMeImage::width(6), 33);
assert_eq!(AniMeImage::width(7), 32);
assert_eq!(AniMeImage::width(8), 32);
assert_eq!(AniMeImage::width(9), 31);
assert_eq!(AniMeImage::width(10), 31);
assert_eq!(AniMeImage::width(11), 30);
assert_eq!(AniMeImage::width(12), 30);
assert_eq!(AniMeImage::width(13), 29);
assert_eq!(AniMeImage::width(14), 29);
assert_eq!(AniMeImage::width(15), 28);
assert_eq!(AniMeImage::width(16), 28);
assert_eq!(AniMeImage::width(17), 27);
assert_eq!(AniMeImage::width(18), 27);
assert_eq!(AnimeImage::width(5), 33);
assert_eq!(AnimeImage::width(6), 33);
assert_eq!(AnimeImage::width(7), 32);
assert_eq!(AnimeImage::width(8), 32);
assert_eq!(AnimeImage::width(9), 31);
assert_eq!(AnimeImage::width(10), 31);
assert_eq!(AnimeImage::width(11), 30);
assert_eq!(AnimeImage::width(12), 30);
assert_eq!(AnimeImage::width(13), 29);
assert_eq!(AnimeImage::width(14), 29);
assert_eq!(AnimeImage::width(15), 28);
assert_eq!(AnimeImage::width(16), 28);
assert_eq!(AnimeImage::width(17), 27);
assert_eq!(AnimeImage::width(18), 27);
}
#[test]
fn row_pitch() {
assert_eq!(AniMeImage::pitch(5), 34);
assert_eq!(AniMeImage::pitch(6), 33);
assert_eq!(AniMeImage::pitch(7), 33);
assert_eq!(AniMeImage::pitch(8), 32);
assert_eq!(AniMeImage::pitch(9), 32);
assert_eq!(AniMeImage::pitch(10), 31);
assert_eq!(AniMeImage::pitch(11), 31);
assert_eq!(AniMeImage::pitch(12), 30);
assert_eq!(AniMeImage::pitch(13), 30);
assert_eq!(AniMeImage::pitch(14), 29);
assert_eq!(AnimeImage::pitch(5), 34);
assert_eq!(AnimeImage::pitch(6), 33);
assert_eq!(AnimeImage::pitch(7), 33);
assert_eq!(AnimeImage::pitch(8), 32);
assert_eq!(AnimeImage::pitch(9), 32);
assert_eq!(AnimeImage::pitch(10), 31);
assert_eq!(AnimeImage::pitch(11), 31);
assert_eq!(AnimeImage::pitch(12), 30);
assert_eq!(AnimeImage::pitch(13), 30);
assert_eq!(AnimeImage::pitch(14), 29);
}
}

View File

@@ -1,10 +1,9 @@
/// The main data conversion for transfering in shortform over dbus or other,
/// or writing directly to the USB device
mod data;
pub use data::*;
/// Useful for specialised effects that required a grid of data
/// Useful for specialised effects that require a grid of data
mod grid;
pub use grid::*;
@@ -12,14 +11,23 @@ pub use grid::*;
mod image;
pub use image::*;
/// A grid of data that is intended to be read out and displayed on the ANiMe as
/// a diagonal
mod diagonal;
pub use diagonal::*;
/// A gif. Can be created from the ASUS gifs which are diagonal layout, or from
/// any standard gif
mod gif;
pub use crate::gif::*;
/// A container of images/grids/gifs/pauses which can be iterated over to generate
/// cool effects
mod sequencer;
pub use sequencer::*;
/// Base errors that are possible
pub mod error;
/// Provides const methods to create the USB HID control packets
pub mod usb;

View File

@@ -6,8 +6,10 @@ use std::{
use glam::Vec2;
use serde_derive::{Deserialize, Serialize};
use crate::{error::AnimeError, AniMeDataBuffer, AniMeGif, AniMeImage, AnimTime};
use crate::{error::AnimeError, AnimeDataBuffer, AnimeGif, AnimeImage, AnimTime};
/// All the possible AniMe actions that can be used. This enum is intended to be
/// a helper for loading up `ActionData`.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum AnimeAction {
/// Full gif sequence. Immutable.
@@ -36,13 +38,14 @@ pub enum AnimeAction {
Pause(Duration),
}
///
/// All the possible AniMe actions that can be used. The enum is intended to be
/// used in a array allowing the user to cycle through a series of actions.
#[derive(Debug, Deserialize, Serialize)]
pub enum ActionData {
/// Full gif sequence. Immutable.
Animation(AniMeGif),
Animation(AnimeGif),
/// Basic image, can have properties changed and image updated via those properties
Image(Box<AniMeDataBuffer>),
Image(Box<AnimeDataBuffer>),
/// A pause to be used between sequences
Pause(Duration),
/// Placeholder
@@ -55,15 +58,19 @@ pub enum ActionData {
Matrix,
}
/// An optimised precomputed set of actions
/// An optimised precomputed set of actions that the user can cycle through
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct Sequences(Vec<ActionData>);
impl Sequences {
#[inline]
pub fn new() -> Self {
Self(Vec::new())
}
/// 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: &AnimeAction) -> Result<(), AnimeError> {
match action {
AnimeAction::AsusAnimation {
@@ -99,6 +106,10 @@ impl Sequences {
Ok(())
}
/// Remove an item at this position from the run buffer. If the `index` supplied
/// is not in range then `None` is returned, otherwise the `ActionData` at that location
/// is yeeted and returned.
#[inline]
pub fn remove_item(&mut self, index: usize) -> Option<ActionData> {
if index < self.0.len() {
return Some(self.0.remove(index));
@@ -116,7 +127,7 @@ impl Sequences {
if index > self.0.len() {
index = self.0.len() - 1;
}
let frames = AniMeGif::create_diagonal_gif(file, duration, brightness)?;
let frames = AnimeGif::create_diagonal_gif(file, duration, brightness)?;
self.0.insert(index, ActionData::Animation(frames));
Ok(())
}
@@ -133,8 +144,8 @@ impl Sequences {
if index > self.0.len() {
index = self.0.len() - 1;
}
let image = AniMeImage::from_png(file, scale, angle, translation, brightness)?;
let data = <AniMeDataBuffer>::from(&image);
let image = AnimeImage::from_png(file, scale, angle, translation, brightness)?;
let data = <AnimeDataBuffer>::from(&image);
self.0.insert(index, ActionData::Image(Box::new(data)));
Ok(())
}
@@ -154,7 +165,7 @@ impl Sequences {
index = self.0.len() - 1;
}
let frames =
AniMeGif::create_png_gif(file, scale, angle, translation, duration, brightness)?;
AnimeGif::create_png_gif(file, scale, angle, translation, duration, brightness)?;
self.0.insert(index, ActionData::Animation(frames));
Ok(())
}
@@ -174,6 +185,7 @@ impl Sequences {
}
}
/// Iteractor helper for iterating over all the actions in `Sequences`
pub struct ActionIterator<'a> {
actions: &'a Sequences,
next_idx: usize,
@@ -182,6 +194,7 @@ pub struct ActionIterator<'a> {
impl<'a> Iterator for ActionIterator<'a> {
type Item = &'a ActionData;
#[inline]
fn next(&mut self) -> Option<&'a ActionData> {
if self.next_idx == self.actions.0.len() {
self.next_idx = 0;

79
rog-anime/src/usb.rs Normal file
View File

@@ -0,0 +1,79 @@
//! Utils for writing to the AniMe USB device
//!
//! Use of the device requires a few steps:
//! 1. Initialise the device by writing the two packets from `get_init_packets()`
//! 2. Write data from `AnimePacketType`
//! 3. Write the packet from `get_flush_packet()`, which tells the device to display the data from step 2
//!
//! Step 1 need to applied only on fresh system boot.
const INIT_STR: [u8; 15] = [
0x5e, b'A', b'S', b'U', b'S', b' ', b'T', b'E', b'C', b'H', b'.', b'I', b'N', b'C', b'.',
];
const PACKET_SIZE: usize = 640;
const DEV_PAGE: u8 = 0x5e;
pub const VENDOR_ID: u16 = 0x0b05;
pub const PROD_ID: u16 = 0x193b;
/// Get the two device initialization packets. These are required for device start
/// after the laptop boots.
#[inline]
pub const fn pkts_for_init() -> [[u8; PACKET_SIZE]; 2] {
let mut packets = [[0; PACKET_SIZE]; 2];
packets[0][0] = DEV_PAGE; // This is the USB page we're using throughout
let mut count = 0;
while count < INIT_STR.len() {
packets[0][count] = INIT_STR[count];
count +=1;
}
//
packets[1][0] = DEV_PAGE; // write it to be sure?
packets[1][1] = 0xc2;
packets
}
/// Should be written to the device after writing the two main data packets that
/// make up the display data packet
#[inline]
pub const fn pkt_for_flush() -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE];
pkt[0] = DEV_PAGE;
pkt[1] = 0xc0;
pkt[2] = 0x03;
pkt
}
/// Get the packet required for setting the device to on, on boot. Requires
/// pkt_for_apply()` to be written after.
#[inline]
pub const fn pkt_for_set_boot(status: bool) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE];
pkt[0] = DEV_PAGE;
pkt[1] = 0xc3;
pkt[2] = 0x01;
pkt[3] = if status { 0x00 } else { 0x80 };
pkt
}
/// Get the packet required for setting the device to on. Requires `pkt_for_apply()`
/// to be written after.
#[inline]
pub const fn pkt_for_set_on(on: bool) -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE];
pkt[0] = DEV_PAGE;
pkt[1] = 0xc0;
pkt[2] = 0x04;
pkt[3] = if on { 0x03 } else { 0x00 };
pkt
}
/// Packet required to apply a device setting
#[inline]
pub const fn pkt_for_apply() -> [u8; PACKET_SIZE] {
let mut pkt = [0; PACKET_SIZE];
pkt[0] = DEV_PAGE;
pkt[1] = 0xc4;
pkt[2] = 0x01;
pkt[3] = 0x80;
pkt
}

View File

@@ -19,7 +19,7 @@
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use rog_anime::AniMeDataBuffer;
use rog_anime::AnimeDataBuffer;
use zbus::{dbus_proxy, Connection, Result};
#[dbus_proxy(
@@ -60,7 +60,7 @@ impl<'a> AnimeProxy<'a> {
}
#[inline]
pub fn write(&self, input: AniMeDataBuffer) -> Result<()> {
pub fn write(&self, input: AnimeDataBuffer) -> Result<()> {
self.0.write(input.get())
}
}