mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 09:23:19 +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 in log for G-Sync to help prevent user confusion around gfx switching
|
||||
- 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
|
||||
### Changed
|
||||
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -207,7 +207,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "daemon"
|
||||
version = "3.6.2"
|
||||
version = "3.6.3"
|
||||
dependencies = [
|
||||
"env_logger",
|
||||
"log",
|
||||
@@ -232,7 +232,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "daemon-user"
|
||||
version = "1.1.1"
|
||||
version = "1.2.0"
|
||||
dependencies = [
|
||||
"dirs 3.0.1",
|
||||
"rog_anime",
|
||||
@@ -863,7 +863,7 @@ checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||
|
||||
[[package]]
|
||||
name = "rog_anime"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
dependencies = [
|
||||
"gif",
|
||||
"glam",
|
||||
|
||||
22
MANUAL.md
22
MANUAL.md
@@ -269,6 +269,7 @@ Each object in the array can be one of:
|
||||
<FLOAT>,
|
||||
<FLOAT>
|
||||
],
|
||||
"time": <TIME>,
|
||||
"brightness": <FLOAT>
|
||||
}
|
||||
},
|
||||
@@ -322,6 +323,27 @@ A cycle is how many gif loops to run:
|
||||
```json
|
||||
"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>**
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{env, path::Path, thread::sleep};
|
||||
|
||||
use rog_anime::{ActionData, AnimeAction, Sequences};
|
||||
use rog_anime::{ActionData, ActionLoader, Sequences};
|
||||
use rog_dbus::RogDbusClient;
|
||||
|
||||
fn main() {
|
||||
@@ -17,7 +17,7 @@ fn main() {
|
||||
let mut seq = Sequences::new();
|
||||
seq.insert(
|
||||
0,
|
||||
&AnimeAction::AsusAnimation {
|
||||
&ActionLoader::AsusAnimation {
|
||||
file: path.into(),
|
||||
time: rog_anime::AnimTime::Infinite,
|
||||
brightness,
|
||||
|
||||
@@ -161,7 +161,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
dbus.proxies().anime().set_led_power(anime_turn.into())?
|
||||
}
|
||||
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 {
|
||||
match action {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "daemon-user"
|
||||
version = "1.1.1"
|
||||
version = "1.2.0"
|
||||
authors = ["Luke D Jones <luke@ljones.dev>"]
|
||||
edition = "2018"
|
||||
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 crate::dbus::DbusEvents;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
@@ -17,6 +16,44 @@ use zvariant_derive::Type;
|
||||
|
||||
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)]
|
||||
pub enum TimeType {
|
||||
Timer,
|
||||
@@ -29,14 +66,14 @@ pub enum TimeType {
|
||||
pub struct CtrlAnimeInner<'a> {
|
||||
sequences: Sequences,
|
||||
client: RogDbusClient<'a>,
|
||||
do_early_return: &'a AtomicBool,
|
||||
do_early_return: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl<'a> CtrlAnimeInner<'static> {
|
||||
pub fn new(
|
||||
sequences: Sequences,
|
||||
client: RogDbusClient<'static>,
|
||||
do_early_return: &'static AtomicBool,
|
||||
do_early_return: Arc<AtomicBool>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
sequences,
|
||||
@@ -45,7 +82,7 @@ impl<'a> CtrlAnimeInner<'static> {
|
||||
})
|
||||
}
|
||||
/// 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) {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -53,32 +90,10 @@ impl<'a> CtrlAnimeInner<'static> {
|
||||
for action in self.sequences.iter() {
|
||||
match action {
|
||||
ActionData::Animation(frames) => {
|
||||
let mut count = 0;
|
||||
let start = Instant::now();
|
||||
'animation: loop {
|
||||
for frame in frames.frames() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
rog_anime::run_animation(frames, self.do_early_return.clone(), &|output| {
|
||||
self.client.proxies().anime().write(output).unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
ActionData::Image(image) => {
|
||||
self.client
|
||||
@@ -115,7 +130,7 @@ pub struct CtrlAnime<'a> {
|
||||
client: RogDbusClient<'a>,
|
||||
inner: Arc<Mutex<CtrlAnimeInner<'a>>>,
|
||||
/// Must be the same Atomic as in CtrlAnimeInner
|
||||
inner_early_return: &'a AtomicBool,
|
||||
inner_early_return: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl<'a> CtrlAnime<'static> {
|
||||
@@ -123,7 +138,7 @@ impl<'a> CtrlAnime<'static> {
|
||||
config: Arc<Mutex<UserAnimeConfig>>,
|
||||
inner: Arc<Mutex<CtrlAnimeInner<'static>>>,
|
||||
client: RogDbusClient<'static>,
|
||||
inner_early_return: &'static AtomicBool,
|
||||
inner_early_return: Arc<AtomicBool>,
|
||||
) -> Result<Self, Error> {
|
||||
Ok(CtrlAnime {
|
||||
config,
|
||||
@@ -159,18 +174,13 @@ impl CtrlAnime<'static> {
|
||||
&mut self,
|
||||
index: u32,
|
||||
file: String,
|
||||
time: TimeType,
|
||||
count: u32,
|
||||
time: Timer,
|
||||
brightness: f32,
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let time: AnimTime = match time {
|
||||
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
|
||||
TimeType::Count => AnimTime::Cycles(count),
|
||||
TimeType::Infinite => AnimTime::Infinite,
|
||||
};
|
||||
let time: AnimTime = time.into();
|
||||
let file = Path::new(&file);
|
||||
let action = AnimeAction::AsusAnimation {
|
||||
let action = ActionLoader::AsusAnimation {
|
||||
file: file.into(),
|
||||
brightness,
|
||||
time,
|
||||
@@ -205,19 +215,14 @@ impl CtrlAnime<'static> {
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
xy: (f32, f32),
|
||||
time: TimeType,
|
||||
count: u32,
|
||||
time: Timer,
|
||||
brightness: f32,
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let time: AnimTime = match time {
|
||||
TimeType::Timer => AnimTime::Time(Duration::from_millis(count as u64)),
|
||||
TimeType::Count => AnimTime::Cycles(count),
|
||||
TimeType::Infinite => AnimTime::Infinite,
|
||||
};
|
||||
let time: AnimTime = time.into();
|
||||
let file = Path::new(&file);
|
||||
let translation = Vec2::new(xy.0, xy.1);
|
||||
let action = AnimeAction::ImageAnimation {
|
||||
let action = ActionLoader::ImageAnimation {
|
||||
file: file.into(),
|
||||
scale,
|
||||
angle,
|
||||
@@ -248,6 +253,7 @@ impl CtrlAnime<'static> {
|
||||
Err(zbus::fdo::Error::Failed("UserConfig lock fail".into()))
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn insert_image(
|
||||
&mut self,
|
||||
index: u32,
|
||||
@@ -255,16 +261,19 @@ impl CtrlAnime<'static> {
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
xy: (f32, f32),
|
||||
time: Option<Timer>,
|
||||
brightness: f32,
|
||||
) -> zbus::fdo::Result<String> {
|
||||
if let Ok(mut config) = self.config.try_lock() {
|
||||
let file = Path::new(&file);
|
||||
let action = AnimeAction::Image {
|
||||
let time = time.map(|time| time.into());
|
||||
let action = ActionLoader::Image {
|
||||
file: file.into(),
|
||||
scale,
|
||||
angle,
|
||||
translation: Vec2::new(xy.0, xy.1),
|
||||
brightness,
|
||||
time,
|
||||
};
|
||||
|
||||
// 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> {
|
||||
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
|
||||
self.inner_early_return.store(true, Ordering::SeqCst);
|
||||
|
||||
|
||||
@@ -11,9 +11,6 @@ use zbus::{fdo, Connection};
|
||||
|
||||
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>> {
|
||||
println!(" user daemon v{}", rog_user::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
|
||||
if supported.anime_ctrl.0 {
|
||||
let early_return = Arc::new(AtomicBool::new(false));
|
||||
// Inner behind mutex required for thread safety
|
||||
let inner = Arc::new(Mutex::new(CtrlAnimeInner::new(
|
||||
anime,
|
||||
client,
|
||||
&ANIME_INNER_EARLY_RETURN,
|
||||
early_return.clone(),
|
||||
)?));
|
||||
// Need new client object for dbus control part
|
||||
let (client, _) = RogDbusClient::new().unwrap();
|
||||
let anime_control = CtrlAnime::new(
|
||||
anime_config,
|
||||
inner.clone(),
|
||||
client,
|
||||
&ANIME_INNER_EARLY_RETURN,
|
||||
)?;
|
||||
let anime_control = CtrlAnime::new(anime_config, inner.clone(), client, early_return)?;
|
||||
anime_control.add_to_server(&mut server);
|
||||
// Thread using inner
|
||||
let _anime_thread = thread::Builder::new()
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use rog_anime::{AnimTime, AnimeAction, Sequences, Vec2};
|
||||
use rog_anime::{ActionLoader, AnimTime, Fade, Sequences, Vec2};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::Error;
|
||||
@@ -12,7 +12,7 @@ use crate::error::Error;
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct UserAnimeConfig {
|
||||
pub name: String,
|
||||
pub anime: Vec<AnimeAction>,
|
||||
pub anime: Vec<ActionLoader>,
|
||||
}
|
||||
|
||||
impl UserAnimeConfig {
|
||||
@@ -95,34 +95,47 @@ impl Default for UserAnimeConfig {
|
||||
Self {
|
||||
name: "default".to_string(),
|
||||
anime: vec![
|
||||
AnimeAction::AsusAnimation {
|
||||
ActionLoader::AsusAnimation {
|
||||
file: "/usr/share/asusd/anime/asus/rog/Sunset.gif".into(),
|
||||
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(),
|
||||
scale: 0.9,
|
||||
angle: 0.65,
|
||||
translation: Vec2::default(),
|
||||
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(),
|
||||
scale: 1.0,
|
||||
angle: 0.0,
|
||||
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,
|
||||
},
|
||||
AnimeAction::Pause(Duration::from_secs(6)),
|
||||
AnimeAction::ImageAnimation {
|
||||
ActionLoader::Pause(Duration::from_secs(1)),
|
||||
ActionLoader::ImageAnimation {
|
||||
file: "/usr/share/asusd/anime/custom/sonic-wait.gif".into(),
|
||||
scale: 0.9,
|
||||
angle: 0.0,
|
||||
translation: Vec2::new(3.0, 2.0),
|
||||
brightness: 0.5,
|
||||
time: AnimTime::Cycles(2),
|
||||
time: AnimTime::Count(2),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "daemon"
|
||||
version = "3.6.2"
|
||||
version = "3.6.3"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::VERSION;
|
||||
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 std::fs::{File, OpenOptions};
|
||||
use std::io::{Read, Write};
|
||||
@@ -11,10 +12,10 @@ pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf";
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AnimeConfigV341 {
|
||||
pub system: Option<AnimeAction>,
|
||||
pub boot: Option<AnimeAction>,
|
||||
pub suspend: Option<AnimeAction>,
|
||||
pub shutdown: Option<AnimeAction>,
|
||||
pub system: Option<ActionLoader>,
|
||||
pub boot: Option<ActionLoader>,
|
||||
pub suspend: Option<ActionLoader>,
|
||||
pub shutdown: Option<ActionLoader>,
|
||||
}
|
||||
|
||||
impl AnimeConfigV341 {
|
||||
@@ -49,10 +50,10 @@ impl AnimeConfigV341 {
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AnimeConfigV352 {
|
||||
pub system: Vec<AnimeAction>,
|
||||
pub boot: Vec<AnimeAction>,
|
||||
pub wake: Vec<AnimeAction>,
|
||||
pub shutdown: Vec<AnimeAction>,
|
||||
pub system: Vec<ActionLoader>,
|
||||
pub boot: Vec<ActionLoader>,
|
||||
pub wake: Vec<ActionLoader>,
|
||||
pub shutdown: Vec<ActionLoader>,
|
||||
pub brightness: f32,
|
||||
}
|
||||
|
||||
@@ -110,10 +111,10 @@ impl AnimeConfigCached {
|
||||
/// Config for base system actions for the anime display
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AnimeConfig {
|
||||
pub system: Vec<AnimeAction>,
|
||||
pub boot: Vec<AnimeAction>,
|
||||
pub wake: Vec<AnimeAction>,
|
||||
pub shutdown: Vec<AnimeAction>,
|
||||
pub system: Vec<ActionLoader>,
|
||||
pub boot: Vec<ActionLoader>,
|
||||
pub wake: Vec<ActionLoader>,
|
||||
pub shutdown: Vec<ActionLoader>,
|
||||
pub brightness: f32,
|
||||
pub awake_enabled: bool,
|
||||
pub boot_anim_enabled: bool,
|
||||
@@ -165,8 +166,11 @@ impl AnimeConfig {
|
||||
info!("Updated config version to: {}", VERSION);
|
||||
return config;
|
||||
}
|
||||
warn!("Could not deserialise {}", ANIME_CONFIG_PATH);
|
||||
panic!("Please remove {} then restart asusd", ANIME_CONFIG_PATH);
|
||||
AnimeConfig::write_backup(buf);
|
||||
warn!(
|
||||
"Could not deserialise {}. Backed up as *-old",
|
||||
ANIME_CONFIG_PATH
|
||||
);
|
||||
}
|
||||
}
|
||||
AnimeConfig::create_default(&mut file)
|
||||
@@ -176,23 +180,31 @@ impl AnimeConfig {
|
||||
// create a default config here
|
||||
let config = AnimeConfig {
|
||||
system: vec![],
|
||||
boot: vec![AnimeAction::ImageAnimation {
|
||||
boot: vec![ActionLoader::ImageAnimation {
|
||||
file: "/usr/share/asusd/anime/custom/sonic-run.gif".into(),
|
||||
scale: 0.9,
|
||||
angle: 0.65,
|
||||
translation: Vec2::default(),
|
||||
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(),
|
||||
scale: 0.9,
|
||||
angle: 0.65,
|
||||
translation: Vec2::default(),
|
||||
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(),
|
||||
scale: 0.9,
|
||||
angle: 0.0,
|
||||
@@ -234,4 +246,12 @@ impl AnimeConfig {
|
||||
file.write_all(json.as_bytes())
|
||||
.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;
|
||||
|
||||
/// for parsing old v3.1.7 config
|
||||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
pub(crate) struct ConfigV317 {
|
||||
pub gfx_mode: GfxVendors,
|
||||
@@ -26,7 +27,7 @@ pub(crate) struct ConfigV317 {
|
||||
impl ConfigV317 {
|
||||
pub(crate) fn into_current(self) -> Config {
|
||||
Config {
|
||||
gfx_mode: GfxVendors::Hybrid,
|
||||
gfx_mode: self.gfx_mode,
|
||||
gfx_tmp_mode: None,
|
||||
gfx_managed: self.gfx_managed,
|
||||
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,
|
||||
VENDOR_ID,
|
||||
},
|
||||
ActionData, AnimTime, AnimeDataBuffer, AnimePacketType, AnimePowerStates, ANIME_DATA_LEN,
|
||||
ActionData, AnimeDataBuffer, AnimePacketType, AnimePowerStates, ANIME_DATA_LEN,
|
||||
};
|
||||
use rog_types::supported::AnimeSupportedFunctions;
|
||||
use rusb::{Device, DeviceHandle};
|
||||
@@ -13,7 +13,6 @@ use std::{
|
||||
error::Error,
|
||||
sync::{Arc, Mutex},
|
||||
thread::sleep,
|
||||
time::Instant,
|
||||
};
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
@@ -144,31 +143,15 @@ impl CtrlAnime {
|
||||
for action in actions.iter() {
|
||||
match action {
|
||||
ActionData::Animation(frames) => {
|
||||
let mut count = 0;
|
||||
let start = Instant::now();
|
||||
'animation: loop {
|
||||
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;
|
||||
}
|
||||
rog_anime::run_animation(frames, thread_exit.clone(), &|frame| {
|
||||
if let Ok(lock) = inner.try_lock() {
|
||||
lock.write_data_buffer(frame);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if thread_exit.load(Ordering::SeqCst) {
|
||||
break 'main;
|
||||
}
|
||||
}
|
||||
ActionData::Image(image) => {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use sysfs_class::RuntimePM;
|
||||
use ::zbus::Connection;
|
||||
use ctrl_gfx::error::GfxError;
|
||||
use ctrl_gfx::*;
|
||||
@@ -14,6 +13,7 @@ use std::{iter::FromIterator, thread::JoinHandle};
|
||||
use std::{process::Command, thread::sleep, time::Duration};
|
||||
use std::{str::FromStr, sync::mpsc};
|
||||
use std::{sync::Arc, sync::Mutex};
|
||||
use sysfs_class::RuntimePM;
|
||||
use sysfs_class::{PciDevice, SysClass};
|
||||
use system::{GraphicsDevice, PciBus};
|
||||
|
||||
@@ -67,8 +67,12 @@ impl CtrlGraphics {
|
||||
let mut nvidia = Vec::new();
|
||||
let mut other = Vec::new();
|
||||
for dev in devs.iter() {
|
||||
let c = dev.class().map_err(|err|{
|
||||
error!("GFX: device error: {}, {}", dev.path().to_string_lossy(), err);
|
||||
let c = dev.class().map_err(|err| {
|
||||
error!(
|
||||
"GFX: device error: {}, {}",
|
||||
dev.path().to_string_lossy(),
|
||||
err
|
||||
);
|
||||
err
|
||||
})?;
|
||||
if 0x03 == (c >> 16) & 0xFF {
|
||||
@@ -408,8 +412,12 @@ impl CtrlGraphics {
|
||||
// Make sure the power management is set to auto for nvidia devices
|
||||
let devs = PciDevice::all()?;
|
||||
for dev in devs.iter() {
|
||||
let c = dev.class().map_err(|err|{
|
||||
error!("GFX: device error: {}, {}", dev.path().to_string_lossy(), err);
|
||||
let c = dev.class().map_err(|err| {
|
||||
error!(
|
||||
"GFX: device error: {}, {}",
|
||||
dev.path().to_string_lossy(),
|
||||
err
|
||||
);
|
||||
err
|
||||
})?;
|
||||
if 0x03 == (c >> 16) & 0xFF && dev.vendor()? == 0x10DE {
|
||||
|
||||
@@ -89,7 +89,8 @@ impl CtrlFanAndCpu {
|
||||
*existing = profile.clone();
|
||||
existing.set_system_all()?;
|
||||
} else {
|
||||
config.power_profiles
|
||||
config
|
||||
.power_profiles
|
||||
.insert(profile.name.clone(), profile.clone());
|
||||
profile.set_system_all()?;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rog_anime"
|
||||
version = "1.0.4"
|
||||
version = "1.0.5"
|
||||
license = "MPL-2.0"
|
||||
readme = "README.md"
|
||||
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};
|
||||
#[cfg(feature = "dbus")]
|
||||
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`
|
||||
const BLOCK_START: usize = 7;
|
||||
/// *Not* inclusive, the byte before this is the final for each "pane"
|
||||
@@ -79,3 +90,93 @@ impl From<AnimeDataBuffer> for AnimePacketType {
|
||||
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 {
|
||||
/// Time in milliseconds for animation to run
|
||||
Time(Duration),
|
||||
/// How many full animation loops to run
|
||||
Cycles(u32),
|
||||
/// How many full animation loops to run or how many seconds if image is static
|
||||
Count(u32),
|
||||
/// Run for infinite time
|
||||
Infinite,
|
||||
/// Fade in, play for, fade out
|
||||
Fade(Fade),
|
||||
}
|
||||
|
||||
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
|
||||
/// that the animation should be shown for.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
@@ -67,6 +103,7 @@ impl AnimeGif {
|
||||
let mut decoder = decoder.read_info(file)?;
|
||||
|
||||
let mut frames = Vec::with_capacity(decoder.buffer_size());
|
||||
|
||||
while let Some(frame) = decoder.read_next_frame()? {
|
||||
let wait = frame.delay * 10;
|
||||
if matches!(frame.dispose, gif::DisposalMethod::Background) {
|
||||
@@ -162,6 +199,39 @@ impl AnimeGif {
|
||||
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
|
||||
#[inline]
|
||||
pub fn frames(&self) -> &[AnimeFrame] {
|
||||
@@ -173,4 +243,16 @@ impl AnimeGif {
|
||||
pub fn duration(&self) -> AnimTime {
|
||||
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::{
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use glam::Vec2;
|
||||
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
|
||||
/// a helper for loading up `ActionData`.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum AnimeAction {
|
||||
pub enum ActionLoader {
|
||||
/// Full gif sequence. Immutable.
|
||||
AsusAnimation {
|
||||
file: PathBuf,
|
||||
time: AnimTime,
|
||||
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 {
|
||||
file: PathBuf,
|
||||
scale: f32,
|
||||
@@ -32,6 +29,7 @@ pub enum AnimeAction {
|
||||
scale: f32,
|
||||
angle: f32,
|
||||
translation: Vec2,
|
||||
time: Option<AnimTime>,
|
||||
brightness: f32,
|
||||
},
|
||||
/// A pause to be used between sequences
|
||||
@@ -59,9 +57,9 @@ pub enum 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 {
|
||||
AnimeAction::AsusAnimation {
|
||||
ActionLoader::AsusAnimation {
|
||||
file,
|
||||
time: duration,
|
||||
brightness,
|
||||
@@ -70,33 +68,59 @@ impl ActionData {
|
||||
*duration,
|
||||
*brightness,
|
||||
)?),
|
||||
AnimeAction::ImageAnimation {
|
||||
ActionLoader::ImageAnimation {
|
||||
file,
|
||||
scale,
|
||||
angle,
|
||||
translation,
|
||||
time: duration,
|
||||
brightness,
|
||||
} => ActionData::Animation(AnimeGif::create_png_gif(
|
||||
&file,
|
||||
*scale,
|
||||
*angle,
|
||||
*translation,
|
||||
*duration,
|
||||
*brightness,
|
||||
)?),
|
||||
AnimeAction::Image {
|
||||
} => {
|
||||
if let Some(ext) = file.extension() {
|
||||
if ext.to_string_lossy().to_lowercase() == "png" {
|
||||
return Ok(ActionData::Animation(AnimeGif::create_png_static(
|
||||
&file,
|
||||
*scale,
|
||||
*angle,
|
||||
*translation,
|
||||
*duration,
|
||||
*brightness,
|
||||
)?));
|
||||
}
|
||||
}
|
||||
ActionData::Animation(AnimeGif::create_png_gif(
|
||||
&file,
|
||||
*scale,
|
||||
*angle,
|
||||
*translation,
|
||||
*duration,
|
||||
*brightness,
|
||||
)?)
|
||||
}
|
||||
ActionLoader::Image {
|
||||
file,
|
||||
scale,
|
||||
angle,
|
||||
translation,
|
||||
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 data = <AnimeDataBuffer>::from(&image);
|
||||
ActionData::Image(Box::new(data))
|
||||
}
|
||||
AnimeAction::Pause(duration) => ActionData::Pause(*duration),
|
||||
ActionLoader::Pause(duration) => ActionData::Pause(*duration),
|
||||
};
|
||||
Ok(a)
|
||||
}
|
||||
@@ -115,38 +139,8 @@ impl Sequences {
|
||||
/// Use a base `AnimeAction` to generate the precomputed data and insert in to
|
||||
/// the run buffer
|
||||
#[inline]
|
||||
pub fn insert(&mut self, index: usize, action: &AnimeAction) -> Result<(), AnimeError> {
|
||||
match 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),
|
||||
};
|
||||
pub fn insert(&mut self, index: usize, action: &ActionLoader) -> Result<(), AnimeError> {
|
||||
self.0.insert(index, ActionData::from_anime_action(action)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -161,66 +155,6 @@ impl Sequences {
|
||||
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 {
|
||||
ActionIterator {
|
||||
actions: &self,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::sync::mpsc::Sender;
|
||||
use rog_anime::{AnimeDataBuffer, AnimePowerStates};
|
||||
use std::sync::mpsc::Sender;
|
||||
use zbus::{dbus_proxy, Connection, Result};
|
||||
|
||||
#[dbus_proxy(
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
//!
|
||||
//! …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};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use rog_aura::AuraModeNum;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use zvariant_derive::Type;
|
||||
use std::fmt;
|
||||
use zvariant_derive::Type;
|
||||
|
||||
#[derive(Serialize, Deserialize, Type, Debug)]
|
||||
pub struct SupportedFunctions {
|
||||
@@ -60,7 +60,11 @@ impl fmt::Display for AnimeSupportedFunctions {
|
||||
impl fmt::Display for ChargeSupportedFunctions {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user