cec: Add module and interface for accessing/controlling state

This commit is contained in:
Vicki Pfau 2024-04-25 18:28:52 -07:00
parent bf521a7bbb
commit 1dcdfb2b23
5 changed files with 147 additions and 4 deletions

View file

@ -258,10 +258,21 @@
com.steampowered.SteamOSManager1.UserManager
@short_description: Interface to control various aspects of SteamOS running as a user.
Version available: 8
Version available: 8
-->
<interface name="com.steampowered.SteamOSManager1.UserManager">
<!--
HdmiCecState:
The current state of HDMI-CEC features on the system
Valid states: 0 = Disabled, 1 = Control Only, 2 = Control And Wake
Version available: 8
-->
<property name="HdmiCecState" type="u" access="readwrite"/>
<!--
Version:
@ -270,6 +281,8 @@
The manager may not support the latest version of the API.
Each method/property has an associated version number that
denotes in which interface version it first became available.
Version available: 8
-->
<property name="Version" type="u" access="read"/>

102
src/cec.rs Normal file
View file

@ -0,0 +1,102 @@
/*
* Copyright © 2023 Collabora Ltd.
* Copyright © 2024 Valve Software
* Copyright © 2024 Igalia S.L.
*
* SPDX-License-Identifier: MIT
*/
use anyhow::Result;
use std::fmt;
use zbus::Connection;
use crate::systemd::{daemon_reload, EnableState, SystemdUnit};
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum HdmiCecState {
Disabled = 0,
ControlOnly = 1,
ControlAndWake = 2,
}
impl TryFrom<u32> for HdmiCecState {
type Error = &'static str;
fn try_from(v: u32) -> Result<Self, Self::Error> {
match v {
x if x == HdmiCecState::Disabled as u32 => Ok(HdmiCecState::Disabled),
x if x == HdmiCecState::ControlOnly as u32 => Ok(HdmiCecState::ControlOnly),
x if x == HdmiCecState::ControlAndWake as u32 => Ok(HdmiCecState::ControlAndWake),
_ => Err("No enum match for value {v}"),
}
}
}
impl fmt::Display for HdmiCecState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
HdmiCecState::Disabled => write!(f, "Disabled"),
HdmiCecState::ControlOnly => write!(f, "ControlOnly"),
HdmiCecState::ControlAndWake => write!(f, "ControlAndWake"),
}
}
}
pub struct HdmiCecControl<'dbus> {
plasma_rc_unit: SystemdUnit<'dbus>,
wakehook_unit: SystemdUnit<'dbus>,
connection: Connection,
}
impl<'dbus> HdmiCecControl<'dbus> {
pub async fn new(connection: &Connection) -> Result<HdmiCecControl<'dbus>> {
Ok(HdmiCecControl {
plasma_rc_unit: SystemdUnit::new(
connection.clone(),
"plasma-remotecontrollers.service",
)
.await?,
wakehook_unit: SystemdUnit::new(connection.clone(), "wakehook.service").await?,
connection: connection.clone(),
})
}
pub async fn get_enabled_state(&self) -> Result<HdmiCecState> {
Ok(match self.plasma_rc_unit.enabled().await? {
EnableState::Enabled | EnableState::Static => {
match self.wakehook_unit.enabled().await? {
EnableState::Enabled | EnableState::Static => HdmiCecState::ControlAndWake,
_ => HdmiCecState::ControlOnly,
}
}
_ => HdmiCecState::Disabled,
})
}
pub async fn set_enabled_state(&self, state: HdmiCecState) -> Result<()> {
match state {
HdmiCecState::Disabled => {
self.plasma_rc_unit.mask().await?;
self.plasma_rc_unit.stop().await?;
self.wakehook_unit.mask().await?;
self.wakehook_unit.stop().await?;
daemon_reload(&self.connection).await?;
}
HdmiCecState::ControlOnly => {
self.wakehook_unit.mask().await?;
self.wakehook_unit.stop().await?;
self.plasma_rc_unit.unmask().await?;
daemon_reload(&self.connection).await?;
self.plasma_rc_unit.start().await?;
}
HdmiCecState::ControlAndWake => {
self.plasma_rc_unit.unmask().await?;
self.wakehook_unit.unmask().await?;
daemon_reload(&self.connection).await?;
self.plasma_rc_unit.start().await?;
self.wakehook_unit.start().await?;
}
};
Ok(())
}
}

View file

@ -15,6 +15,7 @@ use tokio::signal::unix::{signal, SignalKind};
use tokio_util::sync::CancellationToken;
use tracing::{info, warn};
mod cec;
mod daemon;
mod ds_inhibit;
mod hardware;

View file

@ -43,7 +43,7 @@ pub async fn daemon() -> Result<()> {
bail!(e);
}
};
let session = match create_connection().await {
let _session = match create_connection().await {
Ok(c) => c,
Err(e) => {
let _guard = tracing::subscriber::set_default(subscriber);

View file

@ -7,22 +7,49 @@
*/
use anyhow::Result;
use tracing::error;
use zbus::{interface, Connection};
use crate::API_VERSION;
use crate::{to_zbus_error, to_zbus_fdo_error, API_VERSION};
use crate::cec::{HdmiCecControl, HdmiCecState};
pub struct SteamOSManagerUser {
connection: Connection,
hdmi_cec: HdmiCecControl<'static>,
}
impl SteamOSManagerUser {
pub async fn new(connection: Connection) -> Result<Self> {
Ok(SteamOSManagerUser { connection })
Ok(SteamOSManagerUser {
hdmi_cec: HdmiCecControl::new(&connection).await?,
connection,
})
}
}
#[interface(name = "com.steampowered.SteamOSManager1.UserManager")]
impl SteamOSManagerUser {
#[zbus(property(emits_changed_signal = "false"))]
async fn hdmi_cec_state(&self) -> zbus::fdo::Result<u32> {
match self.hdmi_cec.get_enabled_state().await {
Ok(state) => Ok(state as u32),
Err(e) => Err(to_zbus_fdo_error(e)),
}
}
#[zbus(property)]
async fn set_hdmi_cec_state(&self, state: u32) -> zbus::Result<()> {
let state = match HdmiCecState::try_from(state) {
Ok(state) => state,
Err(err) => return Err(zbus::fdo::Error::InvalidArgs(err.to_string()).into()),
};
self.hdmi_cec
.set_enabled_state(state)
.await
.inspect_err(|message| error!("Error setting CEC state: {message}"))
.map_err(to_zbus_error)
}
#[zbus(property(emits_changed_signal = "const"))]
async fn version(&self) -> u32 {
API_VERSION