Refactored gfx switch session monitor

This commit is contained in:
Luke D Jones
2021-03-16 21:06:18 +13:00
parent 35438e2e77
commit cec4016862
17 changed files with 317 additions and 307 deletions

View File

@@ -56,10 +56,12 @@ impl Config {
.write(true)
.create(true)
.open(&CONFIG_PATH)
.expect(&format!(
"The file {} or directory /etc/asusd/ is missing",
CONFIG_PATH
)); // okay to cause panic here
.unwrap_or_else(|_| {
panic!(
"The file {} or directory /etc/asusd/ is missing",
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 {

View File

@@ -21,7 +21,6 @@ use rog_types::{
error::AuraError,
};
use rusb::{Device, DeviceHandle};
use std::convert::TryInto;
use std::error::Error;
use std::time::Duration;
use zbus::dbus_interface;
@@ -60,7 +59,7 @@ pub trait Dbus {
impl crate::ZbusAdd for CtrlAnimeDisplay {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Anime".try_into().unwrap(), self)
.at("/org/asuslinux/Anime", self)
.map_err(|err| {
warn!("CtrlAnimeDisplay: add_to_server {}", err);
err

View File

@@ -2,7 +2,6 @@ use crate::{config::Config, error::RogError, GetSupported};
//use crate::dbus::DbusEvents;
use log::{info, warn};
use serde_derive::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
@@ -64,7 +63,7 @@ impl CtrlCharge {
impl crate::ZbusAdd for CtrlCharge {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Charge".try_into().unwrap(), self)
.at("/org/asuslinux/Charge", self)
.map_err(|err| {
warn!("CtrlCharge: add_to_server {}", err);
err

View File

@@ -6,7 +6,6 @@ use crate::{
use log::{info, warn};
use rog_types::profile::{FanLevel, ProfileEvent};
use serde_derive::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
@@ -135,7 +134,7 @@ impl DbusFanAndCpu {
impl crate::ZbusAdd for DbusFanAndCpu {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Profile".try_into().unwrap(), self)
.at("/org/asuslinux/Profile", self)
.map_err(|err| {
warn!("DbusFanAndCpu: add_to_server {}", err);
err

View File

@@ -2,19 +2,24 @@ use ctrl_gfx::error::GfxError;
use ctrl_gfx::*;
use ctrl_rog_bios::CtrlRogBios;
use log::{error, info, warn};
use logind_zbus::{
types::{SessionClass, SessionInfo, SessionType},
ManagerProxy, SessionProxy,
};
use rog_types::gfx_vendors::GfxVendors;
use session_manager::{are_gfx_sessions_alive, get_sessions};
use std::{io::Write, ops::Add, path::Path};
use std::{io::Write, ops::Add, path::Path, time::Instant};
use std::{iter::FromIterator, thread::JoinHandle};
use std::{process::Command, thread::sleep, time::Duration};
use std::{str::FromStr, sync::mpsc};
use std::{sync::Arc, sync::Mutex};
use sysfs_class::{PciDevice, SysClass};
use system::{GraphicsDevice, PciBus};
use zbus::dbus_interface;
use zbus::{dbus_interface, Connection};
use crate::*;
const THREAD_TIMEOUT_MSG: &str = "GFX: thread time exceeded 3 minutes, exiting";
pub struct CtrlGraphics {
bus: PciBus,
_amd: Vec<GraphicsDevice>,
@@ -34,8 +39,6 @@ trait Dbus {
fn notify_action(&self, action: &str) -> zbus::Result<()>;
}
use std::convert::TryInto;
#[dbus_interface(name = "org.asuslinux.Daemon")]
impl Dbus for CtrlGraphics {
fn vendor(&self) -> String {
@@ -72,12 +75,7 @@ impl Dbus for CtrlGraphics {
impl ZbusAdd for CtrlGraphics {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(
&"/org/asuslinux/Gfx"
.try_into()
.expect("Couldn't add to zbus"),
self,
)
.at("/org/asuslinux/Gfx", self)
.map_err(|err| {
warn!("GFX: CtrlGraphics: add_to_server {}", err);
err
@@ -165,20 +163,18 @@ impl CtrlGraphics {
self.nvidia.clone()
}
fn save_gfx_mode(vendor: GfxVendors, config: Arc<Mutex<Config>>) -> Result<(), RogError> {
fn save_gfx_mode(vendor: GfxVendors, config: Arc<Mutex<Config>>) {
if let Ok(mut config) = config.lock() {
config.gfx_mode = vendor.clone();
config.gfx_mode = vendor;
config.write();
return Ok(());
}
// TODO: Error here
Ok(())
}
/// Associated method to get which vendor mode is set
pub fn get_gfx_mode(&self) -> Result<GfxVendors, RogError> {
if let Ok(config) = self.config.lock() {
return Ok(config.gfx_mode.clone());
return Ok(config.gfx_mode);
}
// TODO: Error here
Ok(GfxVendors::Hybrid)
@@ -379,7 +375,7 @@ impl CtrlGraphics {
std::thread::sleep(std::time::Duration::from_millis(500));
count += 1;
}
return Err(GfxError::DisplayManagerTimeout(state.into()).into());
Err(GfxError::DisplayManagerTimeout(state.into()).into())
}
/// Write the config changes and add/remove drivers and devices depending
@@ -425,34 +421,69 @@ impl CtrlGraphics {
Ok(())
}
fn graphical_session_active(
connection: &Connection,
sessions: &[SessionInfo],
) -> Result<bool, RogError> {
for session in sessions {
let session_proxy = SessionProxy::new(&connection, session)?;
if session_proxy.get_class()? == SessionClass::User {
match session_proxy.get_type()? {
SessionType::X11 | SessionType::Wayland | SessionType::MIR => {
if session_proxy.get_active()? {
return Ok(true);
}
}
_ => {}
}
}
}
Ok(false)
}
/// Spools until all user sessions are ended then switches to requested mode
fn fire_starter(
vendor: GfxVendors,
devices: Vec<GraphicsDevice>,
bus: PciBus,
killer: mpsc::Receiver<bool>,
thread_stop: mpsc::Receiver<bool>,
) -> Result<String, RogError> {
info!("GFX: display-manager thread started");
let mut sessions: Vec<session_manager::Session> = get_sessions()?;
const SLEEP_PERIOD: Duration = Duration::from_millis(300);
const REFRESH_COUNTDOWN: u32 = 3;
let mut refresh_sessions = REFRESH_COUNTDOWN;
while are_gfx_sessions_alive(&sessions) {
if let Ok(stop) = killer.try_recv() {
const SLEEP_PERIOD: Duration = Duration::from_millis(100);
let start_time = Instant::now();
let connection = Connection::new_system()?;
let manager = ManagerProxy::new(&connection)?;
let mut sessions = manager.list_sessions()?;
loop {
let tmp = manager.list_sessions()?;
if !tmp.iter().eq(&sessions) {
warn!("GFX: Sessions list changed");
warn!("GFX: Old list:\n{:?}\nNew list:\n{:?}", &sessions, &tmp);
sessions = tmp;
}
if !Self::graphical_session_active(&connection, &sessions)? {
break;
}
if let Ok(stop) = thread_stop.try_recv() {
if stop {
return Ok("Graphics mode change was cancelled".into());
}
}
// exit if 3 minutes pass
if Instant::now().duration_since(start_time).as_secs() > 180 {
warn!("{}", THREAD_TIMEOUT_MSG);
return Ok(THREAD_TIMEOUT_MSG.into());
}
// Don't spin at max speed
sleep(SLEEP_PERIOD);
if refresh_sessions == 0 {
refresh_sessions = REFRESH_COUNTDOWN;
sessions = get_sessions()?;
}
refresh_sessions -= 1;
}
info!("GFX: all graphical user sessions ended, continuing");
Self::do_display_manager_action("stop")?;
@@ -514,7 +545,7 @@ impl CtrlGraphics {
let killer = self.thread_kill.clone();
// Save selected mode in case of reboot
Self::save_gfx_mode(vendor, self.config.clone())?;
Self::save_gfx_mode(vendor, self.config.clone());
let _join: JoinHandle<()> = std::thread::spawn(move || {
Self::fire_starter(vendor, devices, bus, rx)
@@ -526,7 +557,6 @@ impl CtrlGraphics {
if let Ok(mut lock) = killer.try_lock() {
*lock = None;
}
return;
});
// TODO: undo if failed? Save last mode, catch errors...

View File

@@ -18,9 +18,9 @@ use rog_types::{
};
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::{convert::TryInto, path::Path};
use zbus::dbus_interface;
use crate::GetSupported;
@@ -88,7 +88,7 @@ trait Dbus {
impl crate::ZbusAdd for DbusKbdBacklight {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Led".try_into().unwrap(), self)
.at("/org/asuslinux/Led", self)
.map_err(|err| {
error!("DbusKbdBacklight: add_to_server {}", err);
})

View File

@@ -2,12 +2,12 @@ use crate::{config::Config, error::RogError, GetSupported};
use log::{error, info, warn};
use serde_derive::{Deserialize, Serialize};
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;
use std::sync::Arc;
use std::sync::Mutex;
use std::{convert::TryInto, io::BufRead};
use zbus::dbus_interface;
const INITRAMFS_PATH: &str = "/usr/sbin/update-initramfs";
@@ -101,7 +101,7 @@ impl CtrlRogBios {
impl crate::ZbusAdd for CtrlRogBios {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/RogBios".try_into().unwrap(), self)
.at("/org/asuslinux/RogBios", self)
.map_err(|err| {
warn!("CtrlRogBios: add_to_server {}", err);
err
@@ -334,7 +334,7 @@ impl CtrlRogBios {
.map_err(|err| RogError::Write(format!("{:?}", cmd), err))?;
if !status.success() {
error!("Ram disk update failed");
return Err(RogError::Initramfs("Ram disk update failed".into()).into());
return Err(RogError::Initramfs("Ram disk update failed".into()));
} else {
info!("Successfully updated initramfs");
}

View File

@@ -1,5 +1,3 @@
use std::convert::TryInto;
use log::warn;
use serde_derive::{Deserialize, Serialize};
use zbus::dbus_interface;
@@ -32,7 +30,7 @@ impl SupportedFunctions {
impl crate::ZbusAdd for SupportedFunctions {
fn add_to_server(self, server: &mut zbus::ObjectServer) {
server
.at(&"/org/asuslinux/Supported".try_into().unwrap(), self)
.at("/org/asuslinux/Supported", self)
.map_err(|err| {
warn!("SupportedFunctions: add_to_server {}", err);
err

View File

@@ -19,7 +19,6 @@ use std::sync::Mutex;
use daemon::ctrl_rog_bios::CtrlRogBios;
use std::convert::Into;
use std::convert::TryInto;
use zbus::fdo;
use zbus::Connection;
@@ -180,7 +179,7 @@ fn start_daemon() -> Result<(), Box<dyn Error>> {
});
object_server
.with(&"/org/asuslinux/Charge".try_into()?, |obj: &CtrlCharge| {
.with("/org/asuslinux/Charge", |obj: &CtrlCharge| {
let x = obj.limit();
obj.notify_charge(x as u8)
})

View File

@@ -4,7 +4,7 @@ use rog_types::error::GraphicsError;
use std::convert::From;
use std::fmt;
use crate::{ctrl_gfx::error::GfxError, session_manager::SessionError};
use crate::ctrl_gfx::error::GfxError;
#[derive(Debug)]
pub enum RogError {
@@ -28,7 +28,7 @@ pub enum RogError {
Initramfs(String),
Modprobe(String),
Command(String, std::io::Error),
Session(SessionError),
Zbus(zbus::Error),
}
impl fmt::Display for RogError {
@@ -55,7 +55,7 @@ impl fmt::Display for RogError {
RogError::Initramfs(detail) => write!(f, "Initiramfs error: {}", detail),
RogError::Modprobe(detail) => write!(f, "Modprobe error: {}", detail),
RogError::Command(func, error) => write!(f, "Command exec error: {}: {}", func, error),
RogError::Session(detail) => write!(f, "Session error: {}", detail),
RogError::Zbus(detail) => write!(f, "Zbus error: {}", detail),
}
}
}
@@ -82,8 +82,8 @@ impl From<GraphicsError> for RogError {
}
}
impl From<SessionError> for RogError {
fn from(err: SessionError) -> Self {
RogError::Session(err)
impl From<zbus::Error> for RogError {
fn from(err: zbus::Error) -> Self {
RogError::Zbus(err)
}
}
}

View File

@@ -34,21 +34,19 @@ pub fn match_laptop() -> Option<LaptopBase> {
let device_desc = device
.device_descriptor()
.expect("Couldn't get device descriptor");
if device_desc.vendor_id() == 0x0b05 {
if LAPTOP_DEVICES.contains(&device_desc.product_id()) {
let prod_str = format!("{:x?}", device_desc.product_id());
if device_desc.vendor_id() == 0x0b05 && LAPTOP_DEVICES.contains(&device_desc.product_id()) {
let prod_str = format!("{:x?}", device_desc.product_id());
if device_desc.product_id() == 0x1854 {
let mut laptop = laptop(prod_str, None);
if laptop.supported_modes.is_empty() {
laptop.supported_modes = vec![STATIC, BREATHING];
}
return Some(laptop);
if device_desc.product_id() == 0x1854 {
let mut laptop = laptop(prod_str, None);
if laptop.supported_modes.is_empty() {
laptop.supported_modes = vec![STATIC, BREATHING];
}
let laptop = laptop(prod_str, Some("02".to_owned()));
return Some(laptop);
}
let laptop = laptop(prod_str, Some("02".to_owned()));
return Some(laptop);
}
}
warn!(

View File

@@ -30,8 +30,6 @@ pub mod laptops;
/// Fetch all supported functions for the laptop
pub mod ctrl_supported;
pub mod session_manager;
mod error;
use crate::error::RogError;

View File

@@ -1,175 +0,0 @@
use log::{error, warn};
use serde_derive::{Deserialize, Serialize};
use std::process::Command;
#[derive(Debug, PartialEq)]
enum SessionType {
X11,
Wayland,
TTY,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct UserSession {
pub session: String,
pub uid: u32,
pub user: String,
pub seat: String,
pub tty: String,
}
#[derive(Debug)]
pub struct Session {
session_id: String,
session_type: SessionType,
}
pub fn are_gfx_sessions_alive(sessions: &[Session]) -> bool {
for session in sessions {
match is_gfx_alive(session) {
Ok(alive) => {
if alive {
return true;
}
}
Err(err) => warn!("Error checking sessions: {}", err),
}
}
false
}
pub fn get_sessions() -> Result<Vec<Session>, SessionError> {
// loginctl list-sessions --no-legend
let mut cmd = Command::new("loginctl");
cmd.arg("list-sessions");
cmd.arg("--output");
cmd.arg("json");
let mut sessions = Vec::new();
match cmd.output() {
Ok(output) => {
if !output.status.success() {
error!(
"Couldn't get sessions: {}",
String::from_utf8_lossy(&output.stderr)
);
} else if output.status.success() {
if let Ok(data) = serde_json::from_slice::<Vec<UserSession>>(&output.stdout) {
for s in &data {
if let Ok(t) = get_session_type(&s.session) {
sessions.push(Session {
session_id: s.session.to_owned(),
session_type: t,
})
}
}
return Ok(sessions);
}
}
}
Err(err) => error!("Couldn't get sessions: {}", err),
}
Err(SessionError::NoSessions)
}
fn is_gfx_alive(session: &Session) -> Result<bool, SessionError> {
if session.session_type == SessionType::TTY {
return Ok(false);
}
let session_id = session.session_id.to_owned();
let mut cmd = Command::new("loginctl");
cmd.arg("show-session");
cmd.arg(&session_id);
cmd.arg("--property");
cmd.arg("Type");
match cmd.output() {
Ok(output) => {
if !output.status.success() {
let msg = String::from_utf8_lossy(&output.stderr);
if msg.contains("No session") {
return Ok(false);
}
error!(
"Couldn't get session: {}",
String::from_utf8_lossy(&output.stderr)
);
} else if output.status.success() {
return Ok(true);
}
}
Err(err) => error!("Couldn't get session: {}", err),
}
Ok(false)
}
fn get_session_type(session_id: &str) -> Result<SessionType, SessionError> {
//loginctl show-session 2 --property Type
let mut cmd = Command::new("loginctl");
cmd.arg("show-session");
cmd.arg(session_id);
cmd.arg("--property");
cmd.arg("Type");
cmd.arg("--property");
cmd.arg("Class");
match cmd.output() {
Ok(output) => {
if !output.status.success() {
let msg = String::from_utf8_lossy(&output.stderr);
if msg.contains("No session") {
return Err(SessionError::NoSession(session_id.into()));
}
error!(
"Couldn't get session: {}",
String::from_utf8_lossy(&output.stderr)
);
} else if output.status.success() {
let what = String::from_utf8_lossy(&output.stdout);
let mut stype = SessionType::TTY;
let mut user = false;
for line in what.lines() {
if let Some(is_it) = line.split("=").last() {
match is_it.trim() {
"user" => user = true,
"wayland" => stype = SessionType::Wayland,
"x11" => stype = SessionType::X11,
"tty" => stype = SessionType::TTY,
_ => return Err(SessionError::NoSession(session_id.into())),
}
}
}
if user {
return Ok(stype);
}
}
}
Err(err) => error!("Couldn't get session: {}", err),
}
Err(SessionError::NoSession(session_id.into()))
}
use std::fmt;
#[derive(Debug)]
pub enum SessionError {
NoSession(String),
NoSessions,
Command(String, std::io::Error),
}
impl fmt::Display for SessionError {
// This trait requires `fmt` with this exact signature.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SessionError::NoSession(id) => write!(f, "Session {} not active", id),
SessionError::NoSessions => write!(f, "No active sessions"),
SessionError::Command(func, error) => {
write!(f, "Command exec error: {}: {}", func, error)
}
}
}
}
impl std::error::Error for SessionError {}