mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Basic fade in/out of gifs
This commit is contained in:
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Extra info output for gfx dev scan
|
- Extra info output for gfx dev scan
|
||||||
- Extra info in log for G-Sync to help prevent user confusion around gfx switching
|
- Extra info in log for G-Sync to help prevent user confusion around gfx switching
|
||||||
- Add GA503Q led modes
|
- Add GA503Q led modes
|
||||||
|
- Added ability to fade in/out gifs and images for anime. This does break anime configs. See manual for details.
|
||||||
|
|
||||||
# [3.6.1] - 2021-05-25
|
# [3.6.1] - 2021-05-25
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -207,7 +207,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "daemon"
|
name = "daemon"
|
||||||
version = "3.6.2"
|
version = "3.6.3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
@@ -232,7 +232,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "daemon-user"
|
name = "daemon-user"
|
||||||
version = "1.1.1"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs 3.0.1",
|
"dirs 3.0.1",
|
||||||
"rog_anime",
|
"rog_anime",
|
||||||
@@ -863,7 +863,7 @@ checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rog_anime"
|
name = "rog_anime"
|
||||||
version = "1.0.4"
|
version = "1.0.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gif",
|
"gif",
|
||||||
"glam",
|
"glam",
|
||||||
|
|||||||
22
MANUAL.md
22
MANUAL.md
@@ -269,6 +269,7 @@ Each object in the array can be one of:
|
|||||||
<FLOAT>,
|
<FLOAT>,
|
||||||
<FLOAT>
|
<FLOAT>
|
||||||
],
|
],
|
||||||
|
"time": <TIME>,
|
||||||
"brightness": <FLOAT>
|
"brightness": <FLOAT>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -322,6 +323,27 @@ A cycle is how many gif loops to run:
|
|||||||
```json
|
```json
|
||||||
"time": "Infinite",
|
"time": "Infinite",
|
||||||
```
|
```
|
||||||
|
`Fade` allows an image or gif to fade in and out, and remain at max brightness to n time:
|
||||||
|
```json
|
||||||
|
"time": {
|
||||||
|
"Fade": {
|
||||||
|
"fade_in": {
|
||||||
|
"secs": 2,
|
||||||
|
"nanos": 0
|
||||||
|
},
|
||||||
|
"show_for": {
|
||||||
|
"secs": 1,
|
||||||
|
"nanos": 0
|
||||||
|
},
|
||||||
|
"fade_out": {
|
||||||
|
"secs": 2,
|
||||||
|
"nanos": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
`show_for` can be `null`, if it is `null` then the `show_for` becomes `gif_time_length - fade_in - fade_out`.
|
||||||
|
This is period for which the gif or image will be max brightness (as set).
|
||||||
|
|
||||||
**<INT>**
|
**<INT>**
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{env, path::Path, thread::sleep};
|
use std::{env, path::Path, thread::sleep};
|
||||||
|
|
||||||
use rog_anime::{ActionData, AnimeAction, Sequences};
|
use rog_anime::{ActionData, ActionLoader, Sequences};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClient;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -17,7 +17,7 @@ fn main() {
|
|||||||
let mut seq = Sequences::new();
|
let mut seq = Sequences::new();
|
||||||
seq.insert(
|
seq.insert(
|
||||||
0,
|
0,
|
||||||
&AnimeAction::AsusAnimation {
|
&ActionLoader::AsusAnimation {
|
||||||
file: path.into(),
|
file: path.into(),
|
||||||
time: rog_anime::AnimTime::Infinite,
|
time: rog_anime::AnimTime::Infinite,
|
||||||
brightness,
|
brightness,
|
||||||
|
|||||||
@@ -161,7 +161,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
dbus.proxies().anime().set_led_power(anime_turn.into())?
|
dbus.proxies().anime().set_led_power(anime_turn.into())?
|
||||||
}
|
}
|
||||||
if let Some(anime_boot) = cmd.boot {
|
if let Some(anime_boot) = cmd.boot {
|
||||||
dbus.proxies().anime().set_system_animations(anime_boot.into())?
|
dbus.proxies()
|
||||||
|
.anime()
|
||||||
|
.set_system_animations(anime_boot.into())?
|
||||||
}
|
}
|
||||||
if let Some(action) = cmd.command {
|
if let Some(action) = cmd.command {
|
||||||
match action {
|
match action {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "daemon-user"
|
name = "daemon-user"
|
||||||
version = "1.1.1"
|
version = "1.2.0"
|
||||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "Usermode daemon for user settings, anime, per-key lighting"
|
description = "Usermode daemon for user settings, anime, per-key lighting"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use rog_anime::{ActionData, AnimTime, AnimeAction, Sequences, Vec2};
|
use rog_anime::{ActionData, ActionLoader, AnimTime, Fade, Sequences, Vec2};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClient;
|
||||||
//use crate::dbus::DbusEvents;
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -17,6 +16,44 @@ use zvariant_derive::Type;
|
|||||||
|
|
||||||
use crate::{error::Error, user_config::UserAnimeConfig};
|
use crate::{error::Error, user_config::UserAnimeConfig};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
|
||||||
|
pub struct Timer {
|
||||||
|
type_of: TimeType,
|
||||||
|
/// If time type is Timer then this is milliseonds, otherwise it is animation loop count
|
||||||
|
count: u64,
|
||||||
|
/// Used only for `TimeType::Timer`, milliseonds to fade the image in for
|
||||||
|
fade_in: Option<u64>,
|
||||||
|
/// Used only for `TimeType::Timer`, milliseonds to fade the image out for
|
||||||
|
fade_out: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Timer> for AnimTime {
|
||||||
|
fn from(time: Timer) -> Self {
|
||||||
|
match time.type_of {
|
||||||
|
TimeType::Timer => {
|
||||||
|
if time.fade_in.is_some() || time.fade_out.is_some() {
|
||||||
|
let fade_in = time
|
||||||
|
.fade_in
|
||||||
|
.map_or(Duration::from_secs(0), Duration::from_millis);
|
||||||
|
let fade_out = time
|
||||||
|
.fade_out
|
||||||
|
.map_or(Duration::from_secs(0), Duration::from_millis);
|
||||||
|
let show_for = if time.count != 0 {
|
||||||
|
Some(Duration::from_millis(time.count))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
AnimTime::Fade(Fade::new(fade_in, show_for, fade_out))
|
||||||
|
} else {
|
||||||
|
AnimTime::Time(Duration::from_millis(time.count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TimeType::Count => AnimTime::Count(time.count as u32),
|
||||||
|
TimeType::Infinite => AnimTime::Infinite,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
|
#[derive(Debug, Clone, Deserialize, Serialize, Type)]
|
||||||
pub enum TimeType {
|
pub enum TimeType {
|
||||||
Timer,
|
Timer,
|
||||||
@@ -29,14 +66,14 @@ pub enum TimeType {
|
|||||||
pub struct CtrlAnimeInner<'a> {
|
pub struct CtrlAnimeInner<'a> {
|
||||||
sequences: Sequences,
|
sequences: Sequences,
|
||||||
client: RogDbusClient<'a>,
|
client: RogDbusClient<'a>,
|
||||||
do_early_return: &'a AtomicBool,
|
do_early_return: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CtrlAnimeInner<'static> {
|
impl<'a> CtrlAnimeInner<'static> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
sequences: Sequences,
|
sequences: Sequences,
|
||||||
client: RogDbusClient<'static>,
|
client: RogDbusClient<'static>,
|
||||||
do_early_return: &'static AtomicBool,
|
do_early_return: Arc<AtomicBool>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
sequences,
|
sequences,
|
||||||
@@ -45,7 +82,7 @@ impl<'a> CtrlAnimeInner<'static> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/// To be called on each main loop iteration to pump out commands to the anime
|
/// To be called on each main loop iteration to pump out commands to the anime
|
||||||
pub fn run(&self) -> Result<(), Error> {
|
pub fn run(&'a self) -> Result<(), Error> {
|
||||||
if self.do_early_return.load(Ordering::SeqCst) {
|
if self.do_early_return.load(Ordering::SeqCst) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -53,32 +90,10 @@ impl<'a> CtrlAnimeInner<'static> {
|
|||||||
for action in self.sequences.iter() {
|
for action in self.sequences.iter() {
|
||||||
match action {
|
match action {
|
||||||
ActionData::Animation(frames) => {
|
ActionData::Animation(frames) => {
|
||||||
let mut count = 0;
|
rog_anime::run_animation(frames, self.do_early_return.clone(), &|output| {
|
||||||
let start = Instant::now();
|
self.client.proxies().anime().write(output).unwrap()
|
||||||
'animation: loop {
|
})
|
||||||
for frame in frames.frames() {
|
.unwrap();
|
||||||
if self.do_early_return.load(Ordering::SeqCst) {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
self.client
|
|
||||||
.proxies()
|
|
||||||
.anime()
|
|
||||||
.write(frame.frame().clone())
|
|
||||||
.unwrap();
|
|
||||||
if let AnimTime::Time(time) = frames.duration() {
|
|
||||||
if Instant::now().duration_since(start) > time {
|
|
||||||
break 'animation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sleep(frame.delay());
|
|
||||||
}
|
|
||||||
if let AnimTime::Cycles(times) = frames.duration() {
|
|
||||||
count += 1;
|
|
||||||
if count >= times {
|
|
||||||
break 'animation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ActionData::Image(image) => {
|
ActionData::Image(image) => {
|
||||||
self.client
|
self.client
|
||||||
@@ -115,7 +130,7 @@ pub struct CtrlAnime<'a> {
|
|||||||
client: RogDbusClient<'a>,
|
client: RogDbusClient<'a>,
|
||||||
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
|
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
|
||||||
/// Must be the same Atomic as in CtrlAnimeInner
|
/// Must be the same Atomic as in CtrlAnimeInner
|
||||||
inner_early_return: &'a AtomicBool,
|
inner_early_return: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CtrlAnime<'static> {
|
impl<'a> CtrlAnime<'static> {
|
||||||
@@ -123,7 +138,7 @@ impl<'a> CtrlAnime<'static> {
|
|||||||
config: Arc<Mutex<UserAnimeConfig>>,
|
config: Arc<Mutex<UserAnimeConfig>>,
|
||||||
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
|
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
|
||||||
client: RogDbusClient<'static>,
|
client: RogDbusClient<'static>,
|
||||||
inner_early_return: &'static AtomicBool,
|
inner_early_return: Arc<AtomicBool>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
Ok(CtrlAnime {
|
Ok(CtrlAnime {
|
||||||
config,
|
config,
|
||||||
@@ -159,18 +174,13 @@ impl CtrlAnime<'static> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
index: u32,
|
index: u32,
|
||||||
file: String,
|
file: String,
|
||||||
time: TimeType,
|
time: Timer,
|
||||||
count: u32,
|
|
||||||
brightness: f32,
|
brightness: f32,
|
||||||
) -> zbus::fdo::Result<String> {
|
) -> zbus::fdo::Result<String> {
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
if let Ok(mut config) = self.config.try_lock() {
|
||||||
let time: AnimTime = match time {
|
let time: AnimTime = time.into();
|
||||||
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
|
|
||||||
TimeType::Count => AnimTime::Cycles(count),
|
|
||||||
TimeType::Infinite => AnimTime::Infinite,
|
|
||||||
};
|
|
||||||
let file = Path::new(&file);
|
let file = Path::new(&file);
|
||||||
let action = AnimeAction::AsusAnimation {
|
let action = ActionLoader::AsusAnimation {
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
brightness,
|
brightness,
|
||||||
time,
|
time,
|
||||||
@@ -205,19 +215,14 @@ impl CtrlAnime<'static> {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
xy: (f32, f32),
|
xy: (f32, f32),
|
||||||
time: TimeType,
|
time: Timer,
|
||||||
count: u32,
|
|
||||||
brightness: f32,
|
brightness: f32,
|
||||||
) -> zbus::fdo::Result<String> {
|
) -> zbus::fdo::Result<String> {
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
if let Ok(mut config) = self.config.try_lock() {
|
||||||
let time: AnimTime = match time {
|
let time: AnimTime = time.into();
|
||||||
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
|
|
||||||
TimeType::Count => AnimTime::Cycles(count),
|
|
||||||
TimeType::Infinite => AnimTime::Infinite,
|
|
||||||
};
|
|
||||||
let file = Path::new(&file);
|
let file = Path::new(&file);
|
||||||
let translation = Vec2::new(xy.0, xy.1);
|
let translation = Vec2::new(xy.0, xy.1);
|
||||||
let action = AnimeAction::ImageAnimation {
|
let action = ActionLoader::ImageAnimation {
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
scale,
|
scale,
|
||||||
angle,
|
angle,
|
||||||
@@ -248,6 +253,7 @@ impl CtrlAnime<'static> {
|
|||||||
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn insert_image(
|
pub fn insert_image(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: u32,
|
index: u32,
|
||||||
@@ -255,16 +261,19 @@ impl CtrlAnime<'static> {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
xy: (f32, f32),
|
xy: (f32, f32),
|
||||||
|
time: Option<Timer>,
|
||||||
brightness: f32,
|
brightness: f32,
|
||||||
) -> zbus::fdo::Result<String> {
|
) -> zbus::fdo::Result<String> {
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
if let Ok(mut config) = self.config.try_lock() {
|
||||||
let file = Path::new(&file);
|
let file = Path::new(&file);
|
||||||
let action = AnimeAction::Image {
|
let time = time.map(|time| time.into());
|
||||||
|
let action = ActionLoader::Image {
|
||||||
file: file.into(),
|
file: file.into(),
|
||||||
scale,
|
scale,
|
||||||
angle,
|
angle,
|
||||||
translation: Vec2::new(xy.0, xy.1),
|
translation: Vec2::new(xy.0, xy.1),
|
||||||
brightness,
|
brightness,
|
||||||
|
time,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Must make the inner run loop return early
|
// Must make the inner run loop return early
|
||||||
@@ -291,7 +300,7 @@ impl CtrlAnime<'static> {
|
|||||||
|
|
||||||
pub fn insert_pause(&mut self, index: u32, millis: u64) -> zbus::fdo::Result<String> {
|
pub fn insert_pause(&mut self, index: u32, millis: u64) -> zbus::fdo::Result<String> {
|
||||||
if let Ok(mut config) = self.config.try_lock() {
|
if let Ok(mut config) = self.config.try_lock() {
|
||||||
let action = AnimeAction::Pause(Duration::from_millis(millis));
|
let action = ActionLoader::Pause(Duration::from_millis(millis));
|
||||||
// Must make the inner run loop return early
|
// Must make the inner run loop return early
|
||||||
self.inner_early_return.store(true, Ordering::SeqCst);
|
self.inner_early_return.store(true, Ordering::SeqCst);
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ use zbus::{fdo, Connection};
|
|||||||
|
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
|
|
||||||
/// The anime loop needs an atomic to make it exit early if required
|
|
||||||
static ANIME_INNER_EARLY_RETURN: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!(" user daemon v{}", rog_user::VERSION);
|
println!(" user daemon v{}", rog_user::VERSION);
|
||||||
println!(" rog-anime v{}", rog_anime::VERSION);
|
println!(" rog-anime v{}", rog_anime::VERSION);
|
||||||
@@ -39,20 +36,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
// Set up the anime data and run loop/thread
|
// Set up the anime data and run loop/thread
|
||||||
if supported.anime_ctrl.0 {
|
if supported.anime_ctrl.0 {
|
||||||
|
let early_return = Arc::new(AtomicBool::new(false));
|
||||||
// Inner behind mutex required for thread safety
|
// Inner behind mutex required for thread safety
|
||||||
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new(
|
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new(
|
||||||
anime,
|
anime,
|
||||||
client,
|
client,
|
||||||
&ANIME_INNER_EARLY_RETURN,
|
early_return.clone(),
|
||||||
)?));
|
)?));
|
||||||
// Need new client object for dbus control part
|
// Need new client object for dbus control part
|
||||||
let (client, _) = RogDbusClient::new().unwrap();
|
let (client, _) = RogDbusClient::new().unwrap();
|
||||||
let anime_control = CtrlAnime::new(
|
let anime_control = CtrlAnime::new(anime_config, inner.clone(), client, early_return)?;
|
||||||
anime_config,
|
|
||||||
inner.clone(),
|
|
||||||
client,
|
|
||||||
&ANIME_INNER_EARLY_RETURN,
|
|
||||||
)?;
|
|
||||||
anime_control.add_to_server(&mut server);
|
anime_control.add_to_server(&mut server);
|
||||||
// Thread using inner
|
// Thread using inner
|
||||||
let _anime_thread = thread::Builder::new()
|
let _anime_thread = thread::Builder::new()
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rog_anime::{AnimTime, AnimeAction, Sequences, Vec2};
|
use rog_anime::{ActionLoader, AnimTime, Fade, Sequences, Vec2};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
@@ -12,7 +12,7 @@ use crate::error::Error;
|
|||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct UserAnimeConfig {
|
pub struct UserAnimeConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub anime: Vec<AnimeAction>,
|
pub anime: Vec<ActionLoader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserAnimeConfig {
|
impl UserAnimeConfig {
|
||||||
@@ -95,34 +95,47 @@ impl Default for UserAnimeConfig {
|
|||||||
Self {
|
Self {
|
||||||
name: "default".to_string(),
|
name: "default".to_string(),
|
||||||
anime: vec![
|
anime: vec![
|
||||||
AnimeAction::AsusAnimation {
|
ActionLoader::AsusAnimation {
|
||||||
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
||||||
brightness: 0.5,
|
brightness: 0.5,
|
||||||
time: AnimTime::Cycles(1),
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(6),
|
||||||
|
None,
|
||||||
|
Duration::from_secs(3),
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
AnimeAction::ImageAnimation {
|
ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.65,
|
angle: 0.65,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
brightness: 0.5,
|
brightness: 0.5,
|
||||||
time: AnimTime::Time(Duration::from_secs(5)),
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(2)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
AnimeAction::Image {
|
ActionLoader::Image {
|
||||||
file: "/usr/share/asusd/anime/custom/rust.png".into(),
|
file: "/usr/share/asusd/anime/custom/rust.png".into(),
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
|
time: Some(AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(1)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
))),
|
||||||
brightness: 0.6,
|
brightness: 0.6,
|
||||||
},
|
},
|
||||||
AnimeAction::Pause(Duration::from_secs(6)),
|
ActionLoader::Pause(Duration::from_secs(1)),
|
||||||
AnimeAction::ImageAnimation {
|
ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
translation: Vec2::new(3.0, 2.0),
|
translation: Vec2::new(3.0, 2.0),
|
||||||
brightness: 0.5,
|
brightness: 0.5,
|
||||||
time: AnimTime::Cycles(2),
|
time: AnimTime::Count(2),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "daemon"
|
name = "daemon"
|
||||||
version = "3.6.2"
|
version = "3.6.3"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Luke <luke@ljones.dev>"]
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::VERSION;
|
use crate::VERSION;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use rog_anime::{error::AnimeError, ActionData, AnimTime, AnimeAction, Vec2};
|
use rog_anime::Fade;
|
||||||
|
use rog_anime::{error::AnimeError, ActionData, ActionLoader, AnimTime, Vec2};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
@@ -11,10 +12,10 @@ pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf";
|
|||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AnimeConfigV341 {
|
pub struct AnimeConfigV341 {
|
||||||
pub system: Option<AnimeAction>,
|
pub system: Option<ActionLoader>,
|
||||||
pub boot: Option<AnimeAction>,
|
pub boot: Option<ActionLoader>,
|
||||||
pub suspend: Option<AnimeAction>,
|
pub suspend: Option<ActionLoader>,
|
||||||
pub shutdown: Option<AnimeAction>,
|
pub shutdown: Option<ActionLoader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AnimeConfigV341 {
|
impl AnimeConfigV341 {
|
||||||
@@ -49,10 +50,10 @@ impl AnimeConfigV341 {
|
|||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AnimeConfigV352 {
|
pub struct AnimeConfigV352 {
|
||||||
pub system: Vec<AnimeAction>,
|
pub system: Vec<ActionLoader>,
|
||||||
pub boot: Vec<AnimeAction>,
|
pub boot: Vec<ActionLoader>,
|
||||||
pub wake: Vec<AnimeAction>,
|
pub wake: Vec<ActionLoader>,
|
||||||
pub shutdown: Vec<AnimeAction>,
|
pub shutdown: Vec<ActionLoader>,
|
||||||
pub brightness: f32,
|
pub brightness: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,10 +111,10 @@ impl AnimeConfigCached {
|
|||||||
/// Config for base system actions for the anime display
|
/// Config for base system actions for the anime display
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct AnimeConfig {
|
pub struct AnimeConfig {
|
||||||
pub system: Vec<AnimeAction>,
|
pub system: Vec<ActionLoader>,
|
||||||
pub boot: Vec<AnimeAction>,
|
pub boot: Vec<ActionLoader>,
|
||||||
pub wake: Vec<AnimeAction>,
|
pub wake: Vec<ActionLoader>,
|
||||||
pub shutdown: Vec<AnimeAction>,
|
pub shutdown: Vec<ActionLoader>,
|
||||||
pub brightness: f32,
|
pub brightness: f32,
|
||||||
pub awake_enabled: bool,
|
pub awake_enabled: bool,
|
||||||
pub boot_anim_enabled: bool,
|
pub boot_anim_enabled: bool,
|
||||||
@@ -165,8 +166,11 @@ impl AnimeConfig {
|
|||||||
info!("Updated config version to: {}", VERSION);
|
info!("Updated config version to: {}", VERSION);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
warn!("Could not deserialise {}", ANIME_CONFIG_PATH);
|
AnimeConfig::write_backup(buf);
|
||||||
panic!("Please remove {} then restart asusd", ANIME_CONFIG_PATH);
|
warn!(
|
||||||
|
"Could not deserialise {}. Backed up as *-old",
|
||||||
|
ANIME_CONFIG_PATH
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnimeConfig::create_default(&mut file)
|
AnimeConfig::create_default(&mut file)
|
||||||
@@ -176,23 +180,31 @@ impl AnimeConfig {
|
|||||||
// create a default config here
|
// create a default config here
|
||||||
let config = AnimeConfig {
|
let config = AnimeConfig {
|
||||||
system: vec![],
|
system: vec![],
|
||||||
boot: vec![AnimeAction::ImageAnimation {
|
boot: vec![ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.65,
|
angle: 0.65,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
brightness: 1.0,
|
brightness: 1.0,
|
||||||
time: AnimTime::Time(Duration::from_secs(5)),
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(2)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
}],
|
}],
|
||||||
wake: vec![AnimeAction::ImageAnimation {
|
wake: vec![ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.65,
|
angle: 0.65,
|
||||||
translation: Vec2::default(),
|
translation: Vec2::default(),
|
||||||
brightness: 1.0,
|
brightness: 1.0,
|
||||||
time: AnimTime::Time(Duration::from_secs(5)),
|
time: AnimTime::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(2)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
}],
|
}],
|
||||||
shutdown: vec![AnimeAction::ImageAnimation {
|
shutdown: vec![ActionLoader::ImageAnimation {
|
||||||
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
||||||
scale: 0.9,
|
scale: 0.9,
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
@@ -234,4 +246,12 @@ impl AnimeConfig {
|
|||||||
file.write_all(json.as_bytes())
|
file.write_all(json.as_bytes())
|
||||||
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_backup(buf: String) {
|
||||||
|
let mut path = ANIME_CONFIG_PATH.to_string();
|
||||||
|
path.push_str("-old");
|
||||||
|
let mut file = File::create(&path).expect("Couldn't overwrite config");
|
||||||
|
file.write_all(buf.as_bytes())
|
||||||
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use std::collections::BTreeMap;
|
|||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
/// for parsing old v3.1.7 config
|
/// for parsing old v3.1.7 config
|
||||||
|
#[allow(dead_code)]
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub(crate) struct ConfigV317 {
|
pub(crate) struct ConfigV317 {
|
||||||
pub gfx_mode: GfxVendors,
|
pub gfx_mode: GfxVendors,
|
||||||
@@ -26,7 +27,7 @@ pub(crate) struct ConfigV317 {
|
|||||||
impl ConfigV317 {
|
impl ConfigV317 {
|
||||||
pub(crate) fn into_current(self) -> Config {
|
pub(crate) fn into_current(self) -> Config {
|
||||||
Config {
|
Config {
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
gfx_mode: self.gfx_mode,
|
||||||
gfx_tmp_mode: None,
|
gfx_tmp_mode: None,
|
||||||
gfx_managed: self.gfx_managed,
|
gfx_managed: self.gfx_managed,
|
||||||
gfx_vfio_enable: false,
|
gfx_vfio_enable: false,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use rog_anime::{
|
|||||||
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
||||||
VENDOR_ID,
|
VENDOR_ID,
|
||||||
},
|
},
|
||||||
ActionData, AnimTime, AnimeDataBuffer, AnimePacketType, AnimePowerStates, ANIME_DATA_LEN,
|
ActionData, AnimeDataBuffer, AnimePacketType, AnimePowerStates, ANIME_DATA_LEN,
|
||||||
};
|
};
|
||||||
use rog_types::supported::AnimeSupportedFunctions;
|
use rog_types::supported::AnimeSupportedFunctions;
|
||||||
use rusb::{Device, DeviceHandle};
|
use rusb::{Device, DeviceHandle};
|
||||||
@@ -13,7 +13,6 @@ use std::{
|
|||||||
error::Error,
|
error::Error,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
thread::sleep,
|
thread::sleep,
|
||||||
time::Instant,
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
@@ -144,31 +143,15 @@ impl CtrlAnime {
|
|||||||
for action in actions.iter() {
|
for action in actions.iter() {
|
||||||
match action {
|
match action {
|
||||||
ActionData::Animation(frames) => {
|
ActionData::Animation(frames) => {
|
||||||
let mut count = 0;
|
rog_anime::run_animation(frames, thread_exit.clone(), &|frame| {
|
||||||
let start = Instant::now();
|
if let Ok(lock) = inner.try_lock() {
|
||||||
'animation: loop {
|
lock.write_data_buffer(frame);
|
||||||
for frame in frames.frames() {
|
|
||||||
if let Ok(lock) = inner.try_lock() {
|
|
||||||
lock.write_data_buffer(frame.frame().clone());
|
|
||||||
}
|
|
||||||
if let AnimTime::Time(time) = frames.duration() {
|
|
||||||
if Instant::now().duration_since(start) > time {
|
|
||||||
break 'animation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sleep(frame.delay());
|
|
||||||
// Need to check for early exit condition here or it might run
|
|
||||||
// until end of gif or time
|
|
||||||
if thread_exit.load(Ordering::SeqCst) {
|
|
||||||
break 'main;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let AnimTime::Cycles(times) = frames.duration() {
|
|
||||||
count += 1;
|
|
||||||
if count >= times {
|
|
||||||
break 'animation;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if thread_exit.load(Ordering::SeqCst) {
|
||||||
|
break 'main;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ActionData::Image(image) => {
|
ActionData::Image(image) => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use sysfs_class::RuntimePM;
|
|
||||||
use ::zbus::Connection;
|
use ::zbus::Connection;
|
||||||
use ctrl_gfx::error::GfxError;
|
use ctrl_gfx::error::GfxError;
|
||||||
use ctrl_gfx::*;
|
use ctrl_gfx::*;
|
||||||
@@ -14,6 +13,7 @@ use std::{iter::FromIterator, thread::JoinHandle};
|
|||||||
use std::{process::Command, thread::sleep, time::Duration};
|
use std::{process::Command, thread::sleep, time::Duration};
|
||||||
use std::{str::FromStr, sync::mpsc};
|
use std::{str::FromStr, sync::mpsc};
|
||||||
use std::{sync::Arc, sync::Mutex};
|
use std::{sync::Arc, sync::Mutex};
|
||||||
|
use sysfs_class::RuntimePM;
|
||||||
use sysfs_class::{PciDevice, SysClass};
|
use sysfs_class::{PciDevice, SysClass};
|
||||||
use system::{GraphicsDevice, PciBus};
|
use system::{GraphicsDevice, PciBus};
|
||||||
|
|
||||||
@@ -67,8 +67,12 @@ impl CtrlGraphics {
|
|||||||
let mut nvidia = Vec::new();
|
let mut nvidia = Vec::new();
|
||||||
let mut other = Vec::new();
|
let mut other = Vec::new();
|
||||||
for dev in devs.iter() {
|
for dev in devs.iter() {
|
||||||
let c = dev.class().map_err(|err|{
|
let c = dev.class().map_err(|err| {
|
||||||
error!("GFX: device error: {}, {}", dev.path().to_string_lossy(), err);
|
error!(
|
||||||
|
"GFX: device error: {}, {}",
|
||||||
|
dev.path().to_string_lossy(),
|
||||||
|
err
|
||||||
|
);
|
||||||
err
|
err
|
||||||
})?;
|
})?;
|
||||||
if 0x03 == (c >> 16) & 0xFF {
|
if 0x03 == (c >> 16) & 0xFF {
|
||||||
@@ -408,8 +412,12 @@ impl CtrlGraphics {
|
|||||||
// Make sure the power management is set to auto for nvidia devices
|
// Make sure the power management is set to auto for nvidia devices
|
||||||
let devs = PciDevice::all()?;
|
let devs = PciDevice::all()?;
|
||||||
for dev in devs.iter() {
|
for dev in devs.iter() {
|
||||||
let c = dev.class().map_err(|err|{
|
let c = dev.class().map_err(|err| {
|
||||||
error!("GFX: device error: {}, {}", dev.path().to_string_lossy(), err);
|
error!(
|
||||||
|
"GFX: device error: {}, {}",
|
||||||
|
dev.path().to_string_lossy(),
|
||||||
|
err
|
||||||
|
);
|
||||||
err
|
err
|
||||||
})?;
|
})?;
|
||||||
if 0x03 == (c >> 16) & 0xFF && dev.vendor()? == 0x10DE {
|
if 0x03 == (c >> 16) & 0xFF && dev.vendor()? == 0x10DE {
|
||||||
|
|||||||
@@ -89,11 +89,12 @@ impl CtrlFanAndCpu {
|
|||||||
*existing = profile.clone();
|
*existing = profile.clone();
|
||||||
existing.set_system_all()?;
|
existing.set_system_all()?;
|
||||||
} else {
|
} else {
|
||||||
config.power_profiles
|
config
|
||||||
|
.power_profiles
|
||||||
.insert(profile.name.clone(), profile.clone());
|
.insert(profile.name.clone(), profile.clone());
|
||||||
profile.set_system_all()?;
|
profile.set_system_all()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.active_profile = profile.name.clone();
|
config.active_profile = profile.name.clone();
|
||||||
config.write();
|
config.write();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rog_anime"
|
name = "rog_anime"
|
||||||
version = "1.0.4"
|
version = "1.0.5"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = ["Luke <luke@ljones.dev>"]
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
|
|||||||
@@ -1,7 +1,18 @@
|
|||||||
|
use std::{
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
thread::sleep,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "dbus")]
|
#[cfg(feature = "dbus")]
|
||||||
use zvariant_derive::Type;
|
use zvariant_derive::Type;
|
||||||
|
|
||||||
|
use crate::{error::AnimeError, 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;
|
||||||
/// *Not* inclusive, the byte before this is the final for each "pane"
|
/// *Not* inclusive, the byte before this is the final for each "pane"
|
||||||
@@ -79,3 +90,93 @@ impl From<AnimeDataBuffer> for AnimePacketType {
|
|||||||
buffers
|
buffers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This runs the animations as a blocking loop by using the `callback` to write data
|
||||||
|
pub fn run_animation(
|
||||||
|
frames: &AnimeGif,
|
||||||
|
do_early_return: Arc<AtomicBool>,
|
||||||
|
callback: &dyn Fn(AnimeDataBuffer),
|
||||||
|
) -> Result<(), AnimeError> {
|
||||||
|
let mut count = 0;
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
let mut timed = false;
|
||||||
|
let mut run_time = frames.total_frame_time();
|
||||||
|
println!("Real gif run length = {:?}", run_time);
|
||||||
|
if let AnimTime::Fade(time) = frames.duration() {
|
||||||
|
if let Some(middle) = time.show_for() {
|
||||||
|
run_time = middle + time.total_fade_time();
|
||||||
|
}
|
||||||
|
timed = true;
|
||||||
|
} else if let AnimTime::Time(time) = frames.duration() {
|
||||||
|
run_time = time;
|
||||||
|
timed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// After setting up all the data
|
||||||
|
let mut fade_in = Duration::from_millis(0);
|
||||||
|
let mut fade_out = Duration::from_millis(0);
|
||||||
|
let mut fade_in_step = 0.0;
|
||||||
|
let mut fade_in_accum = 0.0;
|
||||||
|
let mut fade_out_step = 0.0;
|
||||||
|
let mut fade_out_accum;
|
||||||
|
if let AnimTime::Fade(time) = frames.duration() {
|
||||||
|
fade_in = time.fade_in();
|
||||||
|
fade_out = time.fade_out();
|
||||||
|
fade_in_step = 1.0 / fade_in.as_secs_f32();
|
||||||
|
fade_out_step = 1.0 / fade_out.as_secs_f32();
|
||||||
|
|
||||||
|
if time.total_fade_time() > run_time {
|
||||||
|
println!("Total fade in/out time larger than gif run time. Setting fades to half");
|
||||||
|
fade_in = run_time / 2;
|
||||||
|
fade_in_step = 1.0 / (run_time / 2).as_secs_f32();
|
||||||
|
|
||||||
|
fade_out = run_time / 2;
|
||||||
|
fade_out_step = 1.0 / (run_time / 2).as_secs_f32();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
'animation: loop {
|
||||||
|
for frame in frames.frames() {
|
||||||
|
let frame_start = Instant::now();
|
||||||
|
if do_early_return.load(Ordering::SeqCst) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let mut output = frame.frame().clone();
|
||||||
|
|
||||||
|
if let AnimTime::Fade(_) = frames.duration() {
|
||||||
|
if frame_start <= start + fade_in {
|
||||||
|
for pixel in output.get_mut() {
|
||||||
|
*pixel = (*pixel as f32 * fade_in_accum) as u8;
|
||||||
|
}
|
||||||
|
fade_in_accum = fade_in_step * (frame_start - start).as_secs_f32();
|
||||||
|
} else if frame_start > (start + run_time) - fade_out {
|
||||||
|
if run_time > (frame_start - start) {
|
||||||
|
fade_out_accum =
|
||||||
|
fade_out_step * (run_time - (frame_start - start)).as_secs_f32();
|
||||||
|
} else {
|
||||||
|
fade_out_accum = 0.0;
|
||||||
|
}
|
||||||
|
for pixel in output.get_mut() {
|
||||||
|
*pixel = (*pixel as f32 * fade_out_accum) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(output);
|
||||||
|
|
||||||
|
if timed && Instant::now().duration_since(start) > run_time {
|
||||||
|
break 'animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(frame.delay());
|
||||||
|
}
|
||||||
|
if let AnimTime::Count(times) = frames.duration() {
|
||||||
|
count += 1;
|
||||||
|
if count >= times {
|
||||||
|
break 'animation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,10 +31,12 @@ impl AnimeFrame {
|
|||||||
pub enum AnimTime {
|
pub enum AnimTime {
|
||||||
/// Time in milliseconds for animation to run
|
/// Time in milliseconds for animation to run
|
||||||
Time(Duration),
|
Time(Duration),
|
||||||
/// How many full animation loops to run
|
/// How many full animation loops to run or how many seconds if image is static
|
||||||
Cycles(u32),
|
Count(u32),
|
||||||
/// Run for infinite time
|
/// Run for infinite time
|
||||||
Infinite,
|
Infinite,
|
||||||
|
/// Fade in, play for, fade out
|
||||||
|
Fade(Fade),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AnimTime {
|
impl Default for AnimTime {
|
||||||
@@ -44,6 +46,40 @@ impl Default for AnimTime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fancy brightness control: fade in/out, show at brightness for n time
|
||||||
|
#[derive(Debug, Copy, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct Fade {
|
||||||
|
fade_in: Duration,
|
||||||
|
show_for: Option<Duration>,
|
||||||
|
fade_out: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fade {
|
||||||
|
pub fn new(fade_in: Duration, show_for: Option<Duration>, fade_out: Duration) -> Self {
|
||||||
|
Self {
|
||||||
|
fade_in,
|
||||||
|
show_for,
|
||||||
|
fade_out,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fade_in(&self) -> Duration {
|
||||||
|
self.fade_in
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_for(&self) -> Option<Duration> {
|
||||||
|
self.show_for
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fade_out(&self) -> Duration {
|
||||||
|
self.fade_out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_fade_time(&self) -> Duration {
|
||||||
|
self.fade_in + self.fade_out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A gif animation. This is a collection of frames from the gif, and a duration
|
/// A gif animation. This is a collection of frames from the gif, and a duration
|
||||||
/// that the animation should be shown for.
|
/// that the animation should be shown for.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
@@ -67,6 +103,7 @@ impl AnimeGif {
|
|||||||
let mut decoder = decoder.read_info(file)?;
|
let mut decoder = decoder.read_info(file)?;
|
||||||
|
|
||||||
let mut frames = Vec::with_capacity(decoder.buffer_size());
|
let mut frames = Vec::with_capacity(decoder.buffer_size());
|
||||||
|
|
||||||
while let Some(frame) = decoder.read_next_frame()? {
|
while let Some(frame) = decoder.read_next_frame()? {
|
||||||
let wait = frame.delay * 10;
|
let wait = frame.delay * 10;
|
||||||
if matches!(frame.dispose, gif::DisposalMethod::Background) {
|
if matches!(frame.dispose, gif::DisposalMethod::Background) {
|
||||||
@@ -162,6 +199,39 @@ impl AnimeGif {
|
|||||||
Ok(Self(frames, duration))
|
Ok(Self(frames, duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Make a static gif out of a greyscale png. If no duration is specified then the default
|
||||||
|
/// will be 1 second long. If `AnimTime::Cycles` is specified for `duration` then this can
|
||||||
|
/// be considered how many seconds the image will show for.
|
||||||
|
#[inline]
|
||||||
|
pub fn create_png_static(
|
||||||
|
file_name: &Path,
|
||||||
|
scale: f32,
|
||||||
|
angle: f32,
|
||||||
|
translation: Vec2,
|
||||||
|
duration: AnimTime,
|
||||||
|
brightness: f32,
|
||||||
|
) -> Result<Self, AnimeError> {
|
||||||
|
let image = AnimeImage::from_png(file_name, scale, angle, translation, brightness)?;
|
||||||
|
|
||||||
|
let mut total = Duration::from_millis(1000);
|
||||||
|
if let AnimTime::Fade(fade) = duration {
|
||||||
|
total = fade.total_fade_time();
|
||||||
|
if let Some(middle) = fade.show_for {
|
||||||
|
total += middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make frame delay 30ms, and find frame count
|
||||||
|
let frame_count = total.as_millis() / 30;
|
||||||
|
|
||||||
|
let single = AnimeFrame {
|
||||||
|
data: <AnimeDataBuffer>::from(&image),
|
||||||
|
delay: Duration::from_millis(30),
|
||||||
|
};
|
||||||
|
let frames = vec![single; frame_count as usize];
|
||||||
|
|
||||||
|
Ok(Self(frames, duration))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a slice of the frames this gif has
|
/// Get a slice of the frames this gif has
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn frames(&self) -> &[AnimeFrame] {
|
pub fn frames(&self) -> &[AnimeFrame] {
|
||||||
@@ -173,4 +243,16 @@ impl AnimeGif {
|
|||||||
pub fn duration(&self) -> AnimTime {
|
pub fn duration(&self) -> AnimTime {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the frame count
|
||||||
|
pub fn frame_count(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get total gif time for one run
|
||||||
|
pub fn total_frame_time(&self) -> Duration {
|
||||||
|
let mut time = 0;
|
||||||
|
self.0.iter().for_each(|f| time += f.delay.as_millis());
|
||||||
|
Duration::from_millis(time as u64)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
use std::{
|
use std::{path::PathBuf, time::Duration};
|
||||||
path::{Path, PathBuf},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
@@ -11,14 +8,14 @@ use crate::{error::AnimeError, AnimTime, AnimeDataBuffer, AnimeGif, AnimeImage};
|
|||||||
/// 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
|
||||||
/// a helper for loading up `ActionData`.
|
/// a helper for loading up `ActionData`.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub enum AnimeAction {
|
pub enum ActionLoader {
|
||||||
/// Full gif sequence. Immutable.
|
/// Full gif sequence. Immutable.
|
||||||
AsusAnimation {
|
AsusAnimation {
|
||||||
file: PathBuf,
|
file: PathBuf,
|
||||||
time: AnimTime,
|
time: AnimTime,
|
||||||
brightness: f32,
|
brightness: f32,
|
||||||
},
|
},
|
||||||
/// Basic image, can have properties changed
|
/// Animated gif. If the file is a png a static gif is created using the `time` properties
|
||||||
ImageAnimation {
|
ImageAnimation {
|
||||||
file: PathBuf,
|
file: PathBuf,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
@@ -32,6 +29,7 @@ pub enum AnimeAction {
|
|||||||
scale: f32,
|
scale: f32,
|
||||||
angle: f32,
|
angle: f32,
|
||||||
translation: Vec2,
|
translation: Vec2,
|
||||||
|
time: Option<AnimTime>,
|
||||||
brightness: f32,
|
brightness: f32,
|
||||||
},
|
},
|
||||||
/// A pause to be used between sequences
|
/// A pause to be used between sequences
|
||||||
@@ -59,9 +57,9 @@ pub enum ActionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ActionData {
|
impl ActionData {
|
||||||
pub fn from_anime_action(action: &AnimeAction) -> Result<ActionData, AnimeError> {
|
pub fn from_anime_action(action: &ActionLoader) -> Result<ActionData, AnimeError> {
|
||||||
let a = match action {
|
let a = match action {
|
||||||
AnimeAction::AsusAnimation {
|
ActionLoader::AsusAnimation {
|
||||||
file,
|
file,
|
||||||
time: duration,
|
time: duration,
|
||||||
brightness,
|
brightness,
|
||||||
@@ -70,33 +68,59 @@ impl ActionData {
|
|||||||
*duration,
|
*duration,
|
||||||
*brightness,
|
*brightness,
|
||||||
)?),
|
)?),
|
||||||
AnimeAction::ImageAnimation {
|
ActionLoader::ImageAnimation {
|
||||||
file,
|
file,
|
||||||
scale,
|
scale,
|
||||||
angle,
|
angle,
|
||||||
translation,
|
translation,
|
||||||
time: duration,
|
time: duration,
|
||||||
brightness,
|
brightness,
|
||||||
} => ActionData::Animation(AnimeGif::create_png_gif(
|
} => {
|
||||||
&file,
|
if let Some(ext) = file.extension() {
|
||||||
*scale,
|
if ext.to_string_lossy().to_lowercase() == "png" {
|
||||||
*angle,
|
return Ok(ActionData::Animation(AnimeGif::create_png_static(
|
||||||
*translation,
|
&file,
|
||||||
*duration,
|
*scale,
|
||||||
*brightness,
|
*angle,
|
||||||
)?),
|
*translation,
|
||||||
AnimeAction::Image {
|
*duration,
|
||||||
|
*brightness,
|
||||||
|
)?));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActionData::Animation(AnimeGif::create_png_gif(
|
||||||
|
&file,
|
||||||
|
*scale,
|
||||||
|
*angle,
|
||||||
|
*translation,
|
||||||
|
*duration,
|
||||||
|
*brightness,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
ActionLoader::Image {
|
||||||
file,
|
file,
|
||||||
scale,
|
scale,
|
||||||
angle,
|
angle,
|
||||||
translation,
|
translation,
|
||||||
brightness,
|
brightness,
|
||||||
|
time,
|
||||||
} => {
|
} => {
|
||||||
|
if let Some(time) = time {
|
||||||
|
return Ok(ActionData::Animation(AnimeGif::create_png_static(
|
||||||
|
&file,
|
||||||
|
*scale,
|
||||||
|
*angle,
|
||||||
|
*translation,
|
||||||
|
*time,
|
||||||
|
*brightness,
|
||||||
|
)?));
|
||||||
|
}
|
||||||
|
// If no time then create a plain static image
|
||||||
let image = AnimeImage::from_png(&file, *scale, *angle, *translation, *brightness)?;
|
let image = AnimeImage::from_png(&file, *scale, *angle, *translation, *brightness)?;
|
||||||
let data = <AnimeDataBuffer>::from(&image);
|
let data = <AnimeDataBuffer>::from(&image);
|
||||||
ActionData::Image(Box::new(data))
|
ActionData::Image(Box::new(data))
|
||||||
}
|
}
|
||||||
AnimeAction::Pause(duration) => ActionData::Pause(*duration),
|
ActionLoader::Pause(duration) => ActionData::Pause(*duration),
|
||||||
};
|
};
|
||||||
Ok(a)
|
Ok(a)
|
||||||
}
|
}
|
||||||
@@ -115,38 +139,8 @@ 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: &AnimeAction) -> Result<(), AnimeError> {
|
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<(), AnimeError> {
|
||||||
match action {
|
self.0.insert(index, ActionData::from_anime_action(action)?);
|
||||||
AnimeAction::AsusAnimation {
|
|
||||||
file,
|
|
||||||
time: duration,
|
|
||||||
brightness,
|
|
||||||
} => self.insert_asus_gif(index, &file, *duration, *brightness)?,
|
|
||||||
AnimeAction::ImageAnimation {
|
|
||||||
file,
|
|
||||||
scale,
|
|
||||||
angle,
|
|
||||||
translation,
|
|
||||||
time: duration,
|
|
||||||
brightness,
|
|
||||||
} => self.insert_image_gif(
|
|
||||||
index,
|
|
||||||
&file,
|
|
||||||
*scale,
|
|
||||||
*angle,
|
|
||||||
*translation,
|
|
||||||
*duration,
|
|
||||||
*brightness,
|
|
||||||
)?,
|
|
||||||
AnimeAction::Image {
|
|
||||||
file,
|
|
||||||
scale,
|
|
||||||
angle,
|
|
||||||
translation,
|
|
||||||
brightness,
|
|
||||||
} => self.insert_png(index, &file, *scale, *angle, *translation, *brightness)?,
|
|
||||||
AnimeAction::Pause(duration) => self.insert_pause(index, *duration),
|
|
||||||
};
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,66 +155,6 @@ impl Sequences {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_asus_gif(
|
|
||||||
&mut self,
|
|
||||||
mut index: usize,
|
|
||||||
file: &Path,
|
|
||||||
duration: AnimTime,
|
|
||||||
brightness: f32,
|
|
||||||
) -> Result<(), AnimeError> {
|
|
||||||
if index > self.0.len() {
|
|
||||||
index = self.0.len() - 1;
|
|
||||||
}
|
|
||||||
let frames = AnimeGif::create_diagonal_gif(file, duration, brightness)?;
|
|
||||||
self.0.insert(index, ActionData::Animation(frames));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_png(
|
|
||||||
&mut self,
|
|
||||||
mut index: usize,
|
|
||||||
file: &Path,
|
|
||||||
scale: f32,
|
|
||||||
angle: f32,
|
|
||||||
translation: Vec2,
|
|
||||||
brightness: f32,
|
|
||||||
) -> Result<(), AnimeError> {
|
|
||||||
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);
|
|
||||||
self.0.insert(index, ActionData::Image(Box::new(data)));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn insert_image_gif(
|
|
||||||
&mut self,
|
|
||||||
mut index: usize,
|
|
||||||
file: &Path,
|
|
||||||
scale: f32,
|
|
||||||
angle: f32,
|
|
||||||
translation: Vec2,
|
|
||||||
duration: AnimTime,
|
|
||||||
brightness: f32,
|
|
||||||
) -> Result<(), AnimeError> {
|
|
||||||
if index > self.0.len() {
|
|
||||||
index = self.0.len() - 1;
|
|
||||||
}
|
|
||||||
let frames =
|
|
||||||
AnimeGif::create_png_gif(file, scale, angle, translation, duration, brightness)?;
|
|
||||||
self.0.insert(index, ActionData::Animation(frames));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_pause(&mut self, mut index: usize, duration: Duration) {
|
|
||||||
if index > self.0.len() {
|
|
||||||
index = self.0.len() - 1;
|
|
||||||
}
|
|
||||||
self.0.insert(index, ActionData::Pause(duration));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> ActionIterator {
|
pub fn iter(&self) -> ActionIterator {
|
||||||
ActionIterator {
|
ActionIterator {
|
||||||
actions: &self,
|
actions: &self,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::sync::mpsc::Sender;
|
|
||||||
use rog_anime::{AnimeDataBuffer, AnimePowerStates};
|
use rog_anime::{AnimeDataBuffer, AnimePowerStates};
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
use zbus::{dbus_proxy, Connection, Result};
|
use zbus::{dbus_proxy, Connection, Result};
|
||||||
|
|
||||||
#[dbus_proxy(
|
#[dbus_proxy(
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
//!
|
//!
|
||||||
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
|
||||||
|
|
||||||
use std::sync::{mpsc::Sender};
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
use zbus::{dbus_proxy, Connection, Result};
|
use zbus::{dbus_proxy, Connection, Result};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use rog_aura::AuraModeNum;
|
use rog_aura::AuraModeNum;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use zvariant_derive::Type;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use zvariant_derive::Type;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Type, Debug)]
|
#[derive(Serialize, Deserialize, Type, Debug)]
|
||||||
pub struct SupportedFunctions {
|
pub struct SupportedFunctions {
|
||||||
@@ -60,7 +60,11 @@ impl fmt::Display for AnimeSupportedFunctions {
|
|||||||
impl fmt::Display for ChargeSupportedFunctions {
|
impl fmt::Display for ChargeSupportedFunctions {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
writeln!(f, "Charge:")?;
|
writeln!(f, "Charge:")?;
|
||||||
writeln!(f, "\tBattery charge limit control: {}", self.charge_level_set)
|
writeln!(
|
||||||
|
f,
|
||||||
|
"\tBattery charge limit control: {}",
|
||||||
|
self.charge_level_set
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl fmt::Display for FanCpuSupportedFunctions {
|
impl fmt::Display for FanCpuSupportedFunctions {
|
||||||
@@ -86,4 +90,4 @@ impl fmt::Display for RogBiosSupportedFunctions {
|
|||||||
writeln!(f, "\tPOST sound toggle: {}", self.post_sound_toggle)?;
|
writeln!(f, "\tPOST sound toggle: {}", self.post_sound_toggle)?;
|
||||||
writeln!(f, "\tDedicated GFX toggle: {}", self.dedicated_gfx_toggle)
|
writeln!(f, "\tDedicated GFX toggle: {}", self.dedicated_gfx_toggle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user