Adjust how thread exit is handled for anime controller

This commit is contained in:
Luke D. Jones
2022-06-20 22:43:12 +12:00
parent 2ee7fc9910
commit 13a90b00f3
11 changed files with 109 additions and 89 deletions

1
Cargo.lock generated
View File

@@ -1022,6 +1022,7 @@ version = "1.3.3"
dependencies = [ dependencies = [
"gif", "gif",
"glam", "glam",
"log",
"pix", "pix",
"png_pong", "png_pong",
"serde", "serde",

View File

@@ -59,7 +59,10 @@ pub struct LedModeCommand {
help = "set the keyboard LED suspend animation to enabled while the device is suspended" help = "set the keyboard LED suspend animation to enabled while the device is suspended"
)] )]
pub sleep_enable: Option<bool>, pub sleep_enable: Option<bool>,
#[options(meta = "", help = "set the full keyboard LEDs (keys and side) to enabled")] #[options(
meta = "",
help = "set the full keyboard LEDs (keys and side) to enabled"
)]
pub all_leds_enable: Option<bool>, pub all_leds_enable: Option<bool>,
#[options(meta = "", help = "set the keyboard keys LEDs to enabled")] #[options(meta = "", help = "set the keyboard keys LEDs to enabled")]
pub keys_leds_enable: Option<bool>, pub keys_leds_enable: Option<bool>,

View File

@@ -365,8 +365,7 @@ fn handle_led_mode(
return true; return true;
} }
} }
if supported.multizone_led_mode && command.trim().starts_with("multi") if supported.multizone_led_mode && command.trim().starts_with("multi") {
{
return true; return true;
} }
false false

View File

