diff --git a/Cargo.lock b/Cargo.lock index db0ac285..d3d1290c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,7 @@ name = "asusctl" version = "3.3.1" dependencies = [ "daemon", + "gif", "glam", "gumdrop", "rog_anime", @@ -177,6 +178,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -440,6 +447,16 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "gif" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a668f699973d0f573d15749b7002a9ac9e1f9c6b220e7b165601334c173d8de" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "glam" version = "0.13.1" @@ -735,12 +752,6 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" -[[package]] -name = "owo-colors" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fe43bf372b08cc9ccee5144715db59c79ab00168bbe4cf0d274dc0d5f64d7f" - [[package]] name = "parking" version = "2.0.0" @@ -948,7 +959,6 @@ name = "rog_anime" version = "1.0.0" dependencies = [ "glam", - "owo-colors", "pix", "png_pong", "serde", @@ -1307,6 +1317,12 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "weezl" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c" + [[package]] name = "wepoll-sys" version = "3.0.1" diff --git a/README.md b/README.md index fd3f2e3c..9f363dce 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,11 @@ will probably suffer another rename once it becomes generic enough to do so. - `nvidia`, uses the Nvidia gpu only - `vfio`, binds the Nvidia gpu to vfio for VM pass-through +**Rebootless note:** You must edit `/etc/default/grub` to remove `nvidia-drm.modeset=1` +from the line `GRUB_CMDLINE_LINUX=` and then recreate your grub config. In fedora +you can do this with `sudo grub2-mkconfig -o /etc/grub2.cfg` - other distro may be +similar but with a different config location. + This can be disabled in the config with `"manage_gfx": false,`. Additionally there is an extra setting for laptops capable of g-sync dedicated gfx mode to enable the graphics switching to switch on dedicated gfx for "nvidia" mode. diff --git a/asusctl/Cargo.toml b/asusctl/Cargo.toml index 5145ddfe..b3141b49 100644 --- a/asusctl/Cargo.toml +++ b/asusctl/Cargo.toml @@ -19,4 +19,5 @@ yansi-term = "^0.1" [dev-dependencies] tinybmp = "^0.2.3" glam = "*" -rog_dbus = { path = "../rog-dbus" } \ No newline at end of file +rog_dbus = { path = "../rog-dbus" } +gif = "^0.11.2" \ No newline at end of file diff --git a/asusctl/examples/animatrix-diag-png.rs b/asusctl/examples/animatrix-diag-png.rs new file mode 100644 index 00000000..40a9e7b3 --- /dev/null +++ b/asusctl/examples/animatrix-diag-png.rs @@ -0,0 +1,41 @@ +use std::{env, error::Error, path::Path, process::exit}; + +use rog_anime::{ + AniMeDataBuffer, {AniMeDiagonal, Vec2}, +}; +use rog_dbus::AuraDbusClient; + +fn main() -> Result<(), Box> { + let (client, _) = AuraDbusClient::new().unwrap(); + + let args: Vec = env::args().into_iter().collect(); + if args.len() != 7 { + println!( + "Usage: " + ); + 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 matrix = AniMeDiagonal::from_png( + Path::new(&args[1]), + Vec2::new( + args[2].parse::().unwrap(), + args[3].parse::().unwrap(), + ), + Vec2::new( + args[4].parse::().unwrap(), + args[5].parse::().unwrap(), + ), + args[6].parse::().unwrap(), + )?; + + client + .proxies() + .anime() + .write(::from(&matrix)) + .unwrap(); + + Ok(()) +} diff --git a/asusctl/examples/animatrix-diag.rs b/asusctl/examples/animatrix-diag.rs new file mode 100644 index 00000000..7bb9d126 --- /dev/null +++ b/asusctl/examples/animatrix-diag.rs @@ -0,0 +1,33 @@ +use std::{thread::sleep, time::Duration}; + +use rog_anime::{AniMeDataBuffer, AniMeDiagonal}; +use rog_dbus::AuraDbusClient; + +// In usable data: +// Top row start at 1, ends at 32 + +// 74w x 36h diagonal used by the windows app + +fn main() { + let (client, _) = AuraDbusClient::new().unwrap(); + + + for step in (2..50).rev() { + let mut matrix = AniMeDiagonal::new(); + for c in (0..60).into_iter().step_by(step) { + for i in matrix.get_mut().iter_mut() { + i[c] = 50; + } + } + + for c in (0..35).into_iter().step_by(step) { + for i in matrix.get_mut()[c].iter_mut() { + *i = 50; + } + } + + let m = ::from(&matrix); + client.proxies().anime().write(m).unwrap(); + sleep(Duration::from_millis(300)); + } +} diff --git a/asusctl/examples/animatrix-gif.rs b/asusctl/examples/animatrix-gif.rs new file mode 100644 index 00000000..8b7dd58e --- /dev/null +++ b/asusctl/examples/animatrix-gif.rs @@ -0,0 +1,60 @@ +use std::{env, fs::File, path::Path, thread::sleep, time::Duration}; + +use rog_anime::{AniMeDataBuffer, AniMeDiagonal}; +use rog_dbus::AuraDbusClient; + +struct AniMeFrame { + data: AniMeDataBuffer, + delay: Duration, +} + +fn main() { + let (client, _) = AuraDbusClient::new().unwrap(); + + let args: Vec = env::args().into_iter().collect(); + // if args.len() != 7 { + // exit(-1); + // } + + let mut frames = Vec::new(); + let mut matrix = AniMeDiagonal::new(); + + let mut decoder = gif::DecodeOptions::new(); + // Configure the decoder such that it will expand the image to RGBA. + decoder.set_color_output(gif::ColorOutput::RGBA); + // Read the file header + let file = File::open(Path::new(&args[1])).unwrap(); + let mut decoder = decoder.read_info(file).unwrap(); + + while let Some(frame) = decoder.read_next_frame().unwrap() { + let wait = frame.delay; + for (y, row) in frame.buffer.chunks(frame.width as usize * 4).enumerate() { + for (x, px) in row.chunks(4).enumerate() { + if px[3] != 255 { + // 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]; + } + } + client + .proxies() + .anime() + .write(::from(&matrix)) + .unwrap(); + + frames.push(AniMeFrame { + data: ::from(&matrix), + delay: Duration::from_millis(wait as u64), + }); + + sleep(Duration::from_millis(wait as u64)); + } + + loop { + for frame in frames.iter() { + client.proxies().anime().write(frame.data.clone()).unwrap(); + sleep(frame.delay); + } + } +} diff --git a/asusctl/examples/animatrix-png.rs b/asusctl/examples/animatrix-png.rs index dc6619a3..a3560ebd 100644 --- a/asusctl/examples/animatrix-png.rs +++ b/asusctl/examples/animatrix-png.rs @@ -11,7 +11,7 @@ fn main() -> Result<(), Box> { let args: Vec = env::args().into_iter().collect(); if args.len() != 8 { println!( - "Usage: " + "Usage: " ); 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"); diff --git a/asusctl/examples/controller.gif b/asusctl/examples/controller.gif new file mode 100644 index 00000000..ace761cd Binary files /dev/null and b/asusctl/examples/controller.gif differ diff --git a/asusctl/examples/controller.png b/asusctl/examples/controller.png new file mode 100644 index 00000000..36e29a15 Binary files /dev/null and b/asusctl/examples/controller.png differ diff --git a/asusctl/examples/diag_sq.png b/asusctl/examples/diag_sq.png new file mode 100644 index 00000000..36e29a15 Binary files /dev/null and b/asusctl/examples/diag_sq.png differ diff --git a/asusctl/examples/levelup.png b/asusctl/examples/levelup.png new file mode 100644 index 00000000..b7a8d333 Binary files /dev/null and b/asusctl/examples/levelup.png differ diff --git a/asusctl/examples/sunset.gif b/asusctl/examples/sunset.gif new file mode 100644 index 00000000..f114859c Binary files /dev/null and b/asusctl/examples/sunset.gif differ diff --git a/rog-anime/Cargo.toml b/rog-anime/Cargo.toml index b22dbe35..75342230 100644 --- a/rog-anime/Cargo.toml +++ b/rog-anime/Cargo.toml @@ -13,7 +13,6 @@ edition = "2018" png_pong = "^0.8.0" glam = "*" pix = "0.13" -owo-colors = "2.0.0" serde = "^1.0" serde_derive = "^1.0" diff --git a/rog-anime/src/anime_diagonal.rs b/rog-anime/src/anime_diagonal.rs new file mode 100644 index 00000000..4f35d5c6 --- /dev/null +++ b/rog-anime/src/anime_diagonal.rs @@ -0,0 +1,145 @@ +use std::path::Path; + +use glam::Vec2; + +use crate::{ + anime_data::{AniMeDataBuffer, ANIME_DATA_LEN}, + error::AnimeError, +}; + +const WIDTH: usize = 74; +const HEIGHT: usize = 36; + +#[derive(Debug, Clone)] +pub struct AniMeDiagonal([[u8; WIDTH]; HEIGHT]); + +impl Default for AniMeDiagonal { + fn default() -> Self { + Self::new() + } +} + +impl AniMeDiagonal { + pub fn new() -> Self { + Self([[0u8; WIDTH]; HEIGHT]) + } + + pub fn get_mut(&mut self) -> &mut [[u8; WIDTH]; HEIGHT] { + &mut self.0 + } + + // use with height - y + const fn dy(x: usize, y: usize) -> usize { + x / 2 + x % 2 + y + } + + fn get_row(&self, x: usize, y: usize, len: usize) -> Vec { + let mut buf = Vec::with_capacity(len); + for i in 0..len { + let val = self.0[HEIGHT - y - i - 1][x + i]; + buf.push(val); + } + buf + } + + /// 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()`. + pub fn from_png( + path: &Path, + scale: Vec2, + offset: Vec2, + bright: f32, + ) -> Result { + use pix::el::Pixel; + let data = std::fs::read(path)?; + let data = std::io::Cursor::new(data); + 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(); + + let width; + match raster { + png_pong::PngRaster::Graya8(ras) => { + width = ras.width(); + for (y, row) in ras.pixels() + .chunks(width as usize).enumerate() { + for (x, px) in row.iter().enumerate() { + let v = ::from(px.one() * bright); + matrix.0[y][x] = v; + } + } + } + _ => return Err(AnimeError::Format), + }; + + Ok(matrix) + } +} + +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 { + let mut buf = vec![0u8; ANIME_DATA_LEN]; + + buf[1..=33].copy_from_slice(&anime.get_row(2, 3, 33)); + buf[34..=66].copy_from_slice(&anime.get_row(2, 2, 33)); + buf[68..=100].copy_from_slice(&anime.get_row(2, 1, 33)); + buf[101..=134].copy_from_slice(&anime.get_row(2, 0, 34)); + buf[136..=169].copy_from_slice(&anime.get_row(3, 0, 34)); + buf[170..=202].copy_from_slice(&anime.get_row(4, 0, 33)); + buf[204..=236].copy_from_slice(&anime.get_row(5, 0, 33)); + buf[237..=268].copy_from_slice(&anime.get_row(6, 0, 32)); + buf[270..=301].copy_from_slice(&anime.get_row(7, 0, 32)); + buf[302..=332].copy_from_slice(&anime.get_row(8, 0, 31)); + buf[334..=364].copy_from_slice(&anime.get_row(9, 0, 31)); + buf[365..=394].copy_from_slice(&anime.get_row(10, 0, 30)); + buf[396..=425].copy_from_slice(&anime.get_row(11, 0, 30)); + buf[426..=454].copy_from_slice(&anime.get_row(12, 0, 29)); + buf[456..=484].copy_from_slice(&anime.get_row(13, 0, 29)); + buf[485..=512].copy_from_slice(&anime.get_row(14, 0, 28)); + buf[514..=541].copy_from_slice(&anime.get_row(15, 0, 28)); + buf[542..=568].copy_from_slice(&anime.get_row(16, 0, 27)); + buf[570..=596].copy_from_slice(&anime.get_row(17, 0, 27)); + buf[597..=622].copy_from_slice(&anime.get_row(18, 0, 26)); + buf[624..=649].copy_from_slice(&anime.get_row(19, 0, 26)); + buf[650..=674].copy_from_slice(&anime.get_row(20, 0, 25)); + buf[676..=700].copy_from_slice(&anime.get_row(21, 0, 25)); + buf[701..=724].copy_from_slice(&anime.get_row(22, 0, 24)); + buf[726..=749].copy_from_slice(&anime.get_row(23, 0, 24)); + buf[750..=772].copy_from_slice(&anime.get_row(24, 0, 23)); + buf[774..=796].copy_from_slice(&anime.get_row(25, 0, 23)); + buf[797..=818].copy_from_slice(&anime.get_row(26, 0, 22)); + buf[820..=841].copy_from_slice(&anime.get_row(27, 0, 22)); + buf[842..=862].copy_from_slice(&anime.get_row(28, 0, 21)); + buf[864..=884].copy_from_slice(&anime.get_row(29, 0, 21)); + buf[885..=904].copy_from_slice(&anime.get_row(30, 0, 20)); + buf[906..=925].copy_from_slice(&anime.get_row(31, 0, 20)); + buf[926..=944].copy_from_slice(&anime.get_row(32, 0, 19)); + buf[946..=964].copy_from_slice(&anime.get_row(33, 0, 19)); + buf[965..=982].copy_from_slice(&anime.get_row(34, 0, 18)); + buf[984..=1001].copy_from_slice(&anime.get_row(35, 0, 18)); + buf[1002..=1018].copy_from_slice(&anime.get_row(36, 0, 17)); + buf[1020..=1036].copy_from_slice(&anime.get_row(37, 0, 17)); + buf[1037..=1052].copy_from_slice(&anime.get_row(38, 0, 16)); + buf[1054..=1069].copy_from_slice(&anime.get_row(39, 0, 16)); + buf[1070..=1084].copy_from_slice(&anime.get_row(40, 0, 15)); + buf[1086..=1100].copy_from_slice(&anime.get_row(41, 0, 15)); + buf[1101..=1114].copy_from_slice(&anime.get_row(42, 0, 14)); + buf[1116..=1129].copy_from_slice(&anime.get_row(43, 0, 14)); + buf[1130..=1142].copy_from_slice(&anime.get_row(44, 0, 13)); + buf[1144..=1156].copy_from_slice(&anime.get_row(45, 0, 13)); + buf[1157..=1168].copy_from_slice(&anime.get_row(46, 0, 12)); + buf[1170..=1181].copy_from_slice(&anime.get_row(47, 0, 12)); + buf[1182..=1192].copy_from_slice(&anime.get_row(48, 0, 11)); + buf[1194..=1204].copy_from_slice(&anime.get_row(49, 0, 11)); + buf[1205..=1214].copy_from_slice(&anime.get_row(50, 0, 10)); + buf[1216..=1225].copy_from_slice(&anime.get_row(51, 0, 10)); + buf[1226..=1234].copy_from_slice(&anime.get_row(52, 0, 9)); + buf[1236..=1244].copy_from_slice(&anime.get_row(53, 0, 9)); + + AniMeDataBuffer::from_vec(buf) + } +} diff --git a/rog-anime/src/anime_grid.rs b/rog-anime/src/anime_grid.rs index 0d0d319a..253f6406 100644 --- a/rog-anime/src/anime_grid.rs +++ b/rog-anime/src/anime_grid.rs @@ -1,6 +1,5 @@ use crate::anime_data::{AniMeDataBuffer, ANIME_DATA_LEN}; use crate::anime_image::LED_IMAGE_POSITIONS; -use owo_colors::{OwoColorize, Rgb}; const WIDTH: usize = 33; const HEIGHT: usize = 55; @@ -44,38 +43,38 @@ impl AniMeGrid { } } - pub fn debug_print(&self) { - // this is the index from right. It is used to progressively shorten rows - let mut prog_row_len = WIDTH - 2; + // pub fn debug_print(&self) { + // // this is the index from right. It is used to progressively shorten rows + // let mut prog_row_len = WIDTH - 2; - for (count, row) in self.0.iter().enumerate() { - // Switch to next block (looks like ) - if count % 2 != 0 { - // Row after 6 is only 1 less, then rows after 7 follow pattern - if count == 7 { - prog_row_len -= 1; - } else { - prog_row_len -= 2; - } - } else { - prog_row_len += 1; // if count 6, 0 - } + // for (count, row) in self.0.iter().enumerate() { + // // Switch to next block (looks like ) + // if count % 2 != 0 { + // // Row after 6 is only 1 less, then rows after 7 follow pattern + // if count == 7 { + // prog_row_len -= 1; + // } else { + // prog_row_len -= 2; + // } + // } else { + // prog_row_len += 1; // if count 6, 0 + // } - let index = row.len() - prog_row_len; + // let index = row.len() - prog_row_len; - if count % 2 == 0 { - print!(" "); - } - for (i, n) in row.iter().enumerate() { - if i >= index { - print!(" {}", "XXX".color(Rgb(0, *n, 0))); - } else { - print!(" "); - } - } - println!(); - } - } + // if count % 2 == 0 { + // print!(" "); + // } + // for (i, n) in row.iter().enumerate() { + // if i >= index { + // print!(" {}", "XXX".color(Rgb(0, *n, 0))); + // } else { + // print!(" "); + // } + // } + // println!(); + // } + // } } impl From for AniMeDataBuffer {