mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-02-06 00:15:04 +01:00
Major restructure to move gfx control out to crate
This commit is contained in:
19
Cargo.lock
generated
19
Cargo.lock
generated
@@ -39,6 +39,7 @@ dependencies = [
|
|||||||
"rog_profiles",
|
"rog_profiles",
|
||||||
"rog_types",
|
"rog_types",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"supergfxctl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -54,6 +55,7 @@ dependencies = [
|
|||||||
"rog_dbus",
|
"rog_dbus",
|
||||||
"rog_profiles",
|
"rog_profiles",
|
||||||
"rog_types",
|
"rog_types",
|
||||||
|
"supergfxctl",
|
||||||
"tinybmp",
|
"tinybmp",
|
||||||
"yansi-term",
|
"yansi-term",
|
||||||
]
|
]
|
||||||
@@ -219,6 +221,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"supergfxctl",
|
||||||
"sysfs-class",
|
"sysfs-class",
|
||||||
"toml",
|
"toml",
|
||||||
"udev",
|
"udev",
|
||||||
@@ -922,6 +925,7 @@ dependencies = [
|
|||||||
"rog_aura",
|
"rog_aura",
|
||||||
"rog_profiles",
|
"rog_profiles",
|
||||||
"rog_types",
|
"rog_types",
|
||||||
|
"supergfxctl",
|
||||||
"zbus",
|
"zbus",
|
||||||
"zbus_macros",
|
"zbus_macros",
|
||||||
"zvariant",
|
"zvariant",
|
||||||
@@ -1062,6 +1066,21 @@ dependencies = [
|
|||||||
"syn 0.11.11",
|
"syn 0.11.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "supergfxctl"
|
||||||
|
version = "1.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"logind-zbus",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"sysfs-class",
|
||||||
|
"zbus",
|
||||||
|
"zvariant",
|
||||||
|
"zvariant_derive",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "0.11.11"
|
version = "0.11.11"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-types", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles"]
|
members = ["asusctl", "asus-notify", "daemon", "daemon-user", "rog-types", "rog-dbus", "rog-anime", "rog-aura", "rog-profiles", "supergfx"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
@@ -13,4 +13,4 @@ opt-level = 1
|
|||||||
|
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
debug = false
|
debug = false
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ rog_dbus = { path = "../rog-dbus" }
|
|||||||
rog_aura = { path = "../rog-aura" }
|
rog_aura = { path = "../rog-aura" }
|
||||||
rog_types = { path = "../rog-types" }
|
rog_types = { path = "../rog-types" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
|
supergfxctl = { path = "../supergfx" }
|
||||||
|
|
||||||
[dependencies.notify-rust]
|
[dependencies.notify-rust]
|
||||||
version = "^4.3"
|
version = "^4.3"
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ use notify_rust::{Hint, Notification, NotificationHandle};
|
|||||||
use rog_aura::AuraEffect;
|
use rog_aura::AuraEffect;
|
||||||
use rog_dbus::{DbusProxies, Signals};
|
use rog_dbus::{DbusProxies, Signals};
|
||||||
use rog_profiles::Profile;
|
use rog_profiles::Profile;
|
||||||
use rog_types::gfx_vendors::GfxRequiredUserAction;
|
use supergfxctl::gfx_vendors::{GfxRequiredUserAction, GfxVendors};
|
||||||
use rog_types::gfx_vendors::GfxVendors;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ rog_types = { path = "../rog-types" }
|
|||||||
daemon = { path = "../daemon" }
|
daemon = { path = "../daemon" }
|
||||||
gumdrop = "^0.8"
|
gumdrop = "^0.8"
|
||||||
yansi-term = "^0.1"
|
yansi-term = "^0.1"
|
||||||
|
supergfxctl = { path = "../supergfx" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tinybmp = "^0.2.3"
|
tinybmp = "^0.2.3"
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ use rog_anime::{AnimeDataBuffer, AnimeImage, Vec2, ANIME_DATA_LEN};
|
|||||||
use rog_aura::{self, AuraEffect};
|
use rog_aura::{self, AuraEffect};
|
||||||
use rog_dbus::RogDbusClient;
|
use rog_dbus::RogDbusClient;
|
||||||
use rog_types::{
|
use rog_types::{
|
||||||
gfx_vendors::{GfxRequiredUserAction, GfxVendors},
|
|
||||||
supported::{
|
supported::{
|
||||||
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
|
AnimeSupportedFunctions, LedSupportedFunctions, PlatformProfileFunctions,
|
||||||
RogBiosSupportedFunctions,
|
RogBiosSupportedFunctions,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use supergfxctl::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
|
||||||
use std::{env::args, path::Path};
|
use std::{env::args, path::Path};
|
||||||
use yansi_term::Colour::Green;
|
use yansi_term::Colour::Green;
|
||||||
use yansi_term::Colour::Red;
|
use yansi_term::Colour::Red;
|
||||||
@@ -240,7 +240,7 @@ fn do_gfx(
|
|||||||
if command.pow {
|
if command.pow {
|
||||||
let res = dbus.proxies().gfx().gfx_get_pwr()?;
|
let res = dbus.proxies().gfx().gfx_get_pwr()?;
|
||||||
match res {
|
match res {
|
||||||
rog_types::gfx_vendors::GfxPower::Active => {
|
GfxPower::Active => {
|
||||||
println!("Current power status: {}", Red.paint(<&str>::from(&res)))
|
println!("Current power status: {}", Red.paint(<&str>::from(&res)))
|
||||||
}
|
}
|
||||||
_ => println!("Current power status: {}", Green.paint(<&str>::from(&res))),
|
_ => println!("Current power status: {}", Green.paint(<&str>::from(&res))),
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ rog_aura = { path = "../rog-aura" }
|
|||||||
rog_types = { path = "../rog-types" }
|
rog_types = { path = "../rog-types" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
rog_dbus = { path = "../rog-dbus" }
|
rog_dbus = { path = "../rog-dbus" }
|
||||||
|
supergfxctl = { path = "../supergfx" }
|
||||||
rusb = "^0.8"
|
rusb = "^0.8"
|
||||||
udev = "^0.6"
|
udev = "^0.6"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use rog_types::gfx_vendors::GfxVendors;
|
|
||||||
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};
|
||||||
@@ -8,33 +7,20 @@ use crate::config_old::*;
|
|||||||
use crate::VERSION;
|
use crate::VERSION;
|
||||||
|
|
||||||
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||||
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
/// Only for informational purposes.
|
|
||||||
#[serde(skip)]
|
|
||||||
pub gfx_tmp_mode: Option<GfxVendors>,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub gfx_vfio_enable: bool,
|
|
||||||
/// Save charge limit for restoring on boot
|
/// Save charge limit for restoring on boot
|
||||||
pub bat_charge_limit: u8,
|
pub bat_charge_limit: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Config {
|
||||||
fn default() -> Self {
|
fn new() -> Self {
|
||||||
Config {
|
Config {
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: true,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
bat_charge_limit: 100,
|
bat_charge_limit: 100,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
/// `load` will attempt to read the config, and panic if the dir is missing
|
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||||
pub fn load() -> Self {
|
pub fn load() -> Self {
|
||||||
let mut file = OpenOptions::new()
|
let mut file = OpenOptions::new()
|
||||||
@@ -44,47 +30,23 @@ impl Config {
|
|||||||
.open(&CONFIG_PATH)
|
.open(&CONFIG_PATH)
|
||||||
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here
|
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
let config;
|
||||||
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
if read_len == 0 {
|
if read_len == 0 {
|
||||||
return Config::create_default(&mut file);
|
config = Self::new();
|
||||||
|
} else if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
|
config = data;
|
||||||
|
} else if let Ok(data) = serde_json::from_str::<ConfigV352>(&buf) {
|
||||||
|
config = data.into_current();
|
||||||
|
info!("Updated config version to: {}", VERSION);
|
||||||
} else {
|
} else {
|
||||||
if let Ok(data) = serde_json::from_str(&buf) {
|
|
||||||
return data;
|
|
||||||
} else if let Ok(data) = serde_json::from_str::<ConfigV352>(&buf) {
|
|
||||||
let config = data.into_current();
|
|
||||||
config.write();
|
|
||||||
info!("Updated config version to: {}", VERSION);
|
|
||||||
return config;
|
|
||||||
} else if let Ok(data) = serde_json::from_str::<ConfigV341>(&buf) {
|
|
||||||
let config = data.into_current();
|
|
||||||
config.write();
|
|
||||||
info!("Updated config version to: {}", VERSION);
|
|
||||||
return config;
|
|
||||||
} else if let Ok(data) = serde_json::from_str::<ConfigV324>(&buf) {
|
|
||||||
let config = data.into_current();
|
|
||||||
config.write();
|
|
||||||
info!("Updated config version to: {}", VERSION);
|
|
||||||
return config;
|
|
||||||
} else if let Ok(data) = serde_json::from_str::<ConfigV317>(&buf) {
|
|
||||||
let config = data.into_current();
|
|
||||||
config.write();
|
|
||||||
info!("Updated config version to: {}", VERSION);
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
warn!("Could not deserialise {}", CONFIG_PATH);
|
warn!("Could not deserialise {}", CONFIG_PATH);
|
||||||
panic!("Please remove {} then restart asusd", CONFIG_PATH);
|
panic!("Please remove {} then restart asusd", CONFIG_PATH);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
config = Self::new()
|
||||||
}
|
}
|
||||||
Config::create_default(&mut file)
|
config.write();
|
||||||
}
|
|
||||||
|
|
||||||
fn create_default(file: &mut File) -> Self {
|
|
||||||
let config = Config::default();
|
|
||||||
|
|
||||||
// Should be okay to unwrap this as is since it is a Default
|
|
||||||
let json = serde_json::to_string_pretty(&config).unwrap();
|
|
||||||
file.write_all(json.as_bytes())
|
|
||||||
.unwrap_or_else(|_| panic!("Could not write {}", CONFIG_PATH));
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,11 +60,8 @@ impl Config {
|
|||||||
if l == 0 {
|
if l == 0 {
|
||||||
warn!("File is empty {}", CONFIG_PATH);
|
warn!("File is empty {}", CONFIG_PATH);
|
||||||
} else {
|
} else {
|
||||||
let mut x: Config = serde_json::from_str(&buf)
|
serde_json::from_str(&buf)
|
||||||
.unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH));
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", CONFIG_PATH))
|
||||||
// copy over serde skipped values
|
|
||||||
x.gfx_tmp_mode = self.gfx_tmp_mode;
|
|
||||||
*self = x;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +1,9 @@
|
|||||||
use rog_types::gfx_vendors::GfxVendors;
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use supergfxctl::gfx_vendors::GfxVendors;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
/// for parsing old v3.1.7 config
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub(crate) struct ConfigV317 {
|
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub active_profile: String,
|
|
||||||
pub toggle_profiles: Vec<String>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub curr_fan_mode: u8,
|
|
||||||
pub bat_charge_limit: u8,
|
|
||||||
pub kbd_led_brightness: u8,
|
|
||||||
pub kbd_backlight_mode: u8,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub kbd_backlight_modes: Option<bool>,
|
|
||||||
pub power_profiles: BTreeMap<String, ProfileV317>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV317 {
|
|
||||||
pub(crate) fn into_current(self) -> Config {
|
|
||||||
Config {
|
|
||||||
gfx_mode: self.gfx_mode,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: self.gfx_managed,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
bat_charge_limit: self.bat_charge_limit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct ConfigV324 {
|
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub active_profile: String,
|
|
||||||
pub toggle_profiles: Vec<String>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub curr_fan_mode: u8,
|
|
||||||
pub bat_charge_limit: u8,
|
|
||||||
pub power_profiles: BTreeMap<String, ProfileV317>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV324 {
|
|
||||||
pub(crate) fn into_current(self) -> Config {
|
|
||||||
Config {
|
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: self.gfx_managed,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
bat_charge_limit: self.bat_charge_limit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct ConfigV341 {
|
|
||||||
pub gfx_mode: GfxVendors,
|
|
||||||
pub gfx_managed: bool,
|
|
||||||
pub gfx_vfio_enable: bool,
|
|
||||||
pub active_profile: String,
|
|
||||||
pub toggle_profiles: Vec<String>,
|
|
||||||
#[serde(skip)]
|
|
||||||
pub curr_fan_mode: u8,
|
|
||||||
pub bat_charge_limit: u8,
|
|
||||||
pub power_profiles: BTreeMap<String, ProfileV317>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConfigV341 {
|
|
||||||
pub(crate) fn into_current(self) -> Config {
|
|
||||||
Config {
|
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: self.gfx_managed,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
bat_charge_limit: self.bat_charge_limit,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct ConfigV352 {
|
pub struct ConfigV352 {
|
||||||
pub gfx_mode: GfxVendors,
|
pub gfx_mode: GfxVendors,
|
||||||
@@ -101,10 +22,6 @@ pub struct ConfigV352 {
|
|||||||
impl ConfigV352 {
|
impl ConfigV352 {
|
||||||
pub(crate) fn into_current(self) -> Config {
|
pub(crate) fn into_current(self) -> Config {
|
||||||
Config {
|
Config {
|
||||||
gfx_mode: GfxVendors::Hybrid,
|
|
||||||
gfx_tmp_mode: None,
|
|
||||||
gfx_managed: self.gfx_managed,
|
|
||||||
gfx_vfio_enable: false,
|
|
||||||
bat_charge_limit: self.bat_charge_limit,
|
bat_charge_limit: self.bat_charge_limit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
257
daemon/src/ctrl_anime/config.rs
Normal file
257
daemon/src/ctrl_anime/config.rs
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
use crate::VERSION;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
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};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
pub static ANIME_CONFIG_PATH: &str = "/etc/asusd/anime.conf";
|
||||||
|
pub static ANIME_CACHE_PATH: &str = "/etc/asusd/anime-cache.conf";
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct AnimeConfigV341 {
|
||||||
|
pub system: Option<ActionLoader>,
|
||||||
|
pub boot: Option<ActionLoader>,
|
||||||
|
pub suspend: Option<ActionLoader>,
|
||||||
|
pub shutdown: Option<ActionLoader>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimeConfigV341 {
|
||||||
|
pub(crate) fn into_current(self) -> AnimeConfig {
|
||||||
|
AnimeConfig {
|
||||||
|
system: if let Some(ani) = self.system {
|
||||||
|
vec![ani]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
boot: if let Some(ani) = self.boot {
|
||||||
|
vec![ani]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
wake: if let Some(ani) = self.suspend {
|
||||||
|
vec![ani]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
shutdown: if let Some(ani) = self.shutdown {
|
||||||
|
vec![ani]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
},
|
||||||
|
brightness: 1.0,
|
||||||
|
awake_enabled: true,
|
||||||
|
boot_anim_enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct AnimeConfigV352 {
|
||||||
|
pub system: Vec<ActionLoader>,
|
||||||
|
pub boot: Vec<ActionLoader>,
|
||||||
|
pub wake: Vec<ActionLoader>,
|
||||||
|
pub shutdown: Vec<ActionLoader>,
|
||||||
|
pub brightness: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimeConfigV352 {
|
||||||
|
pub(crate) fn into_current(self) -> AnimeConfig {
|
||||||
|
AnimeConfig {
|
||||||
|
system: self.system,
|
||||||
|
boot: self.boot,
|
||||||
|
wake: self.wake,
|
||||||
|
shutdown: self.shutdown,
|
||||||
|
brightness: 1.0,
|
||||||
|
awake_enabled: true,
|
||||||
|
boot_anim_enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Default)]
|
||||||
|
pub struct AnimeConfigCached {
|
||||||
|
pub system: Vec<ActionData>,
|
||||||
|
pub boot: Vec<ActionData>,
|
||||||
|
pub wake: Vec<ActionData>,
|
||||||
|
pub shutdown: Vec<ActionData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimeConfigCached {
|
||||||
|
pub fn init_from_config(&mut self, config: &AnimeConfig) -> Result<(), AnimeError> {
|
||||||
|
let mut sys = Vec::with_capacity(config.system.len());
|
||||||
|
for ani in config.system.iter() {
|
||||||
|
sys.push(ActionData::from_anime_action(ani)?);
|
||||||
|
}
|
||||||
|
self.system = sys;
|
||||||
|
|
||||||
|
let mut boot = Vec::with_capacity(config.boot.len());
|
||||||
|
for ani in config.boot.iter() {
|
||||||
|
boot.push(ActionData::from_anime_action(ani)?);
|
||||||
|
}
|
||||||
|
self.boot = boot;
|
||||||
|
|
||||||
|
let mut wake = Vec::with_capacity(config.wake.len());
|
||||||
|
for ani in config.wake.iter() {
|
||||||
|
wake.push(ActionData::from_anime_action(ani)?);
|
||||||
|
}
|
||||||
|
self.wake = wake;
|
||||||
|
|
||||||
|
let mut shutdown = Vec::with_capacity(config.shutdown.len());
|
||||||
|
for ani in config.shutdown.iter() {
|
||||||
|
shutdown.push(ActionData::from_anime_action(ani)?);
|
||||||
|
}
|
||||||
|
self.shutdown = shutdown;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Config for base system actions for the anime display
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct AnimeConfig {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AnimeConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
AnimeConfig {
|
||||||
|
system: Vec::new(),
|
||||||
|
boot: Vec::new(),
|
||||||
|
wake: Vec::new(),
|
||||||
|
shutdown: Vec::new(),
|
||||||
|
brightness: 1.0,
|
||||||
|
awake_enabled: true,
|
||||||
|
boot_anim_enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnimeConfig {
|
||||||
|
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||||
|
pub fn load() -> Self {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&ANIME_CONFIG_PATH)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!(
|
||||||
|
"The file {} or directory /etc/asusd/ is missing",
|
||||||
|
ANIME_CONFIG_PATH
|
||||||
|
)
|
||||||
|
}); // okay to cause panic here
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
|
if read_len == 0 {
|
||||||
|
return AnimeConfig::create_default(&mut file);
|
||||||
|
} else {
|
||||||
|
if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
|
return data;
|
||||||
|
} else if let Ok(data) = serde_json::from_str::<AnimeConfigV341>(&buf) {
|
||||||
|
let config = data.into_current();
|
||||||
|
config.write();
|
||||||
|
info!("Updated config version to: {}", VERSION);
|
||||||
|
return config;
|
||||||
|
} else if let Ok(data) = serde_json::from_str::<AnimeConfigV352>(&buf) {
|
||||||
|
let config = data.into_current();
|
||||||
|
config.write();
|
||||||
|
info!("Updated config version to: {}", VERSION);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
AnimeConfig::write_backup(buf);
|
||||||
|
warn!(
|
||||||
|
"Could not deserialise {}. Backed up as *-old",
|
||||||
|
ANIME_CONFIG_PATH
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimeConfig::create_default(&mut file)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_default(file: &mut File) -> Self {
|
||||||
|
// create a default config here
|
||||||
|
let config = AnimeConfig {
|
||||||
|
system: vec![],
|
||||||
|
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::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(2)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
|
}],
|
||||||
|
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::Fade(Fade::new(
|
||||||
|
Duration::from_secs(2),
|
||||||
|
Some(Duration::from_secs(2)),
|
||||||
|
Duration::from_secs(2),
|
||||||
|
)),
|
||||||
|
}],
|
||||||
|
shutdown: vec![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: 1.0,
|
||||||
|
time: AnimTime::Infinite,
|
||||||
|
}],
|
||||||
|
brightness: 1.0,
|
||||||
|
awake_enabled: true,
|
||||||
|
boot_anim_enabled: true,
|
||||||
|
};
|
||||||
|
// Should be okay to unwrap this as is since it is a Default
|
||||||
|
let json = serde_json::to_string_pretty(&config).unwrap();
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|_| panic!("Could not write {}", ANIME_CONFIG_PATH));
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&ANIME_CONFIG_PATH)
|
||||||
|
.unwrap_or_else(|err| panic!("Error reading {}: {}", ANIME_CONFIG_PATH, err));
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||||
|
if l == 0 {
|
||||||
|
warn!("File is empty {}", ANIME_CONFIG_PATH);
|
||||||
|
} else {
|
||||||
|
let x: AnimeConfig = serde_json::from_str(&buf)
|
||||||
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", ANIME_CONFIG_PATH));
|
||||||
|
*self = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) {
|
||||||
|
let mut file = File::create(ANIME_CONFIG_PATH).expect("Couldn't overwrite config");
|
||||||
|
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
329
daemon/src/ctrl_anime/mod.rs
Normal file
329
daemon/src/ctrl_anime/mod.rs
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
pub mod config;
|
||||||
|
pub mod zbus;
|
||||||
|
|
||||||
|
use ::zbus::Connection;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use logind_zbus::ManagerProxy;
|
||||||
|
use rog_anime::{
|
||||||
|
usb::{
|
||||||
|
pkt_for_apply, pkt_for_flush, pkt_for_set_boot, pkt_for_set_on, pkts_for_init, PROD_ID,
|
||||||
|
VENDOR_ID,
|
||||||
|
},
|
||||||
|
ActionData, AnimeDataBuffer, AnimePacketType, ANIME_DATA_LEN,
|
||||||
|
};
|
||||||
|
use rog_types::supported::AnimeSupportedFunctions;
|
||||||
|
use rusb::{Device, DeviceHandle};
|
||||||
|
use std::{
|
||||||
|
error::Error,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
thread::sleep,
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{error::RogError, GetSupported};
|
||||||
|
|
||||||
|
use self::config::{AnimeConfig, AnimeConfigCached};
|
||||||
|
|
||||||
|
impl GetSupported for CtrlAnime {
|
||||||
|
type A = AnimeSupportedFunctions;
|
||||||
|
|
||||||
|
fn get_supported() -> Self::A {
|
||||||
|
AnimeSupportedFunctions(CtrlAnime::get_device(VENDOR_ID, PROD_ID).is_ok())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnime {
|
||||||
|
handle: DeviceHandle<rusb::GlobalContext>,
|
||||||
|
cache: AnimeConfigCached,
|
||||||
|
config: AnimeConfig,
|
||||||
|
// set to force thread to exit
|
||||||
|
thread_exit: Arc<AtomicBool>,
|
||||||
|
// Set to false when the thread exits
|
||||||
|
thread_running: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlAnime {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(config: AnimeConfig) -> Result<CtrlAnime, Box<dyn Error>> {
|
||||||
|
// We don't expect this ID to ever change
|
||||||
|
let device = CtrlAnime::get_device(0x0b05, 0x193b)?;
|
||||||
|
|
||||||
|
let mut device = device.open()?;
|
||||||
|
device.reset()?;
|
||||||
|
|
||||||
|
device.set_auto_detach_kernel_driver(true).map_err(|err| {
|
||||||
|
error!("Auto-detach kernel driver failed: {}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
|
||||||
|
device.claim_interface(0).map_err(|err| {
|
||||||
|
error!("Could not claim device interface: {}", err);
|
||||||
|
err
|
||||||
|
})?;
|
||||||
|
|
||||||
|
info!("Device has an AniMe Matrix display");
|
||||||
|
let mut cache = AnimeConfigCached::default();
|
||||||
|
cache.init_from_config(&config)?;
|
||||||
|
|
||||||
|
let ctrl = CtrlAnime {
|
||||||
|
handle: device,
|
||||||
|
cache,
|
||||||
|
config,
|
||||||
|
thread_exit: Arc::new(AtomicBool::new(false)),
|
||||||
|
thread_running: Arc::new(AtomicBool::new(false)),
|
||||||
|
};
|
||||||
|
ctrl.do_initialization();
|
||||||
|
|
||||||
|
Ok(ctrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_device(vendor: u16, product: u16) -> Result<Device<rusb::GlobalContext>, rusb::Error> {
|
||||||
|
for device in rusb::devices()?.iter() {
|
||||||
|
let device_desc = device.device_descriptor()?;
|
||||||
|
if device_desc.vendor_id() == vendor && device_desc.product_id() == product {
|
||||||
|
return Ok(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(rusb::Error::NoDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an action thread. This is classed as a singleton and there should be only
|
||||||
|
/// one running - so the thread uses atomics to signal run/exit.
|
||||||
|
///
|
||||||
|
/// Because this also writes to the usb device, other write tries (display only) *must*
|
||||||
|
/// get the mutex lock and set the thread_exit atomic.
|
||||||
|
fn run_thread(inner: Arc<Mutex<CtrlAnime>>, actions: Vec<ActionData>, mut once: bool) {
|
||||||
|
if actions.is_empty() {
|
||||||
|
warn!("AniMe system actions was empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Loop rules:
|
||||||
|
// - 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
|
||||||
|
|
||||||
|
// The only reason for this outer thread is to prevent blocking while waiting for the
|
||||||
|
// next spawned thread to exit
|
||||||
|
std::thread::Builder::new()
|
||||||
|
.name("AniMe system thread start".into())
|
||||||
|
.spawn(move || {
|
||||||
|
info!("AniMe system thread started");
|
||||||
|
// Getting copies of these Atomics is done *in* the thread to ensure
|
||||||
|
// we don't block other threads/main
|
||||||
|
let thread_exit;
|
||||||
|
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 {
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
thread_exit = lock.thread_exit.clone();
|
||||||
|
thread_running = lock.thread_running.clone();
|
||||||
|
// Make any running loop exit first
|
||||||
|
thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// wait for other threads to set not running so we know they exited
|
||||||
|
if !thread_running.load(Ordering::SeqCst) {
|
||||||
|
thread_exit.store(false, Ordering::SeqCst);
|
||||||
|
info!("AniMe forced a thread to exit");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
'main: loop {
|
||||||
|
if thread_exit.load(Ordering::SeqCst) {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
for action in actions.iter() {
|
||||||
|
match action {
|
||||||
|
ActionData::Animation(frames) => {
|
||||||
|
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) => {
|
||||||
|
once = false;
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
lock.write_data_buffer(image.as_ref().clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActionData::Pause(duration) => sleep(*duration),
|
||||||
|
ActionData::AudioEq => {}
|
||||||
|
ActionData::SystemInfo => {}
|
||||||
|
ActionData::TimeDate => {}
|
||||||
|
ActionData::Matrix => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if once || actions.is_empty() {
|
||||||
|
break 'main;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear the display on exit
|
||||||
|
if let Ok(lock) = inner.try_lock() {
|
||||||
|
let data = AnimeDataBuffer::from_vec([0u8; ANIME_DATA_LEN].to_vec());
|
||||||
|
lock.write_data_buffer(data);
|
||||||
|
}
|
||||||
|
// Loop ended, set the atmonics
|
||||||
|
thread_exit.store(false, Ordering::SeqCst);
|
||||||
|
thread_running.store(false, Ordering::SeqCst);
|
||||||
|
info!("AniMe system thread exited");
|
||||||
|
})
|
||||||
|
.map(|err| info!("AniMe system thread: {:?}", err))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes(&self, message: &[u8]) {
|
||||||
|
match self.handle.write_control(
|
||||||
|
0x21, // request_type
|
||||||
|
0x09, // request
|
||||||
|
0x35e, // value
|
||||||
|
0x00, // index
|
||||||
|
message,
|
||||||
|
Duration::from_millis(200),
|
||||||
|
) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => match err {
|
||||||
|
rusb::Error::Timeout => {}
|
||||||
|
_ => error!("Failed to write to led interrupt: {}", err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write only a data packet. This will modify the leds brightness using the
|
||||||
|
/// global brightness set in config.
|
||||||
|
fn write_data_buffer(&self, mut buffer: AnimeDataBuffer) {
|
||||||
|
for led in buffer.get_mut()[7..].iter_mut() {
|
||||||
|
let mut bright = *led as f32 * self.config.brightness;
|
||||||
|
if bright > 254.0 {
|
||||||
|
bright = 254.0;
|
||||||
|
}
|
||||||
|
*led = bright as u8;
|
||||||
|
}
|
||||||
|
let data = AnimePacketType::from(buffer);
|
||||||
|
for row in data.iter() {
|
||||||
|
self.write_bytes(row);
|
||||||
|
}
|
||||||
|
self.write_bytes(&pkt_for_flush());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_initialization(&self) {
|
||||||
|
let pkts = pkts_for_init();
|
||||||
|
self.write_bytes(&pkts[0]);
|
||||||
|
self.write_bytes(&pkts[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnimeTask<'a> {
|
||||||
|
inner: Arc<Mutex<CtrlAnime>>,
|
||||||
|
_c: Connection,
|
||||||
|
manager: ManagerProxy<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CtrlAnimeTask<'a> {
|
||||||
|
pub fn new(inner: Arc<Mutex<CtrlAnime>>) -> Self {
|
||||||
|
let connection = Connection::new_system().unwrap();
|
||||||
|
|
||||||
|
let manager = ManagerProxy::new(&connection).unwrap();
|
||||||
|
|
||||||
|
let c1 = inner.clone();
|
||||||
|
// Run this action when the system starts shutting down
|
||||||
|
manager
|
||||||
|
.connect_prepare_for_shutdown(move |shutdown| {
|
||||||
|
if shutdown {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = c1.try_lock() {
|
||||||
|
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
CtrlAnime::run_thread(c1.clone(), lock.cache.shutdown.clone(), false);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlAnimeTask: new() {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
let c1 = inner.clone();
|
||||||
|
// Run this action when the system wakes up from sleep
|
||||||
|
manager
|
||||||
|
.connect_prepare_for_sleep(move |sleep| {
|
||||||
|
if !sleep {
|
||||||
|
// wait a fraction for things to wake up properly
|
||||||
|
std::thread::sleep(Duration::from_millis(100));
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = c1.try_lock() {
|
||||||
|
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
CtrlAnime::run_thread(c1.clone(), lock.cache.wake.clone(), true);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlAnimeTask: new() {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
_c: connection,
|
||||||
|
manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> crate::CtrlTask for CtrlAnimeTask<'a> {
|
||||||
|
fn do_task(&self) -> Result<(), RogError> {
|
||||||
|
if let Ok(mut lock) = self.inner.try_lock() {
|
||||||
|
// Refresh the config and cache incase the user has edited it
|
||||||
|
let config = AnimeConfig::load();
|
||||||
|
lock.cache
|
||||||
|
.init_from_config(&config)
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlAnimeTask: do_task {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for signals on each task iteration, this will run the callbacks
|
||||||
|
// if any signal is recieved
|
||||||
|
self.manager.next_signal()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlAnimeReloader(pub Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
impl crate::Reloadable for CtrlAnimeReloader {
|
||||||
|
fn reload(&mut self) -> Result<(), RogError> {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.write_bytes(&pkt_for_set_on(lock.config.awake_enabled));
|
||||||
|
lock.write_bytes(&pkt_for_apply());
|
||||||
|
lock.write_bytes(&pkt_for_set_boot(lock.config.boot_anim_enabled));
|
||||||
|
lock.write_bytes(&pkt_for_apply());
|
||||||
|
|
||||||
|
let action = lock.cache.boot.clone();
|
||||||
|
CtrlAnime::run_thread(self.0.clone(), action, true);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
140
daemon/src/ctrl_anime/zbus.rs
Normal file
140
daemon/src/ctrl_anime/zbus.rs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use log::warn;
|
||||||
|
use rog_anime::{
|
||||||
|
usb::{pkt_for_apply, pkt_for_set_boot, pkt_for_set_on},
|
||||||
|
AnimeDataBuffer, AnimePowerStates,
|
||||||
|
};
|
||||||
|
use zbus::dbus_interface;
|
||||||
|
use zvariant::ObjectPath;
|
||||||
|
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
use super::CtrlAnime;
|
||||||
|
|
||||||
|
pub struct CtrlAnimeZbus(pub Arc<Mutex<CtrlAnime>>);
|
||||||
|
|
||||||
|
/// The struct with the main dbus methods requires this trait
|
||||||
|
impl crate::ZbusAdd for CtrlAnimeZbus {
|
||||||
|
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||||
|
server
|
||||||
|
.at(
|
||||||
|
&ObjectPath::from_str_unchecked("/org/asuslinux/Anime"),
|
||||||
|
self,
|
||||||
|
)
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlAnimeDisplay: add_to_server {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of these calls can be guarnateed to succeed unless we loop until okay
|
||||||
|
// If the try_lock *does* succeed then any other thread trying to lock will not grab it
|
||||||
|
// until we finish.
|
||||||
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
|
impl CtrlAnimeZbus {
|
||||||
|
/// Writes a data stream of length. Will force system thread to exit until it is restarted
|
||||||
|
fn write(&self, input: AnimeDataBuffer) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
lock.write_data_buffer(input);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the global AniMe brightness
|
||||||
|
fn set_brightness(&self, bright: f32) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
|
let mut bright = bright;
|
||||||
|
if bright < 0.0 {
|
||||||
|
bright = 0.0
|
||||||
|
} else if bright > 254.0 {
|
||||||
|
bright = 254.0;
|
||||||
|
}
|
||||||
|
lock.config.brightness = bright;
|
||||||
|
lock.config.write();
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set whether the AniMe is displaying images/data
|
||||||
|
fn set_on_off(&self, status: bool) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
|
lock.write_bytes(&pkt_for_set_on(status));
|
||||||
|
lock.config.awake_enabled = status;
|
||||||
|
lock.config.write();
|
||||||
|
|
||||||
|
let states = AnimePowerStates {
|
||||||
|
enabled: lock.config.awake_enabled,
|
||||||
|
boot_anim_enabled: lock.config.boot_anim_enabled,
|
||||||
|
};
|
||||||
|
self.notify_power_states(&states)
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set whether the AniMe will show boot, suspend, or off animations
|
||||||
|
fn set_boot_on_off(&self, on: bool) {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(mut lock) = self.0.try_lock() {
|
||||||
|
lock.write_bytes(&pkt_for_set_boot(on));
|
||||||
|
lock.write_bytes(&pkt_for_apply());
|
||||||
|
lock.config.boot_anim_enabled = on;
|
||||||
|
lock.config.write();
|
||||||
|
|
||||||
|
let states = AnimePowerStates {
|
||||||
|
enabled: lock.config.awake_enabled,
|
||||||
|
boot_anim_enabled: lock.config.boot_anim_enabled,
|
||||||
|
};
|
||||||
|
self.notify_power_states(&states)
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main loop is the base system set action if the user isn't running
|
||||||
|
/// the user daemon
|
||||||
|
fn run_main_loop(&self, start: bool) {
|
||||||
|
if start {
|
||||||
|
'outer: loop {
|
||||||
|
if let Ok(lock) = self.0.try_lock() {
|
||||||
|
lock.thread_exit.store(true, Ordering::SeqCst);
|
||||||
|
CtrlAnime::run_thread(self.0.clone(), lock.cache.system.clone(), false);
|
||||||
|
break 'outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get status of if the AniMe LEDs are on
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn awake_enabled(&self) -> bool {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return ctrl.config.awake_enabled;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the status of if factory system-status animations are enabled
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn boot_enabled(&self) -> bool {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return ctrl.config.boot_anim_enabled;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Notify listeners of the status of AniMe LED power and factory system-status animations
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
fn notify_power_states(&self, data: &AnimePowerStates) -> zbus::Result<()>;
|
||||||
|
}
|
||||||
267
daemon/src/ctrl_aura/config.rs
Normal file
267
daemon/src/ctrl_aura/config.rs
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
use crate::laptops::LaptopLedData;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use rog_aura::{AuraEffect, AuraModeNum, AuraZone, LedBrightness};
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/aura.conf";
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct AuraConfigV320 {
|
||||||
|
pub brightness: u32,
|
||||||
|
pub current_mode: AuraModeNum,
|
||||||
|
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
||||||
|
pub multizone: Option<AuraMultiZone>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraConfigV320 {
|
||||||
|
pub(crate) fn into_current(self) -> AuraConfig {
|
||||||
|
AuraConfig {
|
||||||
|
brightness: <LedBrightness>::from(self.brightness),
|
||||||
|
current_mode: self.current_mode,
|
||||||
|
builtins: self.builtins,
|
||||||
|
multizone: self.multizone,
|
||||||
|
awake_enabled: true,
|
||||||
|
sleep_anim_enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct AuraConfigV352 {
|
||||||
|
pub brightness: LedBrightness,
|
||||||
|
pub current_mode: AuraModeNum,
|
||||||
|
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
||||||
|
pub multizone: Option<AuraMultiZone>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraConfigV352 {
|
||||||
|
pub(crate) fn into_current(self) -> AuraConfig {
|
||||||
|
AuraConfig {
|
||||||
|
brightness: self.brightness,
|
||||||
|
current_mode: self.current_mode,
|
||||||
|
builtins: self.builtins,
|
||||||
|
multizone: self.multizone,
|
||||||
|
awake_enabled: true,
|
||||||
|
sleep_anim_enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct AuraConfig {
|
||||||
|
pub brightness: LedBrightness,
|
||||||
|
pub current_mode: AuraModeNum,
|
||||||
|
pub builtins: BTreeMap<AuraModeNum, AuraEffect>,
|
||||||
|
pub multizone: Option<AuraMultiZone>,
|
||||||
|
pub awake_enabled: bool,
|
||||||
|
pub sleep_anim_enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AuraConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
AuraConfig {
|
||||||
|
brightness: LedBrightness::Med,
|
||||||
|
current_mode: AuraModeNum::Static,
|
||||||
|
builtins: BTreeMap::new(),
|
||||||
|
multizone: None,
|
||||||
|
awake_enabled: true,
|
||||||
|
sleep_anim_enabled: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraConfig {
|
||||||
|
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||||
|
pub fn load(supported_led_modes: &LaptopLedData) -> Self {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&AURA_CONFIG_PATH)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!(
|
||||||
|
"The file {} or directory /etc/asusd/ is missing",
|
||||||
|
AURA_CONFIG_PATH
|
||||||
|
)
|
||||||
|
}); // okay to cause panic here
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
|
if read_len == 0 {
|
||||||
|
return AuraConfig::create_default(&mut file, supported_led_modes);
|
||||||
|
} else {
|
||||||
|
if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
|
return data;
|
||||||
|
} else if let Ok(data) = serde_json::from_str::<AuraConfigV320>(&buf) {
|
||||||
|
let config = data.into_current();
|
||||||
|
config.write();
|
||||||
|
info!("Updated AuraConfig version");
|
||||||
|
return config;
|
||||||
|
} else if let Ok(data) = serde_json::from_str::<AuraConfigV352>(&buf) {
|
||||||
|
let config = data.into_current();
|
||||||
|
config.write();
|
||||||
|
info!("Updated AuraConfig version");
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
warn!("Could not deserialise {}", AURA_CONFIG_PATH);
|
||||||
|
panic!("Please remove {} then restart asusd", AURA_CONFIG_PATH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AuraConfig::create_default(&mut file, supported_led_modes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_default(file: &mut File, support_data: &LaptopLedData) -> Self {
|
||||||
|
// create a default config here
|
||||||
|
let mut config = AuraConfig::default();
|
||||||
|
|
||||||
|
for n in &support_data.standard {
|
||||||
|
config
|
||||||
|
.builtins
|
||||||
|
.insert(*n, AuraEffect::default_with_mode(*n));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should be okay to unwrap this as is since it is a Default
|
||||||
|
let json = serde_json::to_string(&config).unwrap();
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|_| panic!("Could not write {}", AURA_CONFIG_PATH));
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&AURA_CONFIG_PATH)
|
||||||
|
.unwrap_or_else(|err| panic!("Error reading {}: {}", AURA_CONFIG_PATH, err));
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||||
|
if l == 0 {
|
||||||
|
warn!("File is empty {}", AURA_CONFIG_PATH);
|
||||||
|
} else {
|
||||||
|
let x: AuraConfig = serde_json::from_str(&buf)
|
||||||
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", AURA_CONFIG_PATH));
|
||||||
|
*self = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) {
|
||||||
|
let mut file = File::create(AURA_CONFIG_PATH).expect("Couldn't overwrite config");
|
||||||
|
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multipurpose, will accept AuraEffect with zones and put in the correct store
|
||||||
|
pub fn set_builtin(&mut self, effect: AuraEffect) {
|
||||||
|
match effect.zone() {
|
||||||
|
AuraZone::None => {
|
||||||
|
self.builtins.insert(*effect.mode(), effect);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(multi) = self.multizone.as_mut() {
|
||||||
|
multi.set(effect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_multizone(&self, aura_type: AuraModeNum) -> Option<&[AuraEffect; 4]> {
|
||||||
|
if let Some(multi) = &self.multizone {
|
||||||
|
if aura_type == AuraModeNum::Static {
|
||||||
|
return Some(multi.static_());
|
||||||
|
} else if aura_type == AuraModeNum::Breathe {
|
||||||
|
return Some(multi.breathe());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct AuraMultiZone {
|
||||||
|
static_: [AuraEffect; 4],
|
||||||
|
breathe: [AuraEffect; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuraMultiZone {
|
||||||
|
pub fn set(&mut self, effect: AuraEffect) {
|
||||||
|
if effect.mode == AuraModeNum::Static {
|
||||||
|
match effect.zone {
|
||||||
|
AuraZone::None => {}
|
||||||
|
AuraZone::One => self.static_[0] = effect,
|
||||||
|
AuraZone::Two => self.static_[1] = effect,
|
||||||
|
AuraZone::Three => self.static_[2] = effect,
|
||||||
|
AuraZone::Four => self.static_[3] = effect,
|
||||||
|
}
|
||||||
|
} else if effect.mode == AuraModeNum::Breathe {
|
||||||
|
match effect.zone {
|
||||||
|
AuraZone::None => {}
|
||||||
|
AuraZone::One => self.breathe[0] = effect,
|
||||||
|
AuraZone::Two => self.breathe[1] = effect,
|
||||||
|
AuraZone::Three => self.breathe[2] = effect,
|
||||||
|
AuraZone::Four => self.breathe[3] = effect,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn static_(&self) -> &[AuraEffect; 4] {
|
||||||
|
&self.static_
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn breathe(&self) -> &[AuraEffect; 4] {
|
||||||
|
&self.breathe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AuraMultiZone {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
static_: [
|
||||||
|
AuraEffect {
|
||||||
|
mode: AuraModeNum::Static,
|
||||||
|
zone: AuraZone::One,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AuraEffect {
|
||||||
|
mode: AuraModeNum::Static,
|
||||||
|
zone: AuraZone::Two,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AuraEffect {
|
||||||
|
mode: AuraModeNum::Static,
|
||||||
|
zone: AuraZone::Three,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AuraEffect {
|
||||||
|
mode: AuraModeNum::Static,
|
||||||
|
zone: AuraZone::Four,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
breathe: [
|
||||||
|
AuraEffect {
|
||||||
|
mode: AuraModeNum::Breathe,
|
||||||
|
zone: AuraZone::One,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AuraEffect {
|
||||||
|
mode: AuraModeNum::Breathe,
|
||||||
|
zone: AuraZone::Two,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AuraEffect {
|
||||||
|
mode: AuraModeNum::Breathe,
|
||||||
|
zone: AuraZone::Three,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
AuraEffect {
|
||||||
|
mode: AuraModeNum::Breathe,
|
||||||
|
zone: AuraZone::Four,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
395
daemon/src/ctrl_aura/controller.rs
Normal file
395
daemon/src/ctrl_aura/controller.rs
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
// Only these two packets must be 17 bytes
|
||||||
|
static KBD_BRIGHT_PATH: &str = "/sys/class/leds/asus::kbd_backlight/brightness";
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::RogError,
|
||||||
|
laptops::{LaptopLedData, ASUS_KEYBOARD_DEVICES},
|
||||||
|
CtrlTask,
|
||||||
|
};
|
||||||
|
use log::{info, warn};
|
||||||
|
use logind_zbus::ManagerProxy;
|
||||||
|
use rog_aura::{
|
||||||
|
usb::{
|
||||||
|
LED_APPLY, LED_AWAKE_OFF_SLEEP_OFF, LED_AWAKE_OFF_SLEEP_ON, LED_AWAKE_ON_SLEEP_OFF,
|
||||||
|
LED_AWAKE_ON_SLEEP_ON, LED_SET,
|
||||||
|
},
|
||||||
|
AuraEffect, LedBrightness, LED_MSG_LEN,
|
||||||
|
};
|
||||||
|
use rog_types::supported::LedSupportedFunctions;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::{fs::OpenOptions, thread::spawn};
|
||||||
|
use zbus::Connection;
|
||||||
|
|
||||||
|
use crate::GetSupported;
|
||||||
|
|
||||||
|
use super::config::AuraConfig;
|
||||||
|
|
||||||
|
impl GetSupported for CtrlKbdLed {
|
||||||
|
type A = LedSupportedFunctions;
|
||||||
|
|
||||||
|
fn get_supported() -> Self::A {
|
||||||
|
// let mode = <&str>::from(&<AuraModes>::from(*mode));
|
||||||
|
let multizone_led_mode = false;
|
||||||
|
let per_key_led_mode = false;
|
||||||
|
let laptop = LaptopLedData::get_data();
|
||||||
|
let stock_led_modes = laptop.standard;
|
||||||
|
|
||||||
|
LedSupportedFunctions {
|
||||||
|
brightness_set: CtrlKbdLed::get_kbd_bright_path().is_some(),
|
||||||
|
stock_led_modes,
|
||||||
|
multizone_led_mode,
|
||||||
|
per_key_led_mode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlKbdLed {
|
||||||
|
pub led_node: Option<String>,
|
||||||
|
pub bright_node: String,
|
||||||
|
pub supported_modes: LaptopLedData,
|
||||||
|
pub flip_effect_write: bool,
|
||||||
|
pub config: AuraConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlKbdLedTask<'a> {
|
||||||
|
inner: Arc<Mutex<CtrlKbdLed>>,
|
||||||
|
_c: Connection,
|
||||||
|
manager: ManagerProxy<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CtrlKbdLedTask<'a> {
|
||||||
|
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
|
||||||
|
let connection = Connection::new_system().unwrap();
|
||||||
|
|
||||||
|
let manager = ManagerProxy::new(&connection).unwrap();
|
||||||
|
|
||||||
|
let c1 = inner.clone();
|
||||||
|
// Run this action when the system wakes up from sleep
|
||||||
|
manager
|
||||||
|
.connect_prepare_for_sleep(move |sleep| {
|
||||||
|
if !sleep {
|
||||||
|
let c1 = c1.clone();
|
||||||
|
spawn(move || {
|
||||||
|
// wait a fraction for things to wake up properly
|
||||||
|
//std::thread::sleep(Duration::from_millis(100));
|
||||||
|
loop {
|
||||||
|
if let Ok(ref mut lock) = c1.try_lock() {
|
||||||
|
lock.set_brightness(lock.config.brightness).ok();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("CtrlAnimeTask: new() {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
inner,
|
||||||
|
_c: connection,
|
||||||
|
manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_config(lock: &mut CtrlKbdLed) -> Result<(), RogError> {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&lock.bright_node)
|
||||||
|
.map_err(|err| match err.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
RogError::MissingLedBrightNode((&lock.bright_node).into(), err)
|
||||||
|
}
|
||||||
|
_ => RogError::Path((&lock.bright_node).into(), err),
|
||||||
|
})?;
|
||||||
|
let mut buf = [0u8; 1];
|
||||||
|
file.read_exact(&mut buf)
|
||||||
|
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||||
|
if let Some(num) = char::from(buf[0]).to_digit(10) {
|
||||||
|
if lock.config.brightness != num.into() {
|
||||||
|
lock.config.read();
|
||||||
|
lock.config.brightness = num.into();
|
||||||
|
lock.config.write();
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(RogError::ParseLed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CtrlTask for CtrlKbdLedTask<'a> {
|
||||||
|
fn do_task(&self) -> Result<(), RogError> {
|
||||||
|
self.manager.next_signal()?;
|
||||||
|
if let Ok(ref mut lock) = self.inner.try_lock() {
|
||||||
|
return Self::update_config(lock);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlKbdLedReloader(pub Arc<Mutex<CtrlKbdLed>>);
|
||||||
|
|
||||||
|
impl crate::Reloadable for CtrlKbdLedReloader {
|
||||||
|
fn reload(&mut self) -> Result<(), RogError> {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
let current = ctrl.config.current_mode;
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(¤t).cloned() {
|
||||||
|
ctrl.do_command(mode).ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl.set_states_enabled(ctrl.config.awake_enabled, ctrl.config.sleep_anim_enabled)
|
||||||
|
.map_err(|err| warn!("{}", err))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CtrlKbdLedZbus(pub Arc<Mutex<CtrlKbdLed>>);
|
||||||
|
|
||||||
|
impl CtrlKbdLedZbus {
|
||||||
|
pub fn new(inner: Arc<Mutex<CtrlKbdLed>>) -> Self {
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlKbdLed {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(supported_modes: LaptopLedData, config: AuraConfig) -> Result<Self, RogError> {
|
||||||
|
// TODO: return error if *all* nodes are None
|
||||||
|
let mut led_node = None;
|
||||||
|
for prod in ASUS_KEYBOARD_DEVICES.iter() {
|
||||||
|
match Self::find_led_node(prod) {
|
||||||
|
Ok(node) => {
|
||||||
|
led_node = Some(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(err) => warn!("led_node: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let bright_node = Self::get_kbd_bright_path();
|
||||||
|
|
||||||
|
if led_node.is_none() && bright_node.is_none() {
|
||||||
|
return Err(RogError::MissingFunction(
|
||||||
|
"All keyboard features missing, you may require a v5.11 series kernel or newer"
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if bright_node.is_none() {
|
||||||
|
return Err(RogError::MissingFunction(
|
||||||
|
"No brightness control, you may require a v5.11 series kernel or newer".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ctrl = CtrlKbdLed {
|
||||||
|
led_node,
|
||||||
|
bright_node: bright_node.unwrap(), // If was none then we already returned above
|
||||||
|
supported_modes,
|
||||||
|
flip_effect_write: false,
|
||||||
|
config,
|
||||||
|
};
|
||||||
|
Ok(ctrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_kbd_bright_path() -> Option<String> {
|
||||||
|
if Path::new(KBD_BRIGHT_PATH).exists() {
|
||||||
|
return Some(KBD_BRIGHT_PATH.to_string());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_brightness(&self) -> Result<u8, RogError> {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&self.bright_node)
|
||||||
|
.map_err(|err| match err.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
|
||||||
|
}
|
||||||
|
_ => RogError::Path((&self.bright_node).into(), err),
|
||||||
|
})?;
|
||||||
|
let mut buf = [0u8; 1];
|
||||||
|
file.read_exact(&mut buf)
|
||||||
|
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||||
|
Ok(buf[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_brightness(&self, brightness: LedBrightness) -> Result<(), RogError> {
|
||||||
|
let path = Path::new(&self.bright_node);
|
||||||
|
let mut file =
|
||||||
|
OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.open(&path)
|
||||||
|
.map_err(|err| match err.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => {
|
||||||
|
RogError::MissingLedBrightNode((&self.bright_node).into(), err)
|
||||||
|
}
|
||||||
|
_ => RogError::Path((&self.bright_node).into(), err),
|
||||||
|
})?;
|
||||||
|
file.write_all(&[brightness.as_char_code()])
|
||||||
|
.map_err(|err| RogError::Read("buffer".into(), err))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set if awake/on LED active, and/or sleep animation active
|
||||||
|
pub(super) fn set_states_enabled(&self, awake: bool, sleep: bool) -> Result<(), RogError> {
|
||||||
|
let bytes = if awake && sleep {
|
||||||
|
LED_AWAKE_ON_SLEEP_ON
|
||||||
|
} else if awake && !sleep {
|
||||||
|
LED_AWAKE_ON_SLEEP_OFF
|
||||||
|
} else if !awake && sleep {
|
||||||
|
LED_AWAKE_OFF_SLEEP_ON
|
||||||
|
} else if !awake && !sleep {
|
||||||
|
LED_AWAKE_OFF_SLEEP_OFF
|
||||||
|
} else {
|
||||||
|
LED_AWAKE_ON_SLEEP_ON
|
||||||
|
};
|
||||||
|
self.write_bytes(&bytes)?;
|
||||||
|
self.write_bytes(&LED_SET)?;
|
||||||
|
// Changes won't persist unless apply is set
|
||||||
|
self.write_bytes(&LED_APPLY)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_led_node(id_product: &str) -> Result<String, RogError> {
|
||||||
|
let mut enumerator = udev::Enumerator::new().map_err(|err| {
|
||||||
|
warn!("{}", err);
|
||||||
|
RogError::Udev("enumerator failed".into(), err)
|
||||||
|
})?;
|
||||||
|
enumerator.match_subsystem("hidraw").map_err(|err| {
|
||||||
|
warn!("{}", err);
|
||||||
|
RogError::Udev("match_subsystem failed".into(), err)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for device in enumerator.scan_devices().map_err(|err| {
|
||||||
|
warn!("{}", err);
|
||||||
|
RogError::Udev("scan_devices failed".into(), err)
|
||||||
|
})? {
|
||||||
|
if let Some(parent) = device
|
||||||
|
.parent_with_subsystem_devtype("usb", "usb_device")
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("{}", err);
|
||||||
|
RogError::Udev("parent_with_subsystem_devtype failed".into(), err)
|
||||||
|
})?
|
||||||
|
{
|
||||||
|
if parent
|
||||||
|
.attribute_value("idProduct")
|
||||||
|
.ok_or_else(|| RogError::NotFound("LED idProduct".into()))?
|
||||||
|
== id_product
|
||||||
|
{
|
||||||
|
if let Some(dev_node) = device.devnode() {
|
||||||
|
info!("Using device at: {:?} for LED control", dev_node);
|
||||||
|
return Ok(dev_node.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(RogError::MissingFunction(
|
||||||
|
"ASUS LED device node not found".into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn do_command(&mut self, mode: AuraEffect) -> Result<(), RogError> {
|
||||||
|
self.set_and_save(mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Should only be used if the bytes you are writing are verified correct
|
||||||
|
#[inline]
|
||||||
|
fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||||
|
if let Some(led_node) = &self.led_node {
|
||||||
|
if let Ok(mut file) = OpenOptions::new().write(true).open(led_node) {
|
||||||
|
// println!("write: {:02x?}", &message);
|
||||||
|
return file
|
||||||
|
.write_all(message)
|
||||||
|
.map_err(|err| RogError::Write("write_bytes".into(), err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(RogError::NotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write an effect block
|
||||||
|
#[inline]
|
||||||
|
fn _write_effect(&mut self, effect: &[Vec<u8>]) -> Result<(), RogError> {
|
||||||
|
if self.flip_effect_write {
|
||||||
|
for row in effect.iter().rev() {
|
||||||
|
self.write_bytes(row)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for row in effect.iter() {
|
||||||
|
self.write_bytes(row)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.flip_effect_write = !self.flip_effect_write;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used to set a builtin mode and save the settings for it
|
||||||
|
///
|
||||||
|
/// This needs to be universal so that settings applied by dbus stick
|
||||||
|
#[inline]
|
||||||
|
fn set_and_save(&mut self, mode: AuraEffect) -> Result<(), RogError> {
|
||||||
|
self.config.read();
|
||||||
|
self.write_mode(&mode)?;
|
||||||
|
self.config.current_mode = *mode.mode();
|
||||||
|
self.config.set_builtin(mode);
|
||||||
|
self.config.write();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(super) fn toggle_mode(&mut self, reverse: bool) -> Result<(), RogError> {
|
||||||
|
let current = self.config.current_mode;
|
||||||
|
if let Some(idx) = self
|
||||||
|
.supported_modes
|
||||||
|
.standard
|
||||||
|
.iter()
|
||||||
|
.position(|v| *v == current)
|
||||||
|
{
|
||||||
|
let mut idx = idx;
|
||||||
|
// goes past end of array
|
||||||
|
if reverse {
|
||||||
|
if idx == 0 {
|
||||||
|
idx = self.supported_modes.standard.len() - 1;
|
||||||
|
} else {
|
||||||
|
idx -= 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
idx += 1;
|
||||||
|
if idx == self.supported_modes.standard.len() {
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let next = self.supported_modes.standard[idx];
|
||||||
|
|
||||||
|
self.config.read();
|
||||||
|
if let Some(data) = self.config.builtins.get(&next) {
|
||||||
|
self.write_mode(data)?;
|
||||||
|
self.config.current_mode = next;
|
||||||
|
}
|
||||||
|
self.config.write();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn write_mode(&self, mode: &AuraEffect) -> Result<(), RogError> {
|
||||||
|
if !self.supported_modes.standard.contains(mode.mode()) {
|
||||||
|
return Err(RogError::NotSupported);
|
||||||
|
}
|
||||||
|
let bytes: [u8; LED_MSG_LEN] = mode.into();
|
||||||
|
self.write_bytes(&bytes)?;
|
||||||
|
self.write_bytes(&LED_SET)?;
|
||||||
|
// Changes won't persist unless apply is set
|
||||||
|
self.write_bytes(&LED_APPLY)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
3
daemon/src/ctrl_aura/mod.rs
Normal file
3
daemon/src/ctrl_aura/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod config;
|
||||||
|
pub mod controller;
|
||||||
|
pub mod zbus;
|
||||||
165
daemon/src/ctrl_aura/zbus.rs
Normal file
165
daemon/src/ctrl_aura/zbus.rs
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
use log::{error, warn};
|
||||||
|
use rog_aura::{AuraEffect, LedBrightness, LedPowerStates};
|
||||||
|
use zbus::dbus_interface;
|
||||||
|
use zvariant::ObjectPath;
|
||||||
|
|
||||||
|
use super::controller::CtrlKbdLedZbus;
|
||||||
|
|
||||||
|
impl crate::ZbusAdd for CtrlKbdLedZbus {
|
||||||
|
fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||||
|
server
|
||||||
|
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Led"), self)
|
||||||
|
.map_err(|err| {
|
||||||
|
error!("DbusKbdLed: add_to_server {}", err);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The main interface for changing, reading, or notfying signals
|
||||||
|
///
|
||||||
|
/// LED commands are split between Brightness, Modes, Per-Key
|
||||||
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
|
impl CtrlKbdLedZbus {
|
||||||
|
/// Set the keyboard brightness level (0-3)
|
||||||
|
fn set_brightness(&mut self, brightness: LedBrightness) {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.set_brightness(brightness)
|
||||||
|
.map_err(|err| warn!("{}", err))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the keyboard LED to enabled while the device is awake
|
||||||
|
fn set_awake_enabled(&mut self, enabled: bool) {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.set_states_enabled(enabled, ctrl.config.sleep_anim_enabled)
|
||||||
|
.map_err(|err| warn!("{}", err))
|
||||||
|
.ok();
|
||||||
|
ctrl.config.awake_enabled = enabled;
|
||||||
|
ctrl.config.write();
|
||||||
|
|
||||||
|
let states = LedPowerStates {
|
||||||
|
enabled: ctrl.config.awake_enabled,
|
||||||
|
sleep_anim_enabled: ctrl.config.sleep_anim_enabled,
|
||||||
|
};
|
||||||
|
self.notify_power_states(&states)
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the keyboard LED suspend animation to enabled while the device is suspended
|
||||||
|
fn set_sleep_enabled(&mut self, enabled: bool) {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.set_states_enabled(ctrl.config.awake_enabled, enabled)
|
||||||
|
.map_err(|err| warn!("{}", err))
|
||||||
|
.ok();
|
||||||
|
ctrl.config.sleep_anim_enabled = enabled;
|
||||||
|
ctrl.config.write();
|
||||||
|
let states = LedPowerStates {
|
||||||
|
enabled: ctrl.config.awake_enabled,
|
||||||
|
sleep_anim_enabled: ctrl.config.sleep_anim_enabled,
|
||||||
|
};
|
||||||
|
self.notify_power_states(&states)
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_led_mode(&mut self, effect: AuraEffect) {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
match ctrl.do_command(effect) {
|
||||||
|
Ok(_) => {
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
|
self.notify_led(mode.clone())
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!("{}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_led_mode(&self) {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.toggle_mode(false)
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
|
self.notify_led(mode.clone())
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev_led_mode(&self) {
|
||||||
|
if let Ok(mut ctrl) = self.0.try_lock() {
|
||||||
|
ctrl.toggle_mode(true)
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
|
self.notify_led(mode.clone())
|
||||||
|
.unwrap_or_else(|err| warn!("{}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn awake_enabled(&self) -> bool {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return ctrl.config.awake_enabled;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn sleep_enabled(&self) -> bool {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return ctrl.config.sleep_anim_enabled;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the current mode data
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn led_mode(&self) -> String {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
if let Some(mode) = ctrl.config.builtins.get(&ctrl.config.current_mode) {
|
||||||
|
if let Ok(json) = serde_json::to_string(&mode) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warn!("SetKeyBacklight could not deserialise");
|
||||||
|
"SetKeyBacklight could not deserialise".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a list of available modes
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn led_modes(&self) -> String {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
if let Ok(json) = serde_json::to_string(&ctrl.config.builtins) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warn!("SetKeyBacklight could not deserialise");
|
||||||
|
"SetKeyBacklight could not serialise".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the current LED brightness
|
||||||
|
#[dbus_interface(property)]
|
||||||
|
fn led_brightness(&self) -> i8 {
|
||||||
|
if let Ok(ctrl) = self.0.try_lock() {
|
||||||
|
return ctrl.get_brightness().map(|n| n as i8).unwrap_or(-1);
|
||||||
|
}
|
||||||
|
warn!("SetKeyBacklight could not serialise");
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
fn notify_led(&self, data: AuraEffect) -> zbus::Result<()>;
|
||||||
|
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
fn notify_power_states(&self, data: &LedPowerStates) -> zbus::Result<()>;
|
||||||
|
}
|
||||||
92
daemon/src/ctrl_profiles/config.rs
Normal file
92
daemon/src/ctrl_profiles/config.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
use log::{error, warn};
|
||||||
|
use rog_profiles::error::ProfileError;
|
||||||
|
use rog_profiles::{FanCurves, Profile};
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
pub struct ProfileConfig {
|
||||||
|
#[serde(skip)]
|
||||||
|
config_path: String,
|
||||||
|
/// For restore on boot
|
||||||
|
pub active: Profile,
|
||||||
|
/// States to restore
|
||||||
|
pub fan_curves: Option<FanCurves>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProfileConfig {
|
||||||
|
fn new(config_path: String) -> Result<Self, ProfileError> {
|
||||||
|
let mut platform = ProfileConfig {
|
||||||
|
config_path,
|
||||||
|
active: Profile::Balanced,
|
||||||
|
fan_curves: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if !Profile::is_platform_profile_supported() {
|
||||||
|
return Err(ProfileError::NotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
if FanCurves::is_fan_curves_supported() {
|
||||||
|
let mut curves = FanCurves::default();
|
||||||
|
curves.update_from_platform();
|
||||||
|
platform.fan_curves = Some(curves);
|
||||||
|
}
|
||||||
|
Ok(platform)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProfileConfig {
|
||||||
|
pub fn load(config_path: String) -> Self {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&config_path)
|
||||||
|
.unwrap_or_else(|_| panic!("The directory /etc/asusd/ is missing")); // okay to cause panic here
|
||||||
|
let mut buf = String::new();
|
||||||
|
let mut config;
|
||||||
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
|
if read_len == 0 {
|
||||||
|
config = Self::new(config_path).unwrap();
|
||||||
|
} else if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
|
config = data;
|
||||||
|
config.config_path = config_path;
|
||||||
|
} else {
|
||||||
|
warn!("Could not deserialise {}", config_path);
|
||||||
|
panic!("Please remove {} then restart service", config_path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config = Self::new(config_path).unwrap()
|
||||||
|
}
|
||||||
|
config.write();
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&self.config_path)
|
||||||
|
.unwrap_or_else(|err| panic!("Error reading {}: {}", self.config_path, err));
|
||||||
|
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||||
|
if l == 0 {
|
||||||
|
warn!("File is empty {}", self.config_path);
|
||||||
|
} else {
|
||||||
|
let mut data: ProfileConfig = serde_json::from_str(&buf)
|
||||||
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", self.config_path));
|
||||||
|
// copy over serde skipped values
|
||||||
|
data.config_path = self.config_path.clone();
|
||||||
|
*self = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) {
|
||||||
|
let mut file = File::create(&self.config_path).expect("Couldn't overwrite config");
|
||||||
|
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,18 +10,20 @@ use daemon::ctrl_profiles::controller::CtrlPlatformTask;
|
|||||||
use daemon::{
|
use daemon::{
|
||||||
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
config::Config, ctrl_supported::SupportedFunctions, laptops::print_board_info, GetSupported,
|
||||||
};
|
};
|
||||||
use daemon::{ctrl_anime::*, ctrl_gfx::controller::CtrlGraphics};
|
use daemon::{ctrl_anime::*};
|
||||||
use daemon::{
|
use daemon::{
|
||||||
ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus},
|
ctrl_profiles::{controller::CtrlPlatformProfile, zbus::ProfileZbus},
|
||||||
laptops::LaptopLedData,
|
laptops::LaptopLedData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use supergfxctl::config::GfxConfig;
|
||||||
|
use supergfxctl::controller::CtrlGraphics;
|
||||||
|
use supergfxctl::gfx_vendors::GfxVendors;
|
||||||
use ::zbus::{fdo, Connection, ObjectServer};
|
use ::zbus::{fdo, Connection, ObjectServer};
|
||||||
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
use daemon::{CtrlTask, Reloadable, ZbusAdd};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use rog_dbus::DBUS_NAME;
|
use rog_dbus::DBUS_NAME;
|
||||||
use rog_types::gfx_vendors::GfxVendors;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@@ -32,6 +34,7 @@ use daemon::ctrl_rog_bios::CtrlRogBios;
|
|||||||
use zvariant::ObjectPath;
|
use zvariant::ObjectPath;
|
||||||
|
|
||||||
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
|
static PROFILE_CONFIG_PATH: &str = "/etc/asusd/profile.conf";
|
||||||
|
static GFX_CONFIG_PATH: &str = "/etc/asusd/supergfx.conf";
|
||||||
|
|
||||||
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut logger = env_logger::Builder::new();
|
let mut logger = env_logger::Builder::new();
|
||||||
@@ -81,9 +84,12 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
let mut object_server = ObjectServer::new(&connection);
|
let mut object_server = ObjectServer::new(&connection);
|
||||||
|
|
||||||
let config = Config::load();
|
let config = Config::load();
|
||||||
let enable_gfx_switching = config.gfx_managed;
|
|
||||||
let config = Arc::new(Mutex::new(config));
|
let config = Arc::new(Mutex::new(config));
|
||||||
|
|
||||||
|
let gfx_config = GfxConfig::load(GFX_CONFIG_PATH.into());
|
||||||
|
let enable_gfx_switching = gfx_config.gfx_managed;
|
||||||
|
let gfx_config = Arc::new(Mutex::new(gfx_config));
|
||||||
|
|
||||||
supported.add_to_server(&mut object_server);
|
supported.add_to_server(&mut object_server);
|
||||||
|
|
||||||
match CtrlRogBios::new(config.clone()) {
|
match CtrlRogBios::new(config.clone()) {
|
||||||
@@ -169,12 +175,12 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
// Graphics switching requires some checks on boot specifically for g-sync capable laptops
|
// Graphics switching requires some checks on boot specifically for g-sync capable laptops
|
||||||
if enable_gfx_switching {
|
if enable_gfx_switching {
|
||||||
match CtrlGraphics::new(config.clone()) {
|
match CtrlGraphics::new(gfx_config.clone()) {
|
||||||
Ok(mut ctrl) => {
|
Ok(mut ctrl) => {
|
||||||
// Need to check if a laptop has the dedicated gfx switch
|
// Need to check if a laptop has the dedicated gfx switch
|
||||||
if CtrlRogBios::has_dedicated_gfx_toggle() {
|
if CtrlRogBios::has_dedicated_gfx_toggle() {
|
||||||
if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
|
if let Ok(ded) = CtrlRogBios::get_gfx_mode() {
|
||||||
if let Ok(config) = config.lock() {
|
if let Ok(config) = gfx_config.lock() {
|
||||||
if ded == 1 {
|
if ded == 1 {
|
||||||
warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
|
warn!("Dedicated GFX toggle is on but driver mode is not nvidia \nSetting to nvidia driver mode");
|
||||||
let devices = ctrl.devices();
|
let devices = ctrl.devices();
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
use rog_profiles::error::ProfileError;
|
use rog_profiles::error::ProfileError;
|
||||||
use rog_types::error::GraphicsError;
|
use rog_types::error::GraphicsError;
|
||||||
|
use supergfxctl::error::GfxError;
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::ctrl_gfx::error::GfxError;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum RogError {
|
pub enum RogError {
|
||||||
ParseVendor,
|
ParseVendor,
|
||||||
@@ -24,7 +23,6 @@ pub enum RogError {
|
|||||||
Profiles(ProfileError),
|
Profiles(ProfileError),
|
||||||
Initramfs(String),
|
Initramfs(String),
|
||||||
Modprobe(String),
|
Modprobe(String),
|
||||||
Command(String, std::io::Error),
|
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
Zbus(zbus::Error),
|
Zbus(zbus::Error),
|
||||||
}
|
}
|
||||||
@@ -50,7 +48,6 @@ impl fmt::Display for RogError {
|
|||||||
RogError::Profiles(deets) => write!(f, "Profile error: {}", deets),
|
RogError::Profiles(deets) => write!(f, "Profile error: {}", deets),
|
||||||
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
|
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
|
||||||
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
|
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
|
||||||
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
|
|
||||||
RogError::Io(detail) => write!(f, "std::io error: {}", detail),
|
RogError::Io(detail) => write!(f, "std::io error: {}", detail),
|
||||||
RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail),
|
RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ pub mod ctrl_anime;
|
|||||||
pub mod ctrl_aura;
|
pub mod ctrl_aura;
|
||||||
/// Control of battery charge level
|
/// Control of battery charge level
|
||||||
pub mod ctrl_charge;
|
pub mod ctrl_charge;
|
||||||
/// GPU switching and power
|
|
||||||
pub mod ctrl_gfx;
|
|
||||||
/// Control CPU min/max freq and turbo, fan mode, fan curves
|
/// Control CPU min/max freq and turbo, fan mode, fan curves
|
||||||
///
|
///
|
||||||
/// Intel machines can control:
|
/// Intel machines can control:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ rog_anime = { path = "../rog-anime" }
|
|||||||
rog_aura = { path = "../rog-aura" }
|
rog_aura = { path = "../rog-aura" }
|
||||||
rog_profiles = { path = "../rog-profiles" }
|
rog_profiles = { path = "../rog-profiles" }
|
||||||
rog_types = { path = "../rog-types" }
|
rog_types = { path = "../rog-types" }
|
||||||
|
supergfxctl = { path = "../supergfx" }
|
||||||
zbus = "^1.9"
|
zbus = "^1.9"
|
||||||
zbus_macros = "^1.9"
|
zbus_macros = "^1.9"
|
||||||
zvariant = "^2.8"
|
zvariant = "^2.8"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub mod zbus_supported;
|
|||||||
use rog_anime::AnimePowerStates;
|
use rog_anime::AnimePowerStates;
|
||||||
use rog_aura::{AuraEffect, LedPowerStates};
|
use rog_aura::{AuraEffect, LedPowerStates};
|
||||||
use rog_profiles::Profile;
|
use rog_profiles::Profile;
|
||||||
use rog_types::gfx_vendors::{GfxRequiredUserAction, GfxVendors};
|
use supergfxctl::gfx_vendors::{GfxRequiredUserAction, GfxVendors};
|
||||||
use std::sync::mpsc::{channel, Receiver};
|
use std::sync::mpsc::{channel, Receiver};
|
||||||
use zbus::{Connection, Result, SignalReceiver};
|
use zbus::{Connection, Result, SignalReceiver};
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
|
use supergfxctl::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
|
||||||
use zbus::{dbus_proxy, Connection, Result};
|
use zbus::{dbus_proxy, Connection, Result};
|
||||||
|
|
||||||
#[dbus_proxy(
|
#[dbus_proxy(
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ pub static DBUS_NAME: &str = "org.asuslinux.Daemon";
|
|||||||
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
|
pub static DBUS_PATH: &str = "/org/asuslinux/Daemon";
|
||||||
pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
|
pub static DBUS_IFACE: &str = "org.asuslinux.Daemon";
|
||||||
|
|
||||||
pub mod gfx_vendors;
|
|
||||||
|
|
||||||
pub mod supported;
|
pub mod supported;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
|||||||
26
supergfx/Cargo.toml
Normal file
26
supergfx/Cargo.toml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[package]
|
||||||
|
name = "supergfxctl"
|
||||||
|
version = "1.1.0"
|
||||||
|
license = "MPL-2.0"
|
||||||
|
readme = "README.md"
|
||||||
|
authors = ["Luke <luke@ljones.dev>"]
|
||||||
|
repository = "https://gitlab.com/asus-linux/asusctl"
|
||||||
|
homepage = "https://gitlab.com/asus-linux/asusctl"
|
||||||
|
documentation = "https://docs.rs/rog-anime"
|
||||||
|
description = "Types useful for fancy keyboards on ASUS ROG laptops"
|
||||||
|
keywords = ["graphics", "nvidia", "switching"]
|
||||||
|
edition = "2018"
|
||||||
|
exclude = ["data"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "^1.0"
|
||||||
|
serde_derive = "^1.0"
|
||||||
|
serde_json = "^1.0"
|
||||||
|
log = "^0.4"
|
||||||
|
|
||||||
|
zbus = "^1.9.1"
|
||||||
|
zvariant = "^2.8"
|
||||||
|
zvariant_derive = "^2.8"
|
||||||
|
logind-zbus = "^0.7.1"
|
||||||
|
|
||||||
|
sysfs-class = "^0.1.2"
|
||||||
373
supergfx/LICENSE
Normal file
373
supergfx/LICENSE
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
89
supergfx/src/config.rs
Normal file
89
supergfx/src/config.rs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
use log::{error, warn};
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use crate::gfx_vendors::GfxVendors;
|
||||||
|
|
||||||
|
pub static CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||||
|
pub static AURA_CONFIG_PATH: &str = "/etc/asusd/asusd.conf";
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct GfxConfig {
|
||||||
|
#[serde(skip)]
|
||||||
|
config_path: String,
|
||||||
|
/// The current mode set, also applies on boot
|
||||||
|
pub gfx_mode: GfxVendors,
|
||||||
|
/// Only for informational purposes
|
||||||
|
#[serde(skip)]
|
||||||
|
pub gfx_tmp_mode: Option<GfxVendors>,
|
||||||
|
/// Set if graphics management is enabled
|
||||||
|
pub gfx_managed: bool,
|
||||||
|
/// Set if vfio option is enabled. This requires the vfio drivers to be built as modules
|
||||||
|
pub gfx_vfio_enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GfxConfig {
|
||||||
|
fn new(config_path: String) -> Self {
|
||||||
|
Self {
|
||||||
|
config_path,
|
||||||
|
gfx_mode: GfxVendors::Hybrid,
|
||||||
|
gfx_tmp_mode: None,
|
||||||
|
gfx_managed: true,
|
||||||
|
gfx_vfio_enable: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `load` will attempt to read the config, and panic if the dir is missing
|
||||||
|
pub fn load(config_path: String) -> Self {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&config_path)
|
||||||
|
.unwrap_or_else(|_| panic!("The directory {} is missing", config_path)); // okay to cause panic here
|
||||||
|
let mut buf = String::new();
|
||||||
|
let mut config;
|
||||||
|
if let Ok(read_len) = file.read_to_string(&mut buf) {
|
||||||
|
if read_len == 0 {
|
||||||
|
config = Self::new(config_path);
|
||||||
|
} else if let Ok(data) = serde_json::from_str(&buf) {
|
||||||
|
config = data;
|
||||||
|
config.config_path = config_path;
|
||||||
|
} else {
|
||||||
|
warn!("Could not deserialise {}", config_path);
|
||||||
|
panic!("Please remove {} then restart service", config_path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config = Self::new(config_path)
|
||||||
|
}
|
||||||
|
config.write();
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.open(&self.config_path)
|
||||||
|
.unwrap_or_else(|err| panic!("Error reading {}: {}", self.config_path, err));
|
||||||
|
let mut buf = String::new();
|
||||||
|
if let Ok(l) = file.read_to_string(&mut buf) {
|
||||||
|
if l == 0 {
|
||||||
|
warn!("File is empty {}", self.config_path);
|
||||||
|
} else {
|
||||||
|
let mut x: Self = serde_json::from_str(&buf)
|
||||||
|
.unwrap_or_else(|_| panic!("Could not deserialise {}", self.config_path));
|
||||||
|
// copy over serde skipped values
|
||||||
|
x.gfx_tmp_mode = self.gfx_tmp_mode;
|
||||||
|
*self = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self) {
|
||||||
|
let mut file = File::create(&self.config_path).expect("Couldn't overwrite config");
|
||||||
|
let json = serde_json::to_string_pretty(self).expect("Parse config to JSON failed");
|
||||||
|
file.write_all(json.as_bytes())
|
||||||
|
.unwrap_or_else(|err| error!("Could not write config: {}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,23 @@
|
|||||||
use ::zbus::Connection;
|
|
||||||
use ctrl_gfx::error::GfxError;
|
|
||||||
use ctrl_gfx::*;
|
|
||||||
use ctrl_rog_bios::CtrlRogBios;
|
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use logind_zbus::{
|
use logind_zbus::{
|
||||||
types::{SessionClass, SessionInfo, SessionState, SessionType},
|
types::{SessionClass, SessionInfo, SessionState, SessionType},
|
||||||
ManagerProxy, SessionProxy,
|
ManagerProxy, SessionProxy,
|
||||||
};
|
};
|
||||||
use rog_types::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
|
use ::zbus::Connection;
|
||||||
use std::{io::Write, ops::Add, path::Path, time::Instant};
|
use std::{io::Write, ops::Add, path::Path, time::Instant};
|
||||||
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::RuntimePM;
|
||||||
use sysfs_class::{PciDevice, SysClass};
|
use sysfs_class::{PciDevice, SysClass};
|
||||||
use system::{GraphicsDevice, PciBus};
|
|
||||||
|
|
||||||
use crate::*;
|
use crate::{*, error::GfxError, system::{GraphicsDevice, PciBus}};
|
||||||
|
|
||||||
|
use super::config::GfxConfig;
|
||||||
|
use super::gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors};
|
||||||
|
|
||||||
const THREAD_TIMEOUT_MSG: &str = "GFX: thread time exceeded 3 minutes, exiting";
|
const THREAD_TIMEOUT_MSG: &str = "GFX: thread time exceeded 3 minutes, exiting";
|
||||||
|
const NVIDIA_RUNTIME_STATUS_PATH: &str = "/sys/bus/pci/devices/0000:01:00.0/power/runtime_status";
|
||||||
|
|
||||||
pub struct CtrlGraphics {
|
pub struct CtrlGraphics {
|
||||||
bus: PciBus,
|
bus: PciBus,
|
||||||
@@ -27,20 +26,12 @@ pub struct CtrlGraphics {
|
|||||||
nvidia: Vec<GraphicsDevice>,
|
nvidia: Vec<GraphicsDevice>,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
other: Vec<GraphicsDevice>,
|
other: Vec<GraphicsDevice>,
|
||||||
config: Arc<Mutex<Config>>,
|
config: Arc<Mutex<GfxConfig>>,
|
||||||
thread_kill: Arc<Mutex<Option<mpsc::Sender<bool>>>>,
|
thread_kill: Arc<Mutex<Option<mpsc::Sender<bool>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reloadable for CtrlGraphics {
|
|
||||||
fn reload(&mut self) -> Result<(), RogError> {
|
|
||||||
self.auto_power()?;
|
|
||||||
info!("GFX: Reloaded gfx mode: {:?}", self.get_gfx_mode()?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CtrlGraphics {
|
impl CtrlGraphics {
|
||||||
pub fn new(config: Arc<Mutex<Config>>) -> std::io::Result<CtrlGraphics> {
|
pub fn new(config: Arc<Mutex<GfxConfig>>) -> std::io::Result<CtrlGraphics> {
|
||||||
let bus = PciBus::new()?;
|
let bus = PciBus::new()?;
|
||||||
info!("GFX: Rescanning PCI bus");
|
info!("GFX: Rescanning PCI bus");
|
||||||
bus.rescan()?;
|
bus.rescan()?;
|
||||||
@@ -108,6 +99,13 @@ impl CtrlGraphics {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Force reinit of all state, including reset of device state
|
||||||
|
pub fn reload(&mut self) -> Result<(), GfxError> {
|
||||||
|
self.auto_power()?;
|
||||||
|
info!("GFX: Reloaded gfx mode: {:?}", self.get_gfx_mode()?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bus(&self) -> PciBus {
|
pub fn bus(&self) -> PciBus {
|
||||||
self.bus.clone()
|
self.bus.clone()
|
||||||
}
|
}
|
||||||
@@ -117,7 +115,7 @@ impl CtrlGraphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Save the selected `Vendor` mode to config
|
/// Save the selected `Vendor` mode to config
|
||||||
fn save_gfx_mode(vendor: GfxVendors, config: Arc<Mutex<Config>>) {
|
fn save_gfx_mode(vendor: GfxVendors, config: Arc<Mutex<GfxConfig>>) {
|
||||||
if let Ok(mut config) = config.lock() {
|
if let Ok(mut config) = config.lock() {
|
||||||
config.gfx_mode = vendor;
|
config.gfx_mode = vendor;
|
||||||
config.write();
|
config.write();
|
||||||
@@ -125,7 +123,7 @@ impl CtrlGraphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Associated method to get which vendor mode is set
|
/// Associated method to get which vendor mode is set
|
||||||
pub(super) fn get_gfx_mode(&self) -> Result<GfxVendors, RogError> {
|
pub(super) fn get_gfx_mode(&self) -> Result<GfxVendors, GfxError> {
|
||||||
if let Ok(config) = self.config.lock() {
|
if let Ok(config) = self.config.lock() {
|
||||||
if let Some(mode) = config.gfx_tmp_mode {
|
if let Some(mode) = config.gfx_tmp_mode {
|
||||||
return Ok(mode);
|
return Ok(mode);
|
||||||
@@ -136,12 +134,12 @@ impl CtrlGraphics {
|
|||||||
Ok(GfxVendors::Hybrid)
|
Ok(GfxVendors::Hybrid)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_runtime_status() -> Result<GfxPower, RogError> {
|
pub(super) fn get_runtime_status() -> Result<GfxPower, GfxError> {
|
||||||
let path = Path::new("/sys/bus/pci/devices/0000:01:00.0/power/runtime_status");
|
let path = Path::new(NVIDIA_RUNTIME_STATUS_PATH);
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
let buf = std::fs::read_to_string(path).map_err(|err| {
|
let buf = std::fs::read_to_string(path).map_err(|err| {
|
||||||
RogError::Read(
|
GfxError::Read(
|
||||||
"/sys/bus/pci/devices/0000:01:00.0/power/runtime_status".to_string(),
|
path.to_string_lossy().to_string(),
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
@@ -152,7 +150,7 @@ impl CtrlGraphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Some systems have a fallback service to load nouveau if nvidia fails
|
/// Some systems have a fallback service to load nouveau if nvidia fails
|
||||||
fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), RogError> {
|
fn toggle_fallback_service(vendor: GfxVendors) -> Result<(), GfxError> {
|
||||||
let action = if vendor == GfxVendors::Nvidia {
|
let action = if vendor == GfxVendors::Nvidia {
|
||||||
info!("GFX: Enabling nvidia-fallback.service");
|
info!("GFX: Enabling nvidia-fallback.service");
|
||||||
"enable"
|
"enable"
|
||||||
@@ -165,7 +163,7 @@ impl CtrlGraphics {
|
|||||||
.arg(action)
|
.arg(action)
|
||||||
.arg("nvidia-fallback.service")
|
.arg("nvidia-fallback.service")
|
||||||
.status()
|
.status()
|
||||||
.map_err(|err| RogError::Command("systemctl".into(), err))?;
|
.map_err(|err| GfxError::Command("systemctl".into(), err))?;
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
// Error is ignored in case this service is removed
|
// Error is ignored in case this service is removed
|
||||||
@@ -179,7 +177,7 @@ impl CtrlGraphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write the appropriate xorg config for the chosen mode
|
/// Write the appropriate xorg config for the chosen mode
|
||||||
fn write_xorg_conf(vendor: GfxVendors) -> Result<(), RogError> {
|
fn write_xorg_conf(vendor: GfxVendors) -> Result<(), GfxError> {
|
||||||
let text = if vendor == GfxVendors::Nvidia {
|
let text = if vendor == GfxVendors::Nvidia {
|
||||||
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
|
[PRIMARY_GPU_BEGIN, PRIMARY_GPU_NVIDIA, PRIMARY_GPU_END].concat()
|
||||||
} else {
|
} else {
|
||||||
@@ -187,7 +185,7 @@ impl CtrlGraphics {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !Path::new(XORG_PATH).exists() {
|
if !Path::new(XORG_PATH).exists() {
|
||||||
std::fs::create_dir(XORG_PATH).map_err(|err| RogError::Write(XORG_PATH.into(), err))?;
|
std::fs::create_dir(XORG_PATH).map_err(|err| GfxError::Write(XORG_PATH.into(), err))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let file = XORG_PATH.to_string().add(XORG_FILE);
|
let file = XORG_PATH.to_string().add(XORG_FILE);
|
||||||
@@ -197,11 +195,11 @@ impl CtrlGraphics {
|
|||||||
.truncate(true)
|
.truncate(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(&file)
|
.open(&file)
|
||||||
.map_err(|err| RogError::Write(file, err))?;
|
.map_err(|err| GfxError::Write(file, err))?;
|
||||||
|
|
||||||
file.write_all(&text)
|
file.write_all(&text)
|
||||||
.and_then(|_| file.sync_all())
|
.and_then(|_| file.sync_all())
|
||||||
.map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?;
|
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +230,7 @@ impl CtrlGraphics {
|
|||||||
conf
|
conf
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_modprobe_conf(vendor: GfxVendors, devices: &[GraphicsDevice]) -> Result<(), RogError> {
|
fn write_modprobe_conf(vendor: GfxVendors, devices: &[GraphicsDevice]) -> Result<(), GfxError> {
|
||||||
info!("GFX: Writing {}", MODPROBE_PATH);
|
info!("GFX: Writing {}", MODPROBE_PATH);
|
||||||
let content = match vendor {
|
let content = match vendor {
|
||||||
GfxVendors::Nvidia | GfxVendors::Hybrid => {
|
GfxVendors::Nvidia | GfxVendors::Hybrid => {
|
||||||
@@ -250,16 +248,16 @@ impl CtrlGraphics {
|
|||||||
.truncate(true)
|
.truncate(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(MODPROBE_PATH)
|
.open(MODPROBE_PATH)
|
||||||
.map_err(|err| RogError::Path(MODPROBE_PATH.into(), err))?;
|
.map_err(|err| GfxError::Path(MODPROBE_PATH.into(), err))?;
|
||||||
|
|
||||||
file.write_all(&content)
|
file.write_all(&content)
|
||||||
.and_then(|_| file.sync_all())
|
.and_then(|_| file.sync_all())
|
||||||
.map_err(|err| RogError::Write(MODPROBE_PATH.into(), err))?;
|
.map_err(|err| GfxError::Write(MODPROBE_PATH.into(), err))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unbind_remove_nvidia(devices: &[GraphicsDevice]) -> Result<(), RogError> {
|
fn unbind_remove_nvidia(devices: &[GraphicsDevice]) -> Result<(), GfxError> {
|
||||||
// Unbind NVIDIA graphics devices and their functions
|
// Unbind NVIDIA graphics devices and their functions
|
||||||
let unbinds = devices.iter().map(|dev| dev.unbind());
|
let unbinds = devices.iter().map(|dev| dev.unbind());
|
||||||
// Remove NVIDIA graphics devices and their functions
|
// Remove NVIDIA graphics devices and their functions
|
||||||
@@ -267,14 +265,14 @@ impl CtrlGraphics {
|
|||||||
unbinds
|
unbinds
|
||||||
.chain(removes)
|
.chain(removes)
|
||||||
.collect::<Result<_, _>>()
|
.collect::<Result<_, _>>()
|
||||||
.map_err(|err| RogError::Command("device unbind error".into(), err))
|
.map_err(|err| GfxError::Command("device unbind error".into(), err))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), RogError> {
|
fn unbind_only(devices: &[GraphicsDevice]) -> Result<(), GfxError> {
|
||||||
let unbinds = devices.iter().map(|dev| dev.unbind());
|
let unbinds = devices.iter().map(|dev| dev.unbind());
|
||||||
unbinds
|
unbinds
|
||||||
.collect::<Result<_, _>>()
|
.collect::<Result<_, _>>()
|
||||||
.map_err(|err| RogError::Command("device unbind error".into(), err))
|
.map_err(|err| GfxError::Command("device unbind error".into(), err))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add or remove driver modules
|
/// Add or remove driver modules
|
||||||
@@ -288,7 +286,7 @@ impl CtrlGraphics {
|
|||||||
if count > MAX_TRIES {
|
if count > MAX_TRIES {
|
||||||
let msg = format!("{} {} failed for unknown reason", action, driver);
|
let msg = format!("{} {} failed for unknown reason", action, driver);
|
||||||
error!("GFX: {}", msg);
|
error!("GFX: {}", msg);
|
||||||
return Ok(()); //Err(RogError::Modprobe(msg));
|
return Ok(()); //Err(GfxError::Modprobe(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = cmd
|
let output = cmd
|
||||||
@@ -337,25 +335,25 @@ impl CtrlGraphics {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_display_manager_action(action: &str) -> Result<(), RogError> {
|
fn do_display_manager_action(action: &str) -> Result<(), GfxError> {
|
||||||
let mut cmd = Command::new("systemctl");
|
let mut cmd = Command::new("systemctl");
|
||||||
cmd.arg(action);
|
cmd.arg(action);
|
||||||
cmd.arg(DISPLAY_MANAGER);
|
cmd.arg(DISPLAY_MANAGER);
|
||||||
|
|
||||||
let status = cmd
|
let status = cmd
|
||||||
.status()
|
.status()
|
||||||
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
|
.map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?;
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"systemctl {} {} failed: {:?}",
|
"systemctl {} {} failed: {:?}",
|
||||||
action, DISPLAY_MANAGER, status
|
action, DISPLAY_MANAGER, status
|
||||||
);
|
);
|
||||||
return Err(GfxError::DisplayManagerAction(msg, status).into());
|
return Err(GfxError::DisplayManagerAction(msg, status));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_display_manager_state(state: &str) -> Result<(), RogError> {
|
fn wait_display_manager_state(state: &str) -> Result<(), GfxError> {
|
||||||
let mut cmd = Command::new("systemctl");
|
let mut cmd = Command::new("systemctl");
|
||||||
cmd.arg("is-active");
|
cmd.arg("is-active");
|
||||||
cmd.arg(DISPLAY_MANAGER);
|
cmd.arg(DISPLAY_MANAGER);
|
||||||
@@ -366,14 +364,14 @@ impl CtrlGraphics {
|
|||||||
// 3 seconds max
|
// 3 seconds max
|
||||||
let output = cmd
|
let output = cmd
|
||||||
.output()
|
.output()
|
||||||
.map_err(|err| RogError::Command(format!("{:?}", cmd), err))?;
|
.map_err(|err| GfxError::Command(format!("{:?}", cmd), err))?;
|
||||||
if output.stdout.starts_with(state.as_bytes()) {
|
if output.stdout.starts_with(state.as_bytes()) {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
std::thread::sleep(std::time::Duration::from_millis(250));
|
std::thread::sleep(std::time::Duration::from_millis(250));
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
Err(GfxError::DisplayManagerTimeout(state.into()).into())
|
Err(GfxError::DisplayManagerTimeout(state.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine if we need to logout/thread. Integrated<->Vfio mode does not
|
/// Determine if we need to logout/thread. Integrated<->Vfio mode does not
|
||||||
@@ -417,7 +415,7 @@ impl CtrlGraphics {
|
|||||||
vfio_enable: bool,
|
vfio_enable: bool,
|
||||||
devices: &[GraphicsDevice],
|
devices: &[GraphicsDevice],
|
||||||
bus: &PciBus,
|
bus: &PciBus,
|
||||||
) -> Result<(), RogError> {
|
) -> Result<(), GfxError> {
|
||||||
// Rescan before doing remove or add drivers
|
// Rescan before doing remove or add drivers
|
||||||
bus.rescan()?;
|
bus.rescan()?;
|
||||||
// Make sure the power management is set to auto for nvidia devices
|
// Make sure the power management is set to auto for nvidia devices
|
||||||
@@ -467,7 +465,7 @@ impl CtrlGraphics {
|
|||||||
Self::unbind_only(devices)?;
|
Self::unbind_only(devices)?;
|
||||||
Self::do_driver_action("vfio-pci", "modprobe")?;
|
Self::do_driver_action("vfio-pci", "modprobe")?;
|
||||||
} else {
|
} else {
|
||||||
return Err(GfxError::VfioDisabled.into());
|
return Err(GfxError::VfioDisabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GfxVendors::Integrated => {
|
GfxVendors::Integrated => {
|
||||||
@@ -490,7 +488,7 @@ impl CtrlGraphics {
|
|||||||
fn graphical_user_sessions_exist(
|
fn graphical_user_sessions_exist(
|
||||||
connection: &Connection,
|
connection: &Connection,
|
||||||
sessions: &[SessionInfo],
|
sessions: &[SessionInfo],
|
||||||
) -> Result<bool, RogError> {
|
) -> Result<bool, GfxError> {
|
||||||
for session in sessions {
|
for session in sessions {
|
||||||
let session_proxy = SessionProxy::new(connection, session)?;
|
let session_proxy = SessionProxy::new(connection, session)?;
|
||||||
if session_proxy.get_class()? == SessionClass::User {
|
if session_proxy.get_class()? == SessionClass::User {
|
||||||
@@ -514,8 +512,8 @@ impl CtrlGraphics {
|
|||||||
devices: Vec<GraphicsDevice>,
|
devices: Vec<GraphicsDevice>,
|
||||||
bus: PciBus,
|
bus: PciBus,
|
||||||
thread_stop: mpsc::Receiver<bool>,
|
thread_stop: mpsc::Receiver<bool>,
|
||||||
config: Arc<Mutex<Config>>,
|
config: Arc<Mutex<GfxConfig>>,
|
||||||
) -> Result<String, RogError> {
|
) -> Result<String, GfxError> {
|
||||||
info!("GFX: display-manager thread started");
|
info!("GFX: display-manager thread started");
|
||||||
|
|
||||||
const SLEEP_PERIOD: Duration = Duration::from_millis(100);
|
const SLEEP_PERIOD: Duration = Duration::from_millis(100);
|
||||||
@@ -631,12 +629,12 @@ impl CtrlGraphics {
|
|||||||
/// to switch modes.
|
/// to switch modes.
|
||||||
///
|
///
|
||||||
/// For manually calling (not on boot/startup) via dbus
|
/// For manually calling (not on boot/startup) via dbus
|
||||||
pub fn set_gfx_mode(&mut self, vendor: GfxVendors) -> Result<GfxRequiredUserAction, RogError> {
|
pub fn set_gfx_mode(&mut self, vendor: GfxVendors) -> Result<GfxRequiredUserAction, GfxError> {
|
||||||
if let Ok(gsync) = CtrlRogBios::get_gfx_mode() {
|
// if let Ok(gsync) = CtrlRogBios::get_gfx_mode() {
|
||||||
if gsync == 1 {
|
// if gsync == 1 {
|
||||||
return Err(GfxError::GsyncModeActive.into());
|
// return Err(GfxError::GsyncModeActive.into());
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
let vfio_enable = if let Ok(config) = self.config.try_lock() {
|
let vfio_enable = if let Ok(config) = self.config.try_lock() {
|
||||||
config.gfx_vfio_enable
|
config.gfx_vfio_enable
|
||||||
@@ -645,7 +643,7 @@ impl CtrlGraphics {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !vfio_enable && matches!(vendor, GfxVendors::Vfio) {
|
if !vfio_enable && matches!(vendor, GfxVendors::Vfio) {
|
||||||
return Err(GfxError::VfioDisabled.into());
|
return Err(GfxError::VfioDisabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must always cancel any thread running
|
// Must always cancel any thread running
|
||||||
@@ -687,7 +685,7 @@ impl CtrlGraphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Used only on boot to set correct mode
|
/// Used only on boot to set correct mode
|
||||||
fn auto_power(&mut self) -> Result<(), RogError> {
|
fn auto_power(&mut self) -> Result<(), GfxError> {
|
||||||
let vendor = self.get_gfx_mode()?;
|
let vendor = self.get_gfx_mode()?;
|
||||||
let devices = self.nvidia.clone();
|
let devices = self.nvidia.clone();
|
||||||
let bus = self.bus.clone();
|
let bus = self.bus.clone();
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::{error, process::ExitStatus};
|
use std::{error, process::ExitStatus};
|
||||||
|
|
||||||
use crate::error::RogError;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum GfxError {
|
pub enum GfxError {
|
||||||
ParseVendor,
|
ParseVendor,
|
||||||
@@ -16,6 +14,11 @@ pub enum GfxError {
|
|||||||
MissingModule(String),
|
MissingModule(String),
|
||||||
Modprobe(String),
|
Modprobe(String),
|
||||||
Command(String, std::io::Error),
|
Command(String, std::io::Error),
|
||||||
|
Path(String, std::io::Error),
|
||||||
|
Read(String, std::io::Error),
|
||||||
|
Write(String, std::io::Error),
|
||||||
|
Io(std::io::Error),
|
||||||
|
Zbus(zbus::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for GfxError {
|
impl fmt::Display for GfxError {
|
||||||
@@ -45,14 +48,25 @@ impl fmt::Display for GfxError {
|
|||||||
GfxError::MissingModule(m) => write!(f, "The module {} is missing", m),
|
GfxError::MissingModule(m) => write!(f, "The module {} is missing", m),
|
||||||
GfxError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
|
GfxError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
|
||||||
GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
|
GfxError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
|
||||||
|
GfxError::Path(path, error) => write!(f, "Path {}: {}", path, error),
|
||||||
|
GfxError::Read(path, error) => write!(f, "Read {}: {}", path, error),
|
||||||
|
GfxError::Write(path, error) => write!(f, "Write {}: {}", path, error),
|
||||||
|
GfxError::Io(detail) => write!(f, "std::io error: {}", detail),
|
||||||
|
GfxError::Zbus(detail) => write!(f, "Zbus error: {}", detail),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for GfxError {}
|
impl error::Error for GfxError {}
|
||||||
|
|
||||||
impl From<GfxError> for RogError {
|
impl From<zbus::Error> for GfxError {
|
||||||
fn from(err: GfxError) -> Self {
|
fn from(err: zbus::Error) -> Self {
|
||||||
RogError::GfxSwitching(err)
|
GfxError::Zbus(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for GfxError {
|
||||||
|
fn from(err: std::io::Error) -> Self {
|
||||||
|
GfxError::Io(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
use crate::error::GraphicsError;
|
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use zvariant_derive::Type;
|
use zvariant_derive::Type;
|
||||||
|
|
||||||
|
use crate::error::GfxError;
|
||||||
|
|
||||||
#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Type, PartialEq, Copy, Clone, Deserialize, Serialize)]
|
||||||
pub enum GfxPower {
|
pub enum GfxPower {
|
||||||
Active,
|
Active,
|
||||||
@@ -12,9 +13,9 @@ pub enum GfxPower {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for GfxPower {
|
impl FromStr for GfxPower {
|
||||||
type Err = GraphicsError;
|
type Err = GfxError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, GraphicsError> {
|
fn from_str(s: &str) -> Result<Self, GfxError> {
|
||||||
match s.to_lowercase().trim() {
|
match s.to_lowercase().trim() {
|
||||||
"active" => Ok(GfxPower::Active),
|
"active" => Ok(GfxPower::Active),
|
||||||
"suspended" => Ok(GfxPower::Suspended),
|
"suspended" => Ok(GfxPower::Suspended),
|
||||||
@@ -45,9 +46,9 @@ pub enum GfxVendors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for GfxVendors {
|
impl FromStr for GfxVendors {
|
||||||
type Err = GraphicsError;
|
type Err = GfxError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, GraphicsError> {
|
fn from_str(s: &str) -> Result<Self, GfxError> {
|
||||||
match s.to_lowercase().as_str() {
|
match s.to_lowercase().as_str() {
|
||||||
"nvidia" => Ok(GfxVendors::Nvidia),
|
"nvidia" => Ok(GfxVendors::Nvidia),
|
||||||
"hybrid" => Ok(GfxVendors::Hybrid),
|
"hybrid" => Ok(GfxVendors::Hybrid),
|
||||||
@@ -59,7 +60,7 @@ impl FromStr for GfxVendors {
|
|||||||
"compute\n" => Ok(GfxVendors::Compute),
|
"compute\n" => Ok(GfxVendors::Compute),
|
||||||
"vfio\n" => Ok(GfxVendors::Vfio),
|
"vfio\n" => Ok(GfxVendors::Vfio),
|
||||||
"integrated\n" => Ok(GfxVendors::Integrated),
|
"integrated\n" => Ok(GfxVendors::Integrated),
|
||||||
_ => Err(GraphicsError::ParseVendor),
|
_ => Err(GfxError::ParseVendor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod config;
|
||||||
|
pub mod gfx_vendors;
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
|
|
||||||
pub mod system;
|
pub mod system;
|
||||||
|
|
||||||
pub mod zbus;
|
pub mod zbus;
|
||||||
|
|
||||||
const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"];
|
const NVIDIA_DRIVERS: [&str; 4] = ["nvidia_drm", "nvidia_modeset", "nvidia_uvm", "nvidia"];
|
||||||
55
supergfx/src/zbus.rs
Normal file
55
supergfx/src/zbus.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
use log::{error, info, warn};
|
||||||
|
use zvariant::ObjectPath;
|
||||||
|
use ::zbus::dbus_interface;
|
||||||
|
|
||||||
|
use crate::{gfx_vendors::{GfxPower, GfxRequiredUserAction, GfxVendors}};
|
||||||
|
|
||||||
|
use super::controller::CtrlGraphics;
|
||||||
|
|
||||||
|
#[dbus_interface(name = "org.asuslinux.Daemon")]
|
||||||
|
impl CtrlGraphics {
|
||||||
|
fn vendor(&self) -> zbus::fdo::Result<GfxVendors> {
|
||||||
|
self.get_gfx_mode().map_err(|err| {
|
||||||
|
error!("GFX: {}", err);
|
||||||
|
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn power(&self) -> zbus::fdo::Result<GfxPower> {
|
||||||
|
Self::get_runtime_status().map_err(|err| {
|
||||||
|
error!("GFX: {}", err);
|
||||||
|
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_vendor(&mut self, vendor: GfxVendors) -> zbus::fdo::Result<GfxRequiredUserAction> {
|
||||||
|
info!("GFX: Switching gfx mode to {}", <&str>::from(vendor));
|
||||||
|
let msg = self.set_gfx_mode(vendor).map_err(|err| {
|
||||||
|
error!("GFX: {}", err);
|
||||||
|
zbus::fdo::Error::Failed(format!("GFX fail: {}", err))
|
||||||
|
})?;
|
||||||
|
self.notify_gfx(&vendor)
|
||||||
|
.unwrap_or_else(|err| warn!("GFX: {}", err));
|
||||||
|
self.notify_action(&msg)
|
||||||
|
.unwrap_or_else(|err| warn!("GFX: {}", err));
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
fn notify_gfx(&self, vendor: &GfxVendors) -> zbus::Result<()> {}
|
||||||
|
|
||||||
|
#[dbus_interface(signal)]
|
||||||
|
fn notify_action(&self, action: &GfxRequiredUserAction) -> zbus::Result<()> {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CtrlGraphics {
|
||||||
|
pub fn add_to_server(self, server: &mut zbus::ObjectServer) {
|
||||||
|
server
|
||||||
|
.at(&ObjectPath::from_str_unchecked("/org/asuslinux/Gfx"), self)
|
||||||
|
.map_err(|err| {
|
||||||
|
warn!("GFX: CtrlGraphics: add_to_server {}", err);
|
||||||
|
err
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user