@@ -91,12 +91,16 @@ 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) => {
rog_anime::run_animation(frames, self.do_early_return.clone(), &|output| { rog_anime::run_animation(frames, &|output| {
if self.do_early_return.load(Ordering::Acquire) {
return Ok(true); // Do safe exit
}
self.client self.client
.proxies() .proxies()
.anime() .anime()
.write(output) .write(output)
.map_err(|e| AnimeError::Dbus(format!("{}", e))) .map_err(|e| AnimeError::Dbus(format!("{}", e)))
.map(|_| false)
})?; })?;
} }
ActionData::Image(image) => { ActionData::Image(image) => {

View File

@@ -141,6 +141,7 @@ impl CtrlAnime {
warn!("AniMe system actions was empty"); warn!("AniMe system actions was empty");
return; return;
} }
// Loop rules: // Loop rules:
// - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible. // - Lock the mutex **only when required**. That is, the lock must be held for the shortest duration possible.
// - An AtomicBool used for thread exit should be checked in every loop, including nested // - An AtomicBool used for thread exit should be checked in every loop, including nested
@@ -150,55 +151,54 @@ impl CtrlAnime {
std::thread::Builder::new() std::thread::Builder::new()
.name("AniMe system thread start".into()) .name("AniMe system thread start".into())
.spawn(move || { .spawn(move || {
info!("AniMe system thread started"); info!("AniMe new system thread started");
// Getting copies of these Atomics is done *in* the thread to ensure // Getting copies of these Atomics is done *in* the thread to ensure
// we don't block other threads/main // we don't block other threads/main
let thread_exit; let thread_exit;
let thread_running; let thread_running;
// First two loops are to ensure we *do* aquire a lock on the mutex
// The reason the loop is required is because the USB writes can block
// for up to 10ms. We can't fail to get the atomics.
loop { loop {
if let Ok(lock) = inner.try_lock() { if let Ok(lock) = inner.try_lock() {
thread_exit = lock.thread_exit.clone(); thread_exit = lock.thread_exit.clone();
thread_running = lock.thread_running.clone(); thread_running = lock.thread_running.clone();
// Make any running loop exit first
thread_exit.store(true, Ordering::SeqCst);
break; break;
} }
} }
// First two loops are to ensure we *do* aquire a lock on the mutex
loop { // The reason the loop is required is because the USB writes can block
// wait for other threads to set not running so we know they exited // for up to 10ms. We can't fail to get the atomics.
if !thread_running.load(Ordering::SeqCst) { while thread_running.load(Ordering::SeqCst) {
info!("AniMe forced a thread to exit"); // Make any running loop exit first
break; thread_exit.store(true, Ordering::SeqCst);
} break;
} }
info!("AniMe no previous system thread running (now)");
thread_exit.store(false, Ordering::SeqCst); thread_exit.store(false, Ordering::SeqCst);
thread_running.store(true, Ordering::SeqCst);
'main: loop { 'main: loop {
thread_running.store(true, Ordering::SeqCst);
for action in actions.iter() { for action in actions.iter() {
if thread_exit.load(Ordering::SeqCst) { if thread_exit.load(Ordering::SeqCst) {
break 'main; break 'main;
} }
match action { match action {
ActionData::Animation(frames) => { ActionData::Animation(frames) => {
if let Err(err) = rog_anime::run_animation( if let Err(err) = rog_anime::run_animation(frames, &|frame| {
frames, if thread_exit.load(Ordering::Acquire) {
thread_exit.clone(), info!("rog-anime: frame-loop was asked to exit");
&|frame| { return Ok(true); // Do safe exit
inner }
.try_lock() inner
.map(|lock| lock.write_data_buffer(frame)) .try_lock()
.map_err(|err| { .map(|lock| {
warn!("rog_anime::run_animation:callback {}", err); lock.write_data_buffer(frame);
AnimeError::NoFrames false // Don't exit yet
}) })
}, .map_err(|err| {
) { warn!("rog_anime::run_animation:callback {}", err);
AnimeError::NoFrames
})
}) {
warn!("rog_anime::run_animation:Animation {}", err); warn!("rog_anime::run_animation:Animation {}", err);
break 'main; break 'main;
}; };

View File

@@ -83,7 +83,6 @@ impl AuraConfigV407 {
} }
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
pub struct AuraConfig { pub struct AuraConfig {
pub brightness: LedBrightness, pub brightness: LedBrightness,
@@ -94,7 +93,7 @@ pub struct AuraConfig {
pub sleep_anim_enabled: bool, pub sleep_anim_enabled: bool,
pub all_leds_enabled: bool, pub all_leds_enabled: bool,
pub keys_leds_enabled: bool, pub keys_leds_enabled: bool,
pub side_leds_enabled: bool pub side_leds_enabled: bool,
} }
impl Default for AuraConfig { impl Default for AuraConfig {

View File

@@ -9,10 +9,9 @@ use crate::{
use async_trait::async_trait; use async_trait::async_trait;
use log::{error, info, warn}; use log::{error, info, warn};
use logind_zbus::manager::ManagerProxy; use logind_zbus::manager::ManagerProxy;
use rog_aura::usb::leds_message;
use rog_aura::{ use rog_aura::{
usb::{ usb::{LED_APPLY, LED_SET},
LED_APPLY, LED_SET
},
AuraEffect, LedBrightness, LED_MSG_LEN, AuraEffect, LedBrightness, LED_MSG_LEN,
}; };
use rog_supported::LedSupportedFunctions; use rog_supported::LedSupportedFunctions;
@@ -23,14 +22,11 @@ use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::sync::Mutex; use std::sync::Mutex;
use zbus::Connection; use zbus::Connection;
use rog_aura::usb::leds_message;
use crate::GetSupported; use crate::GetSupported;
use super::config::AuraConfig; use super::config::AuraConfig;
impl GetSupported for CtrlKbdLed { impl GetSupported for CtrlKbdLed {
type A = LedSupportedFunctions; type A = LedSupportedFunctions;
@@ -281,20 +277,19 @@ impl CtrlKbdLed {
self.set_brightness(self.config.brightness) self.set_brightness(self.config.brightness)
} }
/// Set combination state for boot animation/sleep animation/all leds/keys leds/side leds LED active /// Set combination state for boot animation/sleep animation/all leds/keys leds/side leds LED active
pub(super) fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> { pub(super) fn set_power_states(&self, config: &AuraConfig) -> Result<(), RogError> {
let bytes = leds_message(
let bytes = leds_message(config.boot_anim_enabled, config.boot_anim_enabled,
config.sleep_anim_enabled, config.sleep_anim_enabled,
config.all_leds_enabled, config.all_leds_enabled,
config.keys_leds_enabled, config.keys_leds_enabled,
config.side_leds_enabled); config.side_leds_enabled,
);
// Quite ugly, must be a more idiomatic way to do // Quite ugly, must be a more idiomatic way to do
let message = [ let message = [
0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0x5d, 0xbd, 0x01, bytes[0], bytes[1], bytes[2], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
]; ];
self.write_bytes(&message)?; self.write_bytes(&message)?;

View File

@@ -38,15 +38,15 @@ impl CtrlKbdLedZbus {
ctrl.config.write(); ctrl.config.write();
ctrl.set_power_states(&ctrl.config) ctrl.set_power_states(&ctrl.config)
.map_err(|err| warn!("{}", err)) .map_err(|err| warn!("{}", err))
.ok(); .ok();
states = Some(LedPowerStates { states = Some(LedPowerStates {
boot_anim: ctrl.config.boot_anim_enabled, boot_anim: ctrl.config.boot_anim_enabled,
sleep_anim: ctrl.config.sleep_anim_enabled, sleep_anim: ctrl.config.sleep_anim_enabled,
all_leds: ctrl.config.all_leds_enabled, all_leds: ctrl.config.all_leds_enabled,
keys_leds: ctrl.config.keys_leds_enabled, keys_leds: ctrl.config.keys_leds_enabled,
side_leds: ctrl.config.side_leds_enabled side_leds: ctrl.config.side_leds_enabled,
}); });
} }
// Need to pull state out like this due to MutexGuard // Need to pull state out like this due to MutexGuard
@@ -69,15 +69,15 @@ impl CtrlKbdLedZbus {
ctrl.config.write(); ctrl.config.write();
ctrl.set_power_states(&ctrl.config) ctrl.set_power_states(&ctrl.config)
.map_err(|err| warn!("{}", err)) .map_err(|err| warn!("{}", err))
.ok(); .ok();
states = Some(LedPowerStates { states = Some(LedPowerStates {
boot_anim: ctrl.config.boot_anim_enabled, boot_anim: ctrl.config.boot_anim_enabled,
sleep_anim: ctrl.config.sleep_anim_enabled, sleep_anim: ctrl.config.sleep_anim_enabled,
all_leds: ctrl.config.all_leds_enabled, all_leds: ctrl.config.all_leds_enabled,
keys_leds: ctrl.config.keys_leds_enabled, keys_leds: ctrl.config.keys_leds_enabled,
side_leds: ctrl.config.side_leds_enabled side_leds: ctrl.config.side_leds_enabled,
}); });
} }
if let Some(states) = states { if let Some(states) = states {
@@ -109,7 +109,7 @@ impl CtrlKbdLedZbus {
sleep_anim: ctrl.config.sleep_anim_enabled, sleep_anim: ctrl.config.sleep_anim_enabled,
all_leds: ctrl.config.all_leds_enabled, all_leds: ctrl.config.all_leds_enabled,
keys_leds: ctrl.config.keys_leds_enabled, keys_leds: ctrl.config.keys_leds_enabled,
side_leds: ctrl.config.side_leds_enabled side_leds: ctrl.config.side_leds_enabled,
}); });
} }
// Need to pull state out like this due to MutexGuard // Need to pull state out like this due to MutexGuard
@@ -140,7 +140,7 @@ impl CtrlKbdLedZbus {
sleep_anim: ctrl.config.sleep_anim_enabled, sleep_anim: ctrl.config.sleep_anim_enabled,
all_leds: ctrl.config.all_leds_enabled, all_leds: ctrl.config.all_leds_enabled,
keys_leds: ctrl.config.keys_leds_enabled, keys_leds: ctrl.config.keys_leds_enabled,
side_leds: ctrl.config.side_leds_enabled side_leds: ctrl.config.side_leds_enabled,
}); });
} }
// Need to pull state out like this due to MutexGuard // Need to pull state out like this due to MutexGuard
@@ -171,7 +171,7 @@ impl CtrlKbdLedZbus {
sleep_anim: ctrl.config.sleep_anim_enabled, sleep_anim: ctrl.config.sleep_anim_enabled,
all_leds: ctrl.config.all_leds_enabled, all_leds: ctrl.config.all_leds_enabled,
keys_leds: ctrl.config.keys_leds_enabled, keys_leds: ctrl.config.keys_leds_enabled,
side_leds: ctrl.config.side_leds_enabled side_leds: ctrl.config.side_leds_enabled,
}); });
} }
// Need to pull state out like this due to MutexGuard // Need to pull state out like this due to MutexGuard

View File

@@ -20,6 +20,7 @@ dbus = ["zvariant"]
png_pong = "^0.8.0" png_pong = "^0.8.0"
pix = "0.13" pix = "0.13"
gif = "^0.11.2" gif = "^0.11.2"
log = "*"
serde = "^1.0" serde = "^1.0"
serde_derive = "^1.0" serde_derive = "^1.0"

View File

@@ -1,12 +1,9 @@
use std::{ use std::{
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread::sleep, thread::sleep,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use log::info;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
#[cfg(feature = "dbus")] #[cfg(feature = "dbus")]
use zvariant::Type; use zvariant::Type;
@@ -92,10 +89,11 @@ impl From<AnimeDataBuffer> for AnimePacketType {
} }
/// This runs the animations as a blocking loop by using the `callback` to write data /// This runs the animations as a blocking loop by using the `callback` to write data
///
/// If `callback` is `Ok(true)` then `run_animation` will exit the animation loop early.
pub fn run_animation( pub fn run_animation(
frames: &AnimeGif, frames: &AnimeGif,
do_early_return: Arc<AtomicBool>, callback: &dyn Fn(AnimeDataBuffer) -> Result<bool, AnimeError>,
callback: &dyn Fn(AnimeDataBuffer) -> Result<(), AnimeError>,
) -> Result<(), AnimeError> { ) -> Result<(), AnimeError> {
let mut count = 0; let mut count = 0;
let start = Instant::now(); let start = Instant::now();
@@ -140,9 +138,6 @@ pub fn run_animation(
'animation: loop { 'animation: loop {
for frame in frames.frames() { for frame in frames.frames() {
let frame_start = Instant::now(); let frame_start = Instant::now();
if do_early_return.load(Ordering::SeqCst) {
return Ok(());
}
let mut output = frame.frame().clone(); let mut output = frame.frame().clone();
if let AnimTime::Fade(_) = frames.duration() { if let AnimTime::Fade(_) = frames.duration() {
@@ -164,12 +159,14 @@ pub fn run_animation(
} }
} }
callback(output).ok(); if matches!(callback(output), Ok(true)) {
info!("rog-anime: frame-loop callback asked to exit early");
return Ok(());
}
if timed && Instant::now().duration_since(start) > run_time { if timed && Instant::now().duration_since(start) > run_time {
break 'animation; break 'animation;
} }
sleep(frame.delay()); sleep(frame.delay());
} }
if let AnimTime::Count(times) = frames.duration() { if let AnimTime::Count(times) = frames.duration() {

View File

@@ -1,6 +1,6 @@
use crate::usb::LedCfgState::{Off, On};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::ops::{BitAnd, BitOr}; use std::ops::{BitAnd, BitOr};
use crate::usb::LedCfgState::{Off, On};
pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9]; pub const LED_INIT1: [u8; 2] = [0x5d, 0xb9];
pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d pub const LED_INIT2: &str = "]ASUS Tech.Inc."; // ] == 0x5d
@@ -12,12 +12,12 @@ pub const LED_INIT5: [u8; 6] = [0x5e, 0x05, 0x20, 0x31, 0, 0x08];
pub const LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; pub const LED_APPLY: [u8; 17] = [0x5d, 0xb4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
pub const LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; pub const LED_SET: [u8; 17] = [0x5d, 0xb5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
pub const BOOT_MASK:i32 = 0xc31309; pub const BOOT_MASK: i32 = 0xc31309;
pub const SLEEP_MASK:i32 = 0x300904; pub const SLEEP_MASK: i32 = 0x300904;
pub const ALL_LEDS_MASK:i32 = 0x000002; pub const ALL_LEDS_MASK: i32 = 0x000002;
pub const KBD_LEDS_MASK:i32 = 0x080000; pub const KBD_LEDS_MASK: i32 = 0x080000;
pub const SIDE_LEDS_MASK:i32 = 0x040500; pub const SIDE_LEDS_MASK: i32 = 0x040500;
pub const LEDS_STATE_MASK:i32 = ALL_LEDS_MASK | KBD_LEDS_MASK | SIDE_LEDS_MASK; pub const LEDS_STATE_MASK: i32 = ALL_LEDS_MASK | KBD_LEDS_MASK | SIDE_LEDS_MASK;
/// Writes out the correct byte string for brightness /// Writes out the correct byte string for brightness
pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] { pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
@@ -29,7 +29,7 @@ pub const fn aura_brightness_bytes(brightness: u8) -> [u8; 17] {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum LedCfgState { pub enum LedCfgState {
On = 0xffffff, On = 0xffffff,
Off = 0x0 Off = 0x0,
} }
impl From<i32> for LedCfgState { impl From<i32> for LedCfgState {
@@ -37,7 +37,7 @@ impl From<i32> for LedCfgState {
match state { match state {
0xffffff => On, 0xffffff => On,
0x0 => Off, 0x0 => Off,
_ => Off _ => Off,
} }
} }
} }
@@ -46,19 +46,19 @@ impl From<bool> for LedCfgState {
fn from(state: bool) -> Self { fn from(state: bool) -> Self {
match state { match state {
true => On, true => On,
false => Off false => Off,
} }
} }
} }
impl TryFrom <[u8; 3]> for LedCfgState { impl TryFrom<[u8; 3]> for LedCfgState {
type Error = &'static str; type Error = &'static str;
fn try_from(value: [u8; 3]) -> Result<Self, Self::Error> { fn try_from(value: [u8; 3]) -> Result<Self, Self::Error> {
match value { match value {
[0xff, 0xff, 0xff] => Ok(On), [0xff, 0xff, 0xff] => Ok(On),
[0, 0, 0] => Ok(Off), [0, 0, 0] => Ok(Off),
_ => Err("Unconvertible value") _ => Err("Unconvertible value"),
} }
} }
} }
@@ -67,14 +67,14 @@ impl BitAnd<LedCfgState> for i32 {
type Output = i32; type Output = i32;
fn bitand(self, rhs: LedCfgState) -> i32 { fn bitand(self, rhs: LedCfgState) -> i32 {
return self & rhs as i32 return self & rhs as i32;
} }
} }
impl BitOr<LedCfgState> for i32 { impl BitOr<LedCfgState> for i32 {
type Output = i32; type Output = i32;
fn bitor(self, rhs: LedCfgState) -> Self::Output { fn bitor(self, rhs: LedCfgState) -> Self::Output {
return self | rhs as i32 return self | rhs as i32;
} }
} }
@@ -94,17 +94,38 @@ impl BitAnd<LedCfgState> for LedCfgState {
} }
} }
pub fn leds_message (boot_state: bool, sleep_state: bool, all_leds_state: bool, kbd_leds_state: bool, side_leds_state: bool) -> [u8; 3] { pub fn leds_message(
let raw_message = _leds_message(boot_state.into(), sleep_state.into(), all_leds_state.into(), kbd_leds_state.into(), side_leds_state.into()); boot_state: bool,
sleep_state: bool,
all_leds_state: bool,
kbd_leds_state: bool,
side_leds_state: bool,
) -> [u8; 3] {
let raw_message = _leds_message(
boot_state.into(),
sleep_state.into(),
all_leds_state.into(),
kbd_leds_state.into(),
side_leds_state.into(),
);
let [_, lows @ ..] = i32::to_be_bytes(raw_message); let [_, lows @ ..] = i32::to_be_bytes(raw_message);
return lows; return lows;
} }
fn _leds_message (boot_state: LedCfgState, sleep_state: LedCfgState, all_leds_state: LedCfgState, kbd_leds_state: LedCfgState, side_leds_state: LedCfgState) -> i32 { fn _leds_message(
boot_state: LedCfgState,
sleep_state: LedCfgState,
all_leds_state: LedCfgState,
kbd_leds_state: LedCfgState,
side_leds_state: LedCfgState,
) -> i32 {
let full_leds_state = match all_leds_state { let full_leds_state = match all_leds_state {
On => (ALL_LEDS_MASK & all_leds_state) | (KBD_LEDS_MASK & kbd_leds_state) | (SIDE_LEDS_MASK & side_leds_state), On => {
(ALL_LEDS_MASK & all_leds_state)
| (KBD_LEDS_MASK & kbd_leds_state)
| (SIDE_LEDS_MASK & side_leds_state)
}
Off => 0x0100 & side_leds_state, Off => 0x0100 & side_leds_state,
}; };
@@ -112,6 +133,6 @@ fn _leds_message (boot_state: LedCfgState, sleep_state: LedCfgState, all_leds_st
return match (all_leds_state | kbd_leds_state | side_leds_state).into() { return match (all_leds_state | kbd_leds_state | side_leds_state).into() {
On => boot_xor_sleep ^ ((boot_xor_sleep ^ full_leds_state) & LEDS_STATE_MASK), On => boot_xor_sleep ^ ((boot_xor_sleep ^ full_leds_state) & LEDS_STATE_MASK),
_ => boot_xor_sleep _ => boot_xor_sleep,
} };
} }