mirror of
https://gitlab.com/asus-linux/asusctl.git
synced 2026-01-22 17:33:19 +01:00
Try to implement slash bar functionality - part 1
This commit is contained in:
33
Cargo.lock
generated
33
Cargo.lock
generated
@@ -136,6 +136,7 @@ dependencies = [
|
||||
"rog_dbus",
|
||||
"rog_platform",
|
||||
"rog_profiles",
|
||||
"rog_slash",
|
||||
"tinybmp",
|
||||
"tokio",
|
||||
"toml 0.5.11",
|
||||
@@ -160,6 +161,7 @@ dependencies = [
|
||||
"rog_aura",
|
||||
"rog_platform",
|
||||
"rog_profiles",
|
||||
"rog_slash",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"systemd-zbus",
|
||||
@@ -3500,6 +3502,7 @@ dependencies = [
|
||||
"rog_aura",
|
||||
"rog_platform",
|
||||
"rog_profiles",
|
||||
"rog_slash",
|
||||
"zbus 4.0.1",
|
||||
]
|
||||
|
||||
@@ -3545,6 +3548,23 @@ dependencies = [
|
||||
"uhid-virt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rog_slash"
|
||||
version = "6.0.0-alpha1"
|
||||
dependencies = [
|
||||
"cargo-husky",
|
||||
"dmi_id",
|
||||
"gif 0.12.0",
|
||||
"glam",
|
||||
"log",
|
||||
"pix",
|
||||
"png_pong",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"typeshare",
|
||||
"zbus 4.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.8.1"
|
||||
@@ -3883,6 +3903,19 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slashctl"
|
||||
version = "6.0.0-alpha1"
|
||||
dependencies = [
|
||||
"dmi_id",
|
||||
"futures-lite 1.13.0",
|
||||
"inotify",
|
||||
"rog_platform",
|
||||
"rog_profiles",
|
||||
"rog_slash",
|
||||
"udev 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slint"
|
||||
version = "1.5.1"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"asusctl",
|
||||
"slashctl",
|
||||
"asusd",
|
||||
"asusd-user",
|
||||
"config-traits",
|
||||
@@ -12,10 +13,12 @@ members = [
|
||||
"rog-aura",
|
||||
"rog-profiles",
|
||||
"rog-control-center",
|
||||
"rog-slash",
|
||||
"simulators",
|
||||
]
|
||||
default-members = [
|
||||
"asusctl",
|
||||
"slashctl",
|
||||
"asusd",
|
||||
"asusd-user",
|
||||
"cpuctl",
|
||||
|
||||
4
Makefile
4
Makefile
@@ -13,6 +13,7 @@ zshcpl = $(datarootdir)/zsh/site-functions
|
||||
|
||||
BIN_ROG := rog-control-center
|
||||
BIN_C := asusctl
|
||||
BIN_S := slashctl
|
||||
BIN_D := asusd
|
||||
BIN_U := asusd-user
|
||||
LEDCFG := aura_support.ron
|
||||
@@ -47,6 +48,7 @@ install-program:
|
||||
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_ROG)" "$(DESTDIR)$(bindir)/$(BIN_ROG)"
|
||||
|
||||
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_C)" "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_S)" "$(DESTDIR)$(bindir)/$(BIN_S)"
|
||||
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_D)" "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||
$(INSTALL_PROGRAM) "./target/$(TARGET)/$(BIN_U)" "$(DESTDIR)$(bindir)/$(BIN_U)"
|
||||
|
||||
@@ -86,6 +88,7 @@ uninstall:
|
||||
rm -r "$(DESTDIR)$(datarootdir)/icons/hicolor/512x512/apps/$(BIN_ROG).png"
|
||||
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_C)"
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_S)"
|
||||
rm -f "$(DESTDIR)$(bindir)/$(BIN_D)"
|
||||
rm -f "$(DESTDIR)$(libdir)/udev/rules.d/99-$(BIN_D).rules"
|
||||
rm -f "$(DESTDIR)/etc/asusd/$(LEDCFG)"
|
||||
@@ -142,6 +145,7 @@ endif
|
||||
cargo build $(ARGS)
|
||||
ifneq ($(STRIP_BINARIES),0)
|
||||
strip -s ./target/$(TARGET)/$(BIN_C)
|
||||
strip -s ./target/$(TARGET)/$(BIN_S)
|
||||
strip -s ./target/$(TARGET)/$(BIN_D)
|
||||
strip -s ./target/$(TARGET)/$(BIN_U)
|
||||
strip -s ./target/$(TARGET)/$(BIN_ROG)
|
||||
|
||||
@@ -7,6 +7,7 @@ version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
rog_anime = { path = "../rog-anime" }
|
||||
rog_slash = { path = "../rog-slash" }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_dbus = { path = "../rog-dbus" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use gumdrop::Options;
|
||||
@@ -87,15 +88,15 @@ impl FromStr for LedBrightness {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ToString for LedBrightness {
|
||||
fn to_string(&self) -> String {
|
||||
impl Display for LedBrightness {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let s = match self.level {
|
||||
Some(0x00) => "low",
|
||||
Some(0x01) => "med",
|
||||
Some(0x02) => "high",
|
||||
_ => "unknown",
|
||||
};
|
||||
s.to_owned()
|
||||
write!(f, "{}", s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use rog_platform::platform::ThrottlePolicy;
|
||||
use crate::anime_cli::AnimeCommand;
|
||||
use crate::aura_cli::{LedBrightness, LedPowerCommand1, LedPowerCommand2, SetAuraBuiltin};
|
||||
use crate::fan_curve_cli::FanCurveCommand;
|
||||
use crate::slash_cli::SlashCommand;
|
||||
|
||||
#[derive(Default, Options)]
|
||||
pub struct CliStart {
|
||||
@@ -41,6 +42,8 @@ pub enum CliCommand {
|
||||
Graphics(GraphicsCommand),
|
||||
#[options(name = "anime", help = "Manage AniMe Matrix")]
|
||||
Anime(AnimeCommand),
|
||||
#[options(name = "slash", help = "Manage Slash Ledbar")]
|
||||
Slash(SlashCommand),
|
||||
#[options(help = "Change bios settings")]
|
||||
Bios(BiosCommand),
|
||||
}
|
||||
|
||||
@@ -19,14 +19,17 @@ use rog_dbus::zbus_aura::AuraProxyBlocking;
|
||||
use rog_dbus::RogDbusClientBlocking;
|
||||
use rog_platform::platform::{GpuMode, Properties, ThrottlePolicy};
|
||||
use rog_profiles::error::ProfileError;
|
||||
use rog_slash::SlashMode;
|
||||
|
||||
use crate::aura_cli::{AuraPowerStates, LedBrightness};
|
||||
use crate::cli_opts::*;
|
||||
use crate::slash_cli::{SlashCommand};
|
||||
|
||||
mod anime_cli;
|
||||
mod aura_cli;
|
||||
mod cli_opts;
|
||||
mod fan_curve_cli;
|
||||
mod slash_cli;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = args().skip(1).collect();
|
||||
@@ -149,6 +152,7 @@ fn do_parsed(
|
||||
}
|
||||
Some(CliCommand::Graphics(_)) => do_gfx(),
|
||||
Some(CliCommand::Anime(cmd)) => handle_anime(dbus, cmd)?,
|
||||
Some(CliCommand::Slash(cmd)) => handle_slash(dbus, cmd)?,
|
||||
Some(CliCommand::Bios(cmd)) => handle_platform_properties(dbus, supported_properties, cmd)?,
|
||||
None => {
|
||||
if (!parsed.show_supported
|
||||
@@ -458,6 +462,49 @@ fn verify_brightness(brightness: f32) {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_slash(
|
||||
dbus: &RogDbusClientBlocking<'_>,
|
||||
cmd: &SlashCommand,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
if (
|
||||
cmd.enable_display.is_none() &&
|
||||
cmd.brightness.is_none() &&
|
||||
cmd.interval.is_none() &&
|
||||
!cmd.list &&
|
||||
!cmd.next &&
|
||||
!cmd.prev
|
||||
) || cmd.help
|
||||
{
|
||||
println!("Missing arg or command\n\n{}", cmd.self_usage());
|
||||
if let Some(lst) = cmd.self_command_list() {
|
||||
println!("\n{}", lst);
|
||||
}
|
||||
}
|
||||
if let Some(enable) = cmd.enable_display {
|
||||
dbus.proxies().slash().set_enable_display(enable)?;
|
||||
}
|
||||
if let Some(brightness) = cmd.brightness {
|
||||
dbus.proxies().slash().set_brightness(brightness)?;
|
||||
}
|
||||
if let Some(interval) = cmd.interval {
|
||||
dbus.proxies().slash().set_interval(interval)?;
|
||||
}
|
||||
if cmd.next {
|
||||
dbus.proxies().slash().set_current_mode(SlashMode::Bounce as u8)?;
|
||||
}
|
||||
if cmd.prev {
|
||||
dbus.proxies().slash().set_current_mode(SlashMode::Flow as u8)?;
|
||||
}
|
||||
if cmd.list {
|
||||
let res = SlashMode::list();
|
||||
for p in &res {
|
||||
println!("{:?}", p);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_led_mode(
|
||||
aura: &AuraProxyBlocking,
|
||||
mode: &LedModeCommand,
|
||||
|
||||
19
asusctl/src/slash_cli.rs
Normal file
19
asusctl/src/slash_cli.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use gumdrop::Options;
|
||||
|
||||
#[derive(Options)]
|
||||
pub struct SlashCommand {
|
||||
#[options(help = "print help message")]
|
||||
pub help: bool,
|
||||
#[options(meta = "", help = "enable/disable the display")]
|
||||
pub enable_display: Option<bool>,
|
||||
#[options(meta = "", help = "set brightness value <0-255>")]
|
||||
pub brightness: Option<u8>,
|
||||
#[options(meta = "", help = "set interval value <0-255>")]
|
||||
pub interval: Option<u8>,
|
||||
#[options(help = "toggle to next animation in list")]
|
||||
pub next: bool,
|
||||
#[options(help = "toggle to previous animation in list")]
|
||||
pub prev: bool,
|
||||
#[options(help = "list available animations")]
|
||||
pub list: bool,
|
||||
}
|
||||
@@ -16,6 +16,7 @@ path = "src/daemon.rs"
|
||||
[dependencies]
|
||||
config-traits = { path = "../config-traits" }
|
||||
rog_anime = { path = "../rog-anime", features = ["dbus"] }
|
||||
rog_slash = { path = "../rog-slash", features = ["dbus"] }
|
||||
rog_aura = { path = "../rog-aura", features = ["dbus"] }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
|
||||
@@ -90,13 +90,13 @@ impl StdConfig for Config {
|
||||
}
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad3<Config472, Config506, Config507> for Config {}
|
||||
|
||||
@@ -150,13 +150,13 @@ impl StdConfig for AnimeConfig {
|
||||
Self::create_default()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad2<AnimeConfigV460, AnimeConfigV472> for AnimeConfig {}
|
||||
|
||||
@@ -127,16 +127,16 @@ impl StdConfig for AuraConfig {
|
||||
panic!("This should not be used");
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
if self.config_name.is_empty() {
|
||||
panic!("Config file name should not be empty");
|
||||
}
|
||||
self.config_name.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for AuraConfig {}
|
||||
|
||||
@@ -348,7 +348,7 @@ mod tests {
|
||||
};
|
||||
let mut controller = CtrlKbdLed {
|
||||
led_prod: AuraDevice::X19b6,
|
||||
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("id_product").unwrap()),
|
||||
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("id_product").unwrap().0),
|
||||
supported_data: supported_basic_modes,
|
||||
per_key_mode_active: false,
|
||||
config,
|
||||
@@ -386,7 +386,7 @@ mod tests {
|
||||
};
|
||||
let mut controller = CtrlKbdLed {
|
||||
led_prod: AuraDevice::X19b6,
|
||||
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("id_product").unwrap()),
|
||||
led_node: LEDNode::Rog(KeyboardLed::default(), HidRaw::new("id_product").unwrap().0),
|
||||
supported_data: supported_basic_modes,
|
||||
per_key_mode_active: false,
|
||||
config,
|
||||
|
||||
52
asusd/src/ctrl_slash/config.rs
Normal file
52
asusd/src/ctrl_slash/config.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2};
|
||||
use rog_slash::{DeviceState, SlashMode};
|
||||
use crate::ctrl_anime::config::{AnimeConfig, AnimeConfigV460, AnimeConfigV472};
|
||||
|
||||
const CONFIG_FILE: &str = "slash.ron";
|
||||
|
||||
/// Config for base system actions for the anime display
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct SlashConfig {
|
||||
pub slash_enabled: bool,
|
||||
pub slash_brightness: u8,
|
||||
pub slash_interval: u8,
|
||||
pub slash_mode: u8,
|
||||
}
|
||||
|
||||
impl Default for SlashConfig {
|
||||
fn default() -> Self {
|
||||
SlashConfig {
|
||||
slash_enabled: true,
|
||||
slash_brightness: 255,
|
||||
slash_interval: 0,
|
||||
slash_mode: SlashMode::Bounce as u8,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl StdConfig for SlashConfig {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CONFIG_FILE.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
std::path::PathBuf::from(crate::CONFIG_PATH_BASE)
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad for SlashConfig {}
|
||||
|
||||
impl From<&SlashConfig> for DeviceState {
|
||||
fn from(config: &SlashConfig) -> Self {
|
||||
DeviceState {
|
||||
slash_enabled: config.slash_enabled,
|
||||
slash_brightness: config.slash_brightness,
|
||||
slash_interval: config.slash_interval,
|
||||
slash_mode: config.slash_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
100
asusd/src/ctrl_slash/mod.rs
Normal file
100
asusd/src/ctrl_slash/mod.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
pub mod config;
|
||||
pub mod trait_impls;
|
||||
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::usb_raw::USBRaw;
|
||||
use rog_slash::{SlashMode};
|
||||
use rog_slash::usb::{pkt_set_mode, pkt_set_options, pkts_for_init};
|
||||
use crate::ctrl_slash::config::SlashConfig;
|
||||
use crate::error::RogError;
|
||||
|
||||
enum Node {
|
||||
Usb(USBRaw),
|
||||
Hid(HidRaw),
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn write_bytes(&self, message: &[u8]) -> Result<(), RogError> {
|
||||
// TODO: map and pass on errors
|
||||
match self {
|
||||
Node::Usb(u) => {
|
||||
u.write_bytes(message).ok();
|
||||
}
|
||||
Node::Hid(h) => {
|
||||
h.write_bytes(message).ok();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// pub fn set_builtins_enabled(&self, enabled: bool) -> Result<(), SlashCtlError> {
|
||||
// self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
|
||||
// self.write_bytes(&pkt_set_enable_display(enabled))?;
|
||||
// self.write_bytes(&pkt_set_brightness(bright))?;
|
||||
// self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CtrlSlash {
|
||||
// node: HidRaw,
|
||||
node: Node,
|
||||
config: SlashConfig,
|
||||
// slash_type: SlashType,
|
||||
// // set to force thread to exit
|
||||
// thread_exit: Arc<AtomicBool>,
|
||||
// // Set to false when the thread exits
|
||||
// thread_running: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl CtrlSlash {
|
||||
#[inline]
|
||||
pub fn new(config: SlashConfig) -> Result<CtrlSlash, RogError> {
|
||||
let usb = USBRaw::new(rog_slash::usb::PROD_ID).ok();
|
||||
let hid = HidRaw::new(rog_slash::usb::PROD_ID_STR).ok();
|
||||
let node = if usb.is_some() {
|
||||
unsafe { Node::Usb(usb.unwrap_unchecked()) }
|
||||
} else if hid.is_some() {
|
||||
unsafe { Node::Hid(hid.unwrap_unchecked().0) }
|
||||
} else {
|
||||
return Err(RogError::NotSupported);
|
||||
};
|
||||
|
||||
// Maybe, detecting the slash-type may become necessary
|
||||
// let slash_type = get_slash_type()?;
|
||||
|
||||
let ctrl = CtrlSlash {
|
||||
node,
|
||||
config,
|
||||
// slash_type,
|
||||
// thread_exit: Arc::new(AtomicBool::new(false)),
|
||||
// thread_running: Arc::new(AtomicBool::new(false)),
|
||||
};
|
||||
ctrl.do_initialization()?;
|
||||
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
fn do_initialization(&self) -> Result<(), RogError> {
|
||||
|
||||
let init_packets = pkts_for_init();
|
||||
self.node.write_bytes(&init_packets[0])?;
|
||||
self.node.write_bytes(&init_packets[1])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_options(&self, enabled: bool, brightness: u8, interval: u8) -> Result<(), RogError> {
|
||||
let command_packets = pkt_set_options(enabled, brightness, interval);
|
||||
self.node.write_bytes(&command_packets)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_slash_mode(&self, slash_mode: SlashMode) -> Result<(), RogError> {
|
||||
let command_packets = pkt_set_mode(slash_mode as u8);
|
||||
self.node.write_bytes(&command_packets[0])?;
|
||||
self.node.write_bytes(&command_packets[1])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
94
asusd/src/ctrl_slash/trait_impls.rs
Normal file
94
asusd/src/ctrl_slash/trait_impls.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use std::sync::Arc;
|
||||
use log::warn;
|
||||
use zbus::{Connection, interface};
|
||||
use zbus::export::futures_util::lock::Mutex;
|
||||
use config_traits::StdConfig;
|
||||
use rog_slash::DeviceState;
|
||||
use rog_slash::usb::pkt_set_options;
|
||||
use crate::ctrl_slash::CtrlSlash;
|
||||
|
||||
|
||||
pub const SLASH_ZBUS_NAME: &str = "Slash";
|
||||
pub const SLASH_ZBUS_PATH: &str = "/org/asuslinux";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CtrlSlashZbus(pub Arc<Mutex<CtrlSlash>>);
|
||||
|
||||
|
||||
/// The struct with the main dbus methods requires this trait
|
||||
impl crate::ZbusRun for CtrlSlashZbus {
|
||||
async fn add_to_server(self, server: &mut Connection) {
|
||||
Self::add_to_server_helper(self, SLASH_ZBUS_NAME, server).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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.
|
||||
#[interface(name = "org.asuslinux.Slash")]
|
||||
impl CtrlSlashZbus {
|
||||
// /// Writes a data stream of length. Will force system thread to exit until
|
||||
// /// it is restarted
|
||||
// async fn write(&self, input: AnimeDataBuffer) -> zbus::fdo::Result<()> {
|
||||
// let lock = self.0.lock().await;
|
||||
// lock.thread_exit.store(true, Ordering::SeqCst);
|
||||
// lock.write_data_buffer(input).map_err(|err| {
|
||||
// warn!("ctrl_anime::run_animation:callback {}", err);
|
||||
// err
|
||||
// })?;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// Set base brightness level
|
||||
#[zbus(property)]
|
||||
async fn brightness(&self) -> u8 {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.slash_brightness
|
||||
}
|
||||
|
||||
/// Set base brightness level
|
||||
#[zbus(property)]
|
||||
async fn set_brightness(&self, brightness: u8) {
|
||||
let mut lock = self.0.lock().await;
|
||||
let enabled = brightness > 0;
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_options(enabled, brightness, lock.config.slash_interval))
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_slash::set_options {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
lock.config.slash_enabled = enabled;
|
||||
lock.config.slash_brightness = brightness;
|
||||
lock.config.write();
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn enable_display(&self) -> bool {
|
||||
let lock = self.0.lock().await;
|
||||
lock.config.slash_enabled
|
||||
}
|
||||
|
||||
/// Set whether the AniMe is enabled at all
|
||||
#[zbus(property)]
|
||||
async fn set_enable_display(&self, enabled: bool) {
|
||||
let mut lock = self.0.lock().await;
|
||||
lock.node
|
||||
.write_bytes(&pkt_set_options(enabled, lock.config.slash_brightness, lock.config.slash_interval))
|
||||
.map_err(|err| {
|
||||
warn!("ctrl_slash::set_options {}", err);
|
||||
})
|
||||
.ok();
|
||||
|
||||
lock.config.slash_enabled = enabled;
|
||||
lock.config.write();
|
||||
}
|
||||
|
||||
/// Get the device state as stored by asusd
|
||||
// #[zbus(property)]
|
||||
async fn device_state(&self) -> DeviceState {
|
||||
let lock = self.0.lock().await;
|
||||
DeviceState::from(&lock.config)
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,12 @@ use asusd::ctrl_aura::manager::AuraManager;
|
||||
use asusd::ctrl_fancurves::CtrlFanCurveZbus;
|
||||
use asusd::ctrl_platform::CtrlPlatform;
|
||||
use asusd::{print_board_info, start_tasks, CtrlTask, DBUS_NAME};
|
||||
use config_traits::{StdConfig, StdConfigLoad2, StdConfigLoad3};
|
||||
use config_traits::{StdConfig, StdConfigLoad, StdConfigLoad2, StdConfigLoad3};
|
||||
use log::{error, info};
|
||||
use zbus::fdo::ObjectManager;
|
||||
use asusd::ctrl_slash::config::SlashConfig;
|
||||
use asusd::ctrl_slash::CtrlSlash;
|
||||
use asusd::ctrl_slash::trait_impls::CtrlSlashZbus;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -41,6 +44,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
info!(" daemon v{}", asusd::VERSION);
|
||||
info!(" rog-anime v{}", rog_anime::VERSION);
|
||||
info!(" rog-slash v{}", rog_slash::VERSION);
|
||||
info!(" rog-aura v{}", rog_aura::VERSION);
|
||||
info!(" rog-profiles v{}", rog_profiles::VERSION);
|
||||
info!("rog-platform v{}", rog_platform::VERSION);
|
||||
@@ -104,6 +108,16 @@ async fn start_daemon() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
}
|
||||
|
||||
match CtrlSlash::new(SlashConfig::new().load()) {
|
||||
Ok(ctrl) => {
|
||||
let sig_ctx = CtrlPlatform::signal_context(&connection)?;
|
||||
start_tasks(ctrl, &mut connection, sig_ctx).await?;
|
||||
}
|
||||
Err(err) => {
|
||||
info!("Slash control: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
let _ = AuraManager::new(connection.clone()).await?;
|
||||
|
||||
// Request dbus name after finishing initalizing all functions
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
pub mod config;
|
||||
/// Control of anime matrix display
|
||||
pub mod ctrl_anime;
|
||||
/// Control of anime slash led bar
|
||||
pub mod ctrl_slash;
|
||||
/// Keyboard LED brightness control, RGB, and LED display modes
|
||||
pub mod ctrl_aura;
|
||||
/// Control platform profiles + fan-curves if available
|
||||
|
||||
@@ -50,6 +50,10 @@ impl StdConfig for Config {
|
||||
}
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CFG_FILE_NAME.to_owned()
|
||||
}
|
||||
|
||||
fn config_dir() -> std::path::PathBuf {
|
||||
let mut path = dirs::config_dir().unwrap_or_default();
|
||||
|
||||
@@ -62,10 +66,6 @@ impl StdConfig for Config {
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
fn file_name(&self) -> String {
|
||||
CFG_FILE_NAME.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl StdConfigLoad1<Config461> for Config {}
|
||||
|
||||
@@ -35,14 +35,10 @@ fn main() -> Result<()> {
|
||||
let is_rog_ally = prod_family == "RC71L";
|
||||
|
||||
// tmp-dir must live to the end of program life
|
||||
let _tmp_dir = match tempfile::Builder::new()
|
||||
let _tmp_dir = tempfile::Builder::new()
|
||||
.prefix("rog-gui")
|
||||
.rand_bytes(0)
|
||||
.tempdir()
|
||||
{
|
||||
Ok(tmp) => tmp,
|
||||
Err(_) => on_tmp_dir_exists().unwrap(),
|
||||
};
|
||||
.tempdir().unwrap_or_else(|_| on_tmp_dir_exists().unwrap());
|
||||
|
||||
let args: Vec<String> = args().skip(1).collect();
|
||||
|
||||
@@ -76,13 +72,10 @@ fn main() -> Result<()> {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let supported_properties = match dbus.proxies().platform().supported_properties() {
|
||||
Ok(s) => s,
|
||||
Err(_e) => {
|
||||
let supported_properties = dbus.proxies().platform().supported_properties().unwrap_or_else(|_e| {
|
||||
// TODO: show an error window
|
||||
Vec::default()
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Startup
|
||||
let mut config = Config::new().load();
|
||||
|
||||
@@ -12,6 +12,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
asusd = { path = "../asusd" }
|
||||
rog_anime = { path = "../rog-anime", features = ["dbus"] }
|
||||
rog_slash = { path = "../rog-slash", features = ["dbus"] }
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
pub use asusd::{DBUS_IFACE, DBUS_NAME, DBUS_PATH};
|
||||
|
||||
pub mod zbus_anime;
|
||||
pub mod zbus_aura;
|
||||
pub mod zbus_fan_curves;
|
||||
pub mod zbus_platform;
|
||||
pub mod zbus_slash;
|
||||
|
||||
// use rog_anime::AnimePowerStates;
|
||||
// use rog_aura::{AuraEffect, LedPowerStates};
|
||||
@@ -14,6 +16,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub struct DbusProxiesBlocking<'a> {
|
||||
anime: zbus_anime::AnimeProxyBlocking<'a>,
|
||||
slash: zbus_slash::SlashProxyBlocking<'a>,
|
||||
led: zbus_aura::AuraProxyBlocking<'a>,
|
||||
profile: zbus_fan_curves::FanCurvesProxyBlocking<'a>,
|
||||
rog_bios: zbus_platform::PlatformProxyBlocking<'a>,
|
||||
@@ -27,6 +30,7 @@ impl<'a> DbusProxiesBlocking<'a> {
|
||||
Ok((
|
||||
DbusProxiesBlocking {
|
||||
anime: zbus_anime::AnimeProxyBlocking::new(&conn)?,
|
||||
slash: zbus_slash::SlashProxyBlocking::new(&conn)?,
|
||||
led: zbus_aura::AuraProxyBlocking::new(&conn)?,
|
||||
profile: zbus_fan_curves::FanCurvesProxyBlocking::new(&conn)?,
|
||||
rog_bios: zbus_platform::PlatformProxyBlocking::new(&conn)?,
|
||||
@@ -38,15 +42,13 @@ impl<'a> DbusProxiesBlocking<'a> {
|
||||
pub fn anime(&self) -> &zbus_anime::AnimeProxyBlocking<'a> {
|
||||
&self.anime
|
||||
}
|
||||
|
||||
pub fn slash(&self) -> &zbus_slash::SlashProxyBlocking<'a> { &self.slash }
|
||||
pub fn aura(&self) -> &zbus_aura::AuraProxyBlocking<'a> {
|
||||
&self.led
|
||||
}
|
||||
|
||||
pub fn fan_curves(&self) -> &zbus_fan_curves::FanCurvesProxyBlocking<'a> {
|
||||
&self.profile
|
||||
}
|
||||
|
||||
pub fn platform(&self) -> &zbus_platform::PlatformProxyBlocking<'a> {
|
||||
&self.rog_bios
|
||||
}
|
||||
@@ -71,6 +73,7 @@ impl<'a> RogDbusClientBlocking<'a> {
|
||||
|
||||
pub struct DbusProxies<'a> {
|
||||
anime: zbus_anime::AnimeProxy<'a>,
|
||||
slash: zbus_slash::SlashProxy<'a>,
|
||||
led: zbus_aura::AuraProxy<'a>,
|
||||
profile: zbus_fan_curves::FanCurvesProxy<'a>,
|
||||
rog_bios: zbus_platform::PlatformProxy<'a>,
|
||||
@@ -84,6 +87,7 @@ impl<'a> DbusProxies<'a> {
|
||||
Ok((
|
||||
DbusProxies {
|
||||
anime: zbus_anime::AnimeProxy::new(&conn).await?,
|
||||
slash: zbus_slash::SlashProxy::new(&conn).await?,
|
||||
led: zbus_aura::AuraProxy::new(&conn).await?,
|
||||
profile: zbus_fan_curves::FanCurvesProxy::new(&conn).await?,
|
||||
rog_bios: zbus_platform::PlatformProxy::new(&conn).await?,
|
||||
@@ -95,6 +99,7 @@ impl<'a> DbusProxies<'a> {
|
||||
pub fn anime(&self) -> &zbus_anime::AnimeProxy<'a> {
|
||||
&self.anime
|
||||
}
|
||||
pub fn slash(&self) -> &zbus_slash::SlashProxy<'a> { &self.slash }
|
||||
|
||||
pub fn led(&self) -> &zbus_aura::AuraProxy<'a> {
|
||||
&self.led
|
||||
|
||||
35
rog-dbus/src/zbus_slash.rs
Normal file
35
rog-dbus/src/zbus_slash.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use zbus::proxy;
|
||||
|
||||
#[proxy(
|
||||
interface = "org.asuslinux.Slash",
|
||||
default_service = "org.asuslinux.Daemon",
|
||||
default_path = "/org/asuslinux"
|
||||
)]
|
||||
trait Slash {
|
||||
/// RunMainLoop method
|
||||
fn run_main_loop(&self, start: bool) -> zbus::Result<()>;
|
||||
|
||||
/// Brightness property
|
||||
#[zbus(property)]
|
||||
fn brightness(&self) -> zbus::Result<u8>;
|
||||
#[zbus(property)]
|
||||
fn set_brightness(&self, value: u8) -> zbus::Result<()>;
|
||||
|
||||
/// Interval property
|
||||
#[zbus(property)]
|
||||
fn interval(&self) -> zbus::Result<u8>;
|
||||
#[zbus(property)]
|
||||
fn set_interval(&self, value: u8) -> zbus::Result<()>;
|
||||
|
||||
/// BuiltinAnimations property
|
||||
#[zbus(property)]
|
||||
fn current_mode(&self) -> zbus::Result<u8>;
|
||||
#[zbus(property)]
|
||||
fn set_current_mode(&self, value: u8) -> zbus::Result<()>;
|
||||
|
||||
/// EnableDisplay property
|
||||
#[zbus(property)]
|
||||
fn enable_display(&self) -> zbus::Result<bool>;
|
||||
#[zbus(property)]
|
||||
fn set_enable_display(&self, value: bool) -> zbus::Result<()>;
|
||||
}
|
||||
@@ -11,11 +11,13 @@ impl USBRaw {
|
||||
pub fn new(id_product: u16) -> Result<Self> {
|
||||
for device in rusb::devices()?.iter() {
|
||||
let device_desc = device.device_descriptor()?;
|
||||
if device_desc.vendor_id() == 0x0b05 && device_desc.product_id() == id_product {
|
||||
if device_desc.vendor_id() == 0x0b05 {
|
||||
if device_desc.product_id() == id_product {
|
||||
let handle = Self::get_dev_handle(&device)?;
|
||||
return Ok(Self(handle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(PlatformError::MissingFunction(format!(
|
||||
"USBRaw dev {} not found",
|
||||
@@ -27,12 +29,17 @@ impl USBRaw {
|
||||
device: &Device<rusb::GlobalContext>,
|
||||
) -> Result<DeviceHandle<rusb::GlobalContext>> {
|
||||
// We don't expect this ID to ever change
|
||||
let mut device = device.open()?;
|
||||
let device_open = device.open();
|
||||
if let Err(err) = device_open {
|
||||
panic!("Could not open device, try running as root: {}", err);
|
||||
} else {
|
||||
let mut device = device_open.unwrap();
|
||||
device.reset()?;
|
||||
device.set_auto_detach_kernel_driver(true)?;
|
||||
device.claim_interface(0)?;
|
||||
Ok(device)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_bytes(&self, message: &[u8]) -> Result<usize> {
|
||||
self.0
|
||||
|
||||
@@ -2,28 +2,39 @@
|
||||
name = "rog_slash"
|
||||
license = "MPL-2.0"
|
||||
version.workspace = true
|
||||
readme = "README.md"
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
|
||||
homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl"
|
||||
documentation = "https://docs.rs/rog-anime"
|
||||
documentation = "https://docs.rs/rog-slash"
|
||||
description = "ASUS Slash display"
|
||||
keywords = ["ROG", "ASUS", "AniMe", "Slash"]
|
||||
edition = "2024"
|
||||
edition = "2021"
|
||||
exclude = ["data"]
|
||||
|
||||
[features]
|
||||
default = ["dbus", "detect"]
|
||||
dbus = ["zbus"]
|
||||
detect = ["dmi_id"]
|
||||
|
||||
[lib]
|
||||
name = "rog_slash"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
png_pong.workspace = true
|
||||
pix.workspace = true
|
||||
gif.workspace = true
|
||||
log.workspace = true
|
||||
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
zbus.workspace = true
|
||||
concat-idents.workspace = true
|
||||
udev.workspace = true
|
||||
inotify.workspace = true
|
||||
|
||||
glam.workspace = true
|
||||
typeshare.workspace = true
|
||||
|
||||
rusb.workspace = true
|
||||
zbus = { workspace = true, optional = true }
|
||||
|
||||
dmi_id = { path = "../dmi-id", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
cargo-husky.workspace = true
|
||||
rog_aura = { path = "../rog-aura" }
|
||||
105
rog-slash/src/data.rs
Normal file
105
rog-slash/src/data.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
#[cfg(feature = "dbus")]
|
||||
use zbus::zvariant::Type;
|
||||
|
||||
use crate::error::SlashError;
|
||||
|
||||
#[typeshare]
|
||||
#[cfg_attr(feature = "dbus", derive(Type), zvariant(signature = "s"))]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum SlashType {
|
||||
GA403,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl FromStr for SlashType {
|
||||
type Err = SlashError;
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"ga403" | "GA403" => Self::GA403,
|
||||
_ => Self::Unknown,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
// #[cfg_attr(feature = "dbus", derive(Type, Value, OwnedValue))]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum SlashMode {
|
||||
Bounce = 0x10,
|
||||
Slash = 0x12,
|
||||
Loading = 0x13,
|
||||
BitStream = 0x1D,
|
||||
Transmission = 0x1A,
|
||||
Flow = 0x19,
|
||||
Flux = 0x25,
|
||||
Phantom = 0x24,
|
||||
Spectrum = 0x26,
|
||||
Hazard = 0x32,
|
||||
Interfacing = 0x33,
|
||||
Ramp = 0x34,
|
||||
GameOver = 0x42,
|
||||
Start = 0x43,
|
||||
Buzzer = 0x44,
|
||||
}
|
||||
|
||||
impl SlashMode {
|
||||
pub const fn to_string(&self) -> &str
|
||||
{
|
||||
match &self {
|
||||
SlashMode::Bounce => "Bounce",
|
||||
SlashMode::Slash => "Slash",
|
||||
SlashMode::Loading => "Loading",
|
||||
SlashMode::BitStream => "BitStream",
|
||||
SlashMode::Transmission => "Transmission",
|
||||
SlashMode::Flow => "Flow",
|
||||
SlashMode::Flux => "Flux",
|
||||
SlashMode::Phantom => "Phantom",
|
||||
SlashMode::Spectrum => "Spectrum",
|
||||
SlashMode::Hazard => "Hazard",
|
||||
SlashMode::Interfacing => "Interfacing",
|
||||
SlashMode::Ramp => "Ramp",
|
||||
SlashMode::GameOver => "GameOver",
|
||||
SlashMode::Start => "Start",
|
||||
SlashMode::Buzzer => "Buzzer",
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn list() -> [&'static str; 15] {
|
||||
[
|
||||
SlashMode::Bounce.to_string(),
|
||||
SlashMode::Slash.to_string(),
|
||||
SlashMode::Loading.to_string(),
|
||||
SlashMode::BitStream.to_string(),
|
||||
SlashMode::Transmission.to_string(),
|
||||
SlashMode::Flow.to_string(),
|
||||
SlashMode::Flux.to_string(),
|
||||
SlashMode::Phantom.to_string(),
|
||||
SlashMode::Spectrum.to_string(),
|
||||
SlashMode::Hazard.to_string(),
|
||||
SlashMode::Interfacing.to_string(),
|
||||
SlashMode::Ramp.to_string(),
|
||||
SlashMode::GameOver.to_string(),
|
||||
SlashMode::Start.to_string(),
|
||||
SlashMode::Buzzer.to_string(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: move this out
|
||||
#[typeshare]
|
||||
#[cfg_attr(feature = "dbus", derive(Type))]
|
||||
#[typeshare]
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
|
||||
pub struct DeviceState {
|
||||
pub slash_enabled: bool,
|
||||
pub slash_brightness: u8,
|
||||
pub slash_interval: u8,
|
||||
pub slash_mode: u8,
|
||||
}
|
||||
|
||||
39
rog-slash/src/error.rs
Normal file
39
rog-slash/src/error.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
pub type Result<T> = std::result::Result<T, SlashError>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SlashError {
|
||||
Dbus(String),
|
||||
Udev(String, std::io::Error),
|
||||
NoDevice,
|
||||
UnsupportedDevice,
|
||||
DataBufferLength,
|
||||
ParseError(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for SlashError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SlashError::ParseError(e) => write!(f, "Could not parse {e}"),
|
||||
SlashError::Dbus(detail) => write!(f, "{}", detail),
|
||||
SlashError::Udev(deets, error) => write!(f, "udev {}: {}", deets, error),
|
||||
SlashError::NoDevice => write!(f, "No Slash device found"),
|
||||
SlashError::DataBufferLength => write!(
|
||||
f,
|
||||
"The data buffer was incorrect length for generating USB packets"
|
||||
),
|
||||
SlashError::UnsupportedDevice => write!(f, "Unsupported Slash device found"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SlashError {}
|
||||
|
||||
impl From<SlashError> for zbus::fdo::Error {
|
||||
#[inline]
|
||||
fn from(err: SlashError) -> Self {
|
||||
zbus::fdo::Error::Failed(format!("{}", err))
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,12 @@
|
||||
/// The main data conversion for transfering in shortform over dbus or other,
|
||||
/// or writing directly to the USB device
|
||||
mod data;
|
||||
pub use data::*;
|
||||
|
||||
/// Base errors that are possible
|
||||
pub mod error;
|
||||
|
||||
/// Provides const methods to create the USB HID control packets
|
||||
pub mod usb;
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
@@ -1,345 +1,129 @@
|
||||
//! Utils for writing to the `AniMe` USB device
|
||||
//! Utils for writing to the `Slash` USB device
|
||||
//!
|
||||
//! Use of the device requires a few steps:
|
||||
//! 1. Initialise the device by writing the two packets from
|
||||
//! `get_init_packets()` 2. Write data from `AnimePacketType`
|
||||
//! `get_init_packets()` 2. Write data from `SLashPacketType`
|
||||
//! 3. Write the packet from `get_flush_packet()`, which tells the device to
|
||||
//! display the data from step 2
|
||||
//!
|
||||
//! Step 1 need to applied only on fresh system boot.
|
||||
|
||||
use std::str::FromStr;
|
||||
//! Step 1 needs to be applied only on fresh system boot.
|
||||
|
||||
use dmi_id::DMIID;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use typeshare::typeshare;
|
||||
#[cfg(feature = "dbus")]
|
||||
use zbus::zvariant::{OwnedValue, Type, Value};
|
||||
use crate::error::SlashError;
|
||||
use crate::SlashType;
|
||||
|
||||
use crate::error::AnimeError;
|
||||
use crate::AnimeType;
|
||||
|
||||
const PACKET_SIZE: usize = 640;
|
||||
const PACKET_SIZE: usize = 128;
|
||||
const DEV_PAGE: u8 = 0x5e;
|
||||
pub const VENDOR_ID: u16 = 0x0b05;
|
||||
pub const PROD_ID: u16 = 0x193b;
|
||||
pub const VENDOR_ID: u16 = 0x0B05;
|
||||
pub const PROD_ID: u16 = 0x193B;
|
||||
pub const PROD_ID_STR: &str = "193B";
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "dbus",
|
||||
derive(Type, Value, OwnedValue),
|
||||
zvariant(signature = "u")
|
||||
)]
|
||||
#[typeshare]
|
||||
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
|
||||
/// Base LED brightness of the display
|
||||
pub enum Brightness {
|
||||
Off = 0,
|
||||
Low = 1,
|
||||
#[default]
|
||||
Med = 2,
|
||||
High = 3,
|
||||
}
|
||||
pub type SlashUsbPacket = [u8;PACKET_SIZE];
|
||||
|
||||
impl FromStr for Brightness {
|
||||
type Err = AnimeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"Off" | "off" => Brightness::Off,
|
||||
"Low" | "low" => Brightness::Low,
|
||||
"Med" | "med" => Brightness::Med,
|
||||
"High" | "high" => Brightness::High,
|
||||
_ => Brightness::Med,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Brightness {
|
||||
fn from(v: u8) -> Brightness {
|
||||
match v {
|
||||
0 => Brightness::Off,
|
||||
1 => Brightness::Low,
|
||||
3 => Brightness::High,
|
||||
_ => Brightness::Med,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Brightness {
|
||||
fn from(v: i32) -> Brightness {
|
||||
(v as u8).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Brightness> for i32 {
|
||||
fn from(v: Brightness) -> i32 {
|
||||
v as i32
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "dbus",
|
||||
derive(Type, Value, OwnedValue),
|
||||
zvariant(signature = "s")
|
||||
)]
|
||||
#[typeshare]
|
||||
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
|
||||
pub enum AnimBooting {
|
||||
#[default]
|
||||
GlitchConstruction = 0,
|
||||
StaticEmergence = 1,
|
||||
}
|
||||
|
||||
impl FromStr for AnimBooting {
|
||||
type Err = AnimeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"GlitchConstruction" => Ok(Self::GlitchConstruction),
|
||||
"StaticEmergence" => Ok(Self::StaticEmergence),
|
||||
_ => Err(AnimeError::ParseError(s.to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for AnimBooting {
|
||||
fn from(value: i32) -> Self {
|
||||
match value {
|
||||
0 => Self::GlitchConstruction,
|
||||
1 => Self::StaticEmergence,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnimBooting> for i32 {
|
||||
fn from(value: AnimBooting) -> Self {
|
||||
value as i32
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "dbus",
|
||||
derive(Type, Value, OwnedValue),
|
||||
zvariant(signature = "s")
|
||||
)]
|
||||
#[typeshare]
|
||||
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
|
||||
pub enum AnimAwake {
|
||||
#[default]
|
||||
BinaryBannerScroll = 0,
|
||||
RogLogoGlitch = 1,
|
||||
}
|
||||
|
||||
impl FromStr for AnimAwake {
|
||||
type Err = AnimeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"BinaryBannerScroll" => Ok(Self::BinaryBannerScroll),
|
||||
"RogLogoGlitch" => Ok(Self::RogLogoGlitch),
|
||||
_ => Err(AnimeError::ParseError(s.to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for AnimAwake {
|
||||
fn from(value: i32) -> Self {
|
||||
match value {
|
||||
0 => Self::BinaryBannerScroll,
|
||||
1 => Self::RogLogoGlitch,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnimAwake> for i32 {
|
||||
fn from(value: AnimAwake) -> Self {
|
||||
value as i32
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "dbus",
|
||||
derive(Type, Value, OwnedValue),
|
||||
zvariant(signature = "s")
|
||||
)]
|
||||
#[typeshare]
|
||||
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
|
||||
pub enum AnimSleeping {
|
||||
#[default]
|
||||
BannerSwipe = 0,
|
||||
Starfield = 1,
|
||||
}
|
||||
|
||||
impl FromStr for AnimSleeping {
|
||||
type Err = AnimeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"BannerSwipe" => Ok(Self::BannerSwipe),
|
||||
"Starfield" => Ok(Self::Starfield),
|
||||
_ => Err(AnimeError::ParseError(s.to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for AnimSleeping {
|
||||
fn from(value: i32) -> Self {
|
||||
match value {
|
||||
0 => Self::BannerSwipe,
|
||||
1 => Self::Starfield,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnimSleeping> for i32 {
|
||||
fn from(value: AnimSleeping) -> Self {
|
||||
value as i32
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
feature = "dbus",
|
||||
derive(Type, Value, OwnedValue),
|
||||
zvariant(signature = "s")
|
||||
)]
|
||||
#[typeshare]
|
||||
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Deserialize, Serialize)]
|
||||
pub enum AnimShutdown {
|
||||
#[default]
|
||||
GlitchOut = 0,
|
||||
SeeYa = 1,
|
||||
}
|
||||
|
||||
impl FromStr for AnimShutdown {
|
||||
type Err = AnimeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"GlitchOut" => Ok(Self::GlitchOut),
|
||||
"SeeYa" => Ok(Self::SeeYa),
|
||||
_ => Err(AnimeError::ParseError(s.to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for AnimShutdown {
|
||||
fn from(value: i32) -> Self {
|
||||
match value {
|
||||
0 => Self::GlitchOut,
|
||||
1 => Self::SeeYa,
|
||||
_ => Self::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnimShutdown> for i32 {
|
||||
fn from(value: AnimShutdown) -> Self {
|
||||
value as i32
|
||||
}
|
||||
}
|
||||
|
||||
/// `get_anime_type` is very broad, matching on part of the laptop board name
|
||||
/// only. For this reason `find_node()` must be used also to verify if the USB
|
||||
/// device is available.
|
||||
///
|
||||
/// The currently known USB device is `19b6`.
|
||||
/// The currently known USB device is `193B`.
|
||||
#[inline]
|
||||
pub fn get_anime_type() -> Result<AnimeType, AnimeError> {
|
||||
let dmi = DMIID::new().map_err(|_| AnimeError::NoDevice)?; // TODO: better error
|
||||
pub fn get_slash_type() -> Result<SlashType, SlashError> {
|
||||
let dmi = DMIID::new().map_err(|_| SlashError::NoDevice)?; // TODO: better error
|
||||
let board_name = dmi.board_name;
|
||||
|
||||
if board_name.contains("GA401I") || board_name.contains("GA401Q") {
|
||||
return Ok(AnimeType::GA401);
|
||||
} else if board_name.contains("GA402R") {
|
||||
return Ok(AnimeType::GA402);
|
||||
} else if board_name.contains("GU604V") {
|
||||
return Ok(AnimeType::GU604);
|
||||
if board_name.contains("GA403") {
|
||||
return Ok(SlashType::GA403);
|
||||
}
|
||||
log::warn!("AniMe Matrix device found but not yet supported, will default to a GA402 layout");
|
||||
Ok(AnimeType::Unknown)
|
||||
log::warn!("AniMe Slash device found but not yet supported, will default to a GA403 layout");
|
||||
Ok(SlashType::Unknown)
|
||||
}
|
||||
|
||||
/// Get the two device initialization packets. These are required for device
|
||||
/// start after the laptop boots.
|
||||
#[inline]
|
||||
pub const fn pkts_for_init() -> [[u8; PACKET_SIZE]; 2] {
|
||||
let mut packets = [[0; PACKET_SIZE]; 2];
|
||||
packets[0][0] = DEV_PAGE; // This is the USB page we're using throughout
|
||||
let mut count = 0;
|
||||
// TODO: memcpy or slice copy
|
||||
let bytes = "ASUS Tech.Inc.".as_bytes();
|
||||
while count < bytes.len() {
|
||||
packets[0][count + 1] = bytes[count];
|
||||
count += 1;
|
||||
}
|
||||
//
|
||||
packets[1][0] = DEV_PAGE;
|
||||
packets[1][1] = 0xc2;
|
||||
packets
|
||||
pub const fn pkts_for_init() -> [SlashUsbPacket; 2] {
|
||||
let mut pkt1 = [0;PACKET_SIZE];
|
||||
pkt1[0] = DEV_PAGE;
|
||||
pkt1[1] = 0xD7;
|
||||
pkt1[2] = 0x00;
|
||||
pkt1[3] = 0x00;
|
||||
pkt1[4] = 0x01;
|
||||
pkt1[5] = 0xAC;
|
||||
|
||||
let mut pkt2 = [0;PACKET_SIZE];
|
||||
pkt2[0] = DEV_PAGE;
|
||||
pkt2[1] = 0xD2;
|
||||
pkt2[2] = 0x02;
|
||||
pkt2[3] = 0x01;
|
||||
pkt2[4] = 0x08;
|
||||
pkt2[5] = 0xAB;
|
||||
|
||||
[pkt1, pkt2]
|
||||
}
|
||||
|
||||
/// Should be written to the device after writing the two main data packets that
|
||||
/// make up the display data packet
|
||||
#[inline]
|
||||
pub const fn pkt_flush() -> [u8; PACKET_SIZE] {
|
||||
pub const fn pkt_save() -> SlashUsbPacket {
|
||||
let mut pkt = [0;PACKET_SIZE];
|
||||
pkt[0] = DEV_PAGE;
|
||||
pkt[1] = 0xc0;
|
||||
pkt[1] = 0xD4;
|
||||
pkt[2] = 0x00;
|
||||
pkt[3] = 0x00;
|
||||
pkt[4] = 0x01;
|
||||
pkt[5] = 0xAB;
|
||||
|
||||
pkt
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn pkt_set_mode(mode: u8) -> [SlashUsbPacket; 2] {
|
||||
let mut pkt1 = [0;PACKET_SIZE];
|
||||
pkt1[0] = DEV_PAGE;
|
||||
pkt1[1] = 0x02;
|
||||
pkt1[2] = 0x03;
|
||||
pkt1[3] = 0x00;
|
||||
pkt1[4] = 0x0C;
|
||||
|
||||
let mut pkt2 = [0;PACKET_SIZE];
|
||||
pkt2[0] = DEV_PAGE;
|
||||
pkt2[1] = 0xD3;
|
||||
pkt2[2] = 0x04;
|
||||
pkt2[3] = 0x00;
|
||||
pkt2[4] = 0x0C;
|
||||
pkt2[5] = 0x01;
|
||||
pkt2[6] = mode;
|
||||
pkt2[7] = 0x02;
|
||||
pkt2[8] = 0x19;
|
||||
pkt2[9] = 0x03;
|
||||
pkt2[10] = 0x13;
|
||||
pkt2[11] = 0x04;
|
||||
pkt2[12] = 0x11;
|
||||
pkt2[13] = 0x05;
|
||||
pkt2[14] = 0x12;
|
||||
pkt2[15] = 0x06;
|
||||
pkt2[16] = 0x13;
|
||||
|
||||
[pkt1, pkt2]
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn pkt_set_options(enabled: bool, brightness: u8, interval: u8) -> SlashUsbPacket {
|
||||
let status_byte = if enabled { 0x01 } else { 0x00 };
|
||||
|
||||
let mut pkt = [0;PACKET_SIZE];
|
||||
pkt[0] = DEV_PAGE;
|
||||
pkt[1] = 0xD3;
|
||||
pkt[2] = 0x03;
|
||||
pkt
|
||||
}
|
||||
pkt[3] = 0x01;
|
||||
pkt[4] = 0x08;
|
||||
pkt[5] = 0xAB;
|
||||
pkt[6] = 0xFF;
|
||||
pkt[7] = 0x01;
|
||||
pkt[8] = status_byte;
|
||||
pkt[9] = 0x06;
|
||||
pkt[10] = brightness;
|
||||
pkt[11] = 0xFF;
|
||||
pkt[12] = interval;
|
||||
|
||||
/// Packet for setting the brightness (0-3). Requires
|
||||
/// `pkt_for_apply()` to be written after.
|
||||
#[inline]
|
||||
pub const fn pkt_set_brightness(brightness: Brightness) -> [u8; PACKET_SIZE] {
|
||||
let mut pkt = [0; PACKET_SIZE];
|
||||
pkt[0] = DEV_PAGE;
|
||||
pkt[1] = 0xc0;
|
||||
pkt[2] = 0x04;
|
||||
pkt[3] = brightness as u8;
|
||||
pkt
|
||||
}
|
||||
|
||||
/// Enable the display?
|
||||
#[inline]
|
||||
pub const fn pkt_set_enable_display(status: bool) -> [u8; PACKET_SIZE] {
|
||||
let mut pkt = [0; PACKET_SIZE];
|
||||
pkt[0] = DEV_PAGE;
|
||||
pkt[1] = 0xc3;
|
||||
pkt[2] = 0x01;
|
||||
pkt[3] = if status { 0x00 } else { 0x80 };
|
||||
pkt
|
||||
}
|
||||
|
||||
/// Enable builtin animations?
|
||||
#[inline]
|
||||
pub const fn pkt_set_enable_powersave_anim(status: bool) -> [u8; PACKET_SIZE] {
|
||||
let mut pkt = [0; PACKET_SIZE];
|
||||
pkt[0] = DEV_PAGE;
|
||||
pkt[1] = 0xc4;
|
||||
pkt[2] = 0x01;
|
||||
pkt[3] = if status { 0x00 } else { 0x80 };
|
||||
pkt
|
||||
}
|
||||
|
||||
/// Set which animations are shown for each stage
|
||||
#[inline]
|
||||
pub const fn pkt_set_builtin_animations(
|
||||
boot: AnimBooting,
|
||||
awake: AnimAwake,
|
||||
sleep: AnimSleeping,
|
||||
shutdown: AnimShutdown,
|
||||
) -> [u8; PACKET_SIZE] {
|
||||
let mut pkt = [0; PACKET_SIZE];
|
||||
pkt[0] = DEV_PAGE;
|
||||
pkt[1] = 0xc5;
|
||||
pkt[2] = (awake as u8)
|
||||
| ((sleep as u8) << 0x01)
|
||||
| ((shutdown as u8) << 0x02)
|
||||
| ((boot as u8) << 0x03);
|
||||
pkt
|
||||
}
|
||||
|
||||
18
slashctl/Cargo.toml
Normal file
18
slashctl/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "slashctl"
|
||||
license = "MPL-2.0"
|
||||
version.workspace = true
|
||||
authors = ["Luke <luke@ljones.dev>"]
|
||||
repository = "https://gitlab.com/asus-linux/asus-nb-ctrl"
|
||||
homepage = "https://gitlab.com/asus-linux/asus-nb-ctrl"
|
||||
description = "A ctrl app to control the Asus Slash"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
rog_slash = { path = "../rog-slash" }
|
||||
rog_platform = { path = "../rog-platform" }
|
||||
rog_profiles = { path = "../rog-profiles" }
|
||||
dmi_id = { path = "../dmi-id" }
|
||||
futures-lite = "*"
|
||||
udev.workspace = true
|
||||
inotify.workspace = true
|
||||
94
slashctl/src/ctrl_slash/mod.rs
Normal file
94
slashctl/src/ctrl_slash/mod.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use rog_platform::hid_raw::HidRaw;
|
||||
use rog_platform::usb_raw::USBRaw;
|
||||
use rog_slash::{SlashMode};
|
||||
use rog_slash::usb::{pkt_set_mode, pkt_set_options, pkts_for_init};
|
||||
|
||||
use crate::error::SlashCtlError;
|
||||
|
||||
enum Node {
|
||||
Usb(USBRaw),
|
||||
Hid(HidRaw),
|
||||
}
|
||||
|
||||
impl Node {
|
||||
pub fn write_bytes(&self, message: &[u8]) -> Result<(), SlashCtlError> {
|
||||
// TODO: map and pass on errors
|
||||
match self {
|
||||
Node::Usb(u) => {
|
||||
u.write_bytes(message).ok();
|
||||
}
|
||||
Node::Hid(h) => {
|
||||
h.write_bytes(message).ok();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// pub fn set_builtins_enabled(&self, enabled: bool) -> Result<(), SlashCtlError> {
|
||||
// self.write_bytes(&pkt_set_enable_powersave_anim(enabled))?;
|
||||
// self.write_bytes(&pkt_set_enable_display(enabled))?;
|
||||
// self.write_bytes(&pkt_set_brightness(bright))?;
|
||||
// self.write_bytes(&pkt_set_enable_powersave_anim(enabled))
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
pub struct CtrlSlash {
|
||||
// node: HidRaw,
|
||||
node: Node,
|
||||
// slash_type: SlashType,
|
||||
// // set to force thread to exit
|
||||
// thread_exit: Arc<AtomicBool>,
|
||||
// // Set to false when the thread exits
|
||||
// thread_running: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl CtrlSlash {
|
||||
#[inline]
|
||||
pub fn new() -> Result<CtrlSlash, SlashCtlError> {
|
||||
let usb = USBRaw::new(rog_slash::usb::PROD_ID).ok();
|
||||
let hid = HidRaw::new(rog_slash::usb::PROD_ID_STR).ok();
|
||||
let node = if usb.is_some() {
|
||||
unsafe { Node::Usb(usb.unwrap_unchecked()) }
|
||||
} else if hid.is_some() {
|
||||
unsafe { Node::Hid(hid.unwrap_unchecked().0) }
|
||||
} else {
|
||||
return Err(SlashCtlError::NotSupported);
|
||||
};
|
||||
|
||||
// Maybe, detecting the slash-type may become necessary
|
||||
// let slash_type = get_slash_type()?;
|
||||
|
||||
let ctrl = CtrlSlash {
|
||||
node,
|
||||
// slash_type,
|
||||
// thread_exit: Arc::new(AtomicBool::new(false)),
|
||||
// thread_running: Arc::new(AtomicBool::new(false)),
|
||||
};
|
||||
ctrl.do_initialization()?;
|
||||
|
||||
Ok(ctrl)
|
||||
}
|
||||
|
||||
fn do_initialization(&self) -> Result<(), SlashCtlError> {
|
||||
|
||||
let init_packets = pkts_for_init();
|
||||
self.node.write_bytes(&init_packets[0])?;
|
||||
self.node.write_bytes(&init_packets[1])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_options(&self, enabled: bool, brightness: u8, interval: u8) -> Result<(), SlashCtlError> {
|
||||
let command_packets = pkt_set_options(enabled, brightness, interval);
|
||||
self.node.write_bytes(&command_packets)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_slash_mode(&self, slash_mode: SlashMode) -> Result<(), SlashCtlError> {
|
||||
let command_packets = pkt_set_mode(slash_mode as u8);
|
||||
self.node.write_bytes(&command_packets[0])?;
|
||||
self.node.write_bytes(&command_packets[1])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
30
slashctl/src/error.rs
Normal file
30
slashctl/src/error.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::fmt;
|
||||
use std::fmt::{Display};
|
||||
use rog_slash::error::SlashError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SlashCtlError {
|
||||
NotSupported,
|
||||
Slash(SlashError),
|
||||
}
|
||||
|
||||
|
||||
impl Display for SlashCtlError {
|
||||
// This trait requires `fmt` with this exact signature.
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
SlashCtlError::NotSupported => write!(f, "Not supported"),
|
||||
SlashCtlError::Slash(err) => write!(f, "Slash error: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for SlashCtlError {}
|
||||
|
||||
|
||||
|
||||
impl From<SlashError> for SlashCtlError {
|
||||
fn from(err: SlashError) -> Self {
|
||||
SlashCtlError::Slash(err)
|
||||
}
|
||||
}
|
||||
15
slashctl/src/main.rs
Normal file
15
slashctl/src/main.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use crate::ctrl_slash::CtrlSlash;
|
||||
use crate::error::SlashCtlError;
|
||||
|
||||
mod ctrl_slash;
|
||||
mod error;
|
||||
|
||||
fn main() -> Result<(), SlashCtlError>{
|
||||
// let args: Vec<String> = args().skip(1).collect();
|
||||
|
||||
let ctrl_slash = CtrlSlash::new()?;
|
||||
ctrl_slash.set_options(false, 10, 0)?;
|
||||
// ctrl_slash.set_options(true, 5, 2)?;
|
||||
// ctrl_slash.set_slash_mode(SlashModes::Flow)?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user