Add voice api calls to steamosctl and proxy.

Added listing of screen reader locales.
Added listing of voices for a given locale.
Added getting and setting of voice property.
This commit is contained in:
Jeremy Whiting 2025-06-27 16:52:40 -06:00 committed by Jeremy Whiting
parent 02a15c9295
commit afa0eddf41
5 changed files with 82 additions and 30 deletions

View file

@ -359,6 +359,11 @@
-->
<property name="VoiceLocales" type="as" access="read"/>
<!--
Map of voice names per locale
-->
<property name="VoicesForLocale" type="a{sas}" access="read"/>
<!--
Trigger Action
@ -386,19 +391,6 @@
<arg type="t" name="timestamp" direction="in"/>
</method>
<!--
Get the voices for a given locale
Get a list of voices for a given locale
@locale: The locale to get the voices for. e.g. en-US
@voices: A list of voice names to present to the user
-->
<method name="GetVoicesForLocale">
<arg type="s" name="locale" direction="in"/>
<arg type="as" name="voices" direction="out"/>
</method>
</interface>
<!--

View file

@ -5,7 +5,7 @@
* SPDX-License-Identifier: MIT
*/
use anyhow::Result;
use anyhow::{anyhow, Result};
use clap::{ArgAction, Parser, Subcommand};
use itertools::Itertools;
use nix::time::{clock_gettime, ClockId};
@ -254,6 +254,24 @@ enum Commands {
mode: ScreenReaderMode,
},
/// Get screen reader known locales
GetScreenReaderLocales,
/// Get screen reader voices for given locale
GetScreenReaderVoicesForLocale {
/// Valid locales can be found using get-screen-reader-locales.
locale: String,
},
/// Get screen reader voice
GetScreenReaderVoice,
/// Set screen reader voice
SetScreenReaderVoice {
/// The voice name to use for screen reader. Valid voices can be found using get-screen-reader-voices-for-locale
voice: String,
},
/// Trigger screen reader action
TriggerScreenReaderAction {
/// Valid actions are
@ -638,6 +656,34 @@ async fn main() -> Result<()> {
.trigger_action(*action as u32, now.try_into()?)
.await?;
}
Commands::GetScreenReaderVoice => {
let proxy = ScreenReader0Proxy::new(&conn).await?;
let voice = proxy.voice().await?;
println!("Voice: {voice}");
}
Commands::SetScreenReaderVoice { voice } => {
let proxy = ScreenReader0Proxy::new(&conn).await?;
proxy.set_voice(voice).await?;
}
Commands::GetScreenReaderLocales => {
let proxy = ScreenReader0Proxy::new(&conn).await?;
let locales = proxy.voice_locales().await?;
println!("Locales:\n");
for locale in locales.into_iter().sorted() {
println!("- {locale}");
}
}
Commands::GetScreenReaderVoicesForLocale { locale } => {
let proxy = ScreenReader0Proxy::new(&conn).await?;
let voice_list = proxy.voices_for_locale().await?;
let voices = voice_list
.get(locale)
.ok_or_else(|| anyhow!("Unable to load voices map"))?;
println!("Voices:\n");
for voice in voices.iter().sorted() {
println!("- {voice}");
}
}
}
Ok(())

View file

@ -734,6 +734,11 @@ impl ScreenReader0 {
self.screen_reader.get_voice_locales()
}
#[zbus(property)]
async fn voices_for_locale(&self) -> HashMap<String, Vec<String>> {
self.screen_reader.get_voices()
}
async fn trigger_action(&mut self, a: u32, timestamp: u64) -> fdo::Result<()> {
let action = match ScreenReaderAction::try_from(a) {
Ok(action) => action,
@ -744,12 +749,6 @@ impl ScreenReader0 {
.await
.map_err(to_zbus_fdo_error)
}
async fn get_voices(&self, locale: &str) -> fdo::Result<Vec<String>> {
self.screen_reader
.get_voices(locale)
.ok_or(fdo::Error::Failed(String::from("No voices found")))
}
}
#[interface(name = "com.steampowered.SteamOSManager1.Storage1")]

View file

@ -19,12 +19,21 @@ use zbus::proxy;
assume_defaults = true
)]
pub trait ScreenReader0 {
/// TriggerAction method
fn trigger_action(&self, action: u32, timestamp: u64) -> zbus::Result<()>;
/// Enabled property
#[zbus(property)]
fn enabled(&self) -> zbus::Result<bool>;
#[zbus(property)]
fn set_enabled(&self, value: bool) -> zbus::Result<()>;
/// Mode property
#[zbus(property)]
fn mode(&self) -> zbus::Result<u32>;
#[zbus(property)]
fn set_mode(&self, value: u32) -> zbus::Result<()>;
/// Pitch property
#[zbus(property)]
fn pitch(&self) -> zbus::Result<f64>;
@ -37,17 +46,23 @@ pub trait ScreenReader0 {
#[zbus(property)]
fn set_rate(&self, value: f64) -> zbus::Result<()>;
/// Voice property
#[zbus(property)]
fn voice(&self) -> zbus::Result<String>;
#[zbus(property)]
fn set_voice(&self, value: &str) -> zbus::Result<()>;
/// VoiceLocales property
#[zbus(property)]
fn voice_locales(&self) -> zbus::Result<Vec<String>>;
/// VoicesForLocale property
#[zbus(property)]
fn voices_for_locale(&self) -> zbus::Result<std::collections::HashMap<String, Vec<String>>>;
/// Volume property
#[zbus(property)]
fn volume(&self) -> zbus::Result<f64>;
#[zbus(property)]
fn set_volume(&self, value: f64) -> zbus::Result<()>;
/// Mode property
#[zbus(property)]
fn mode(&self) -> zbus::Result<u32>;
#[zbus(property)]
fn set_mode(&self, mode: u32) -> zbus::Result<()>;
fn trigger_action(&self, action: u32, timestamp: u64) -> zbus::Result<()>;
}

View file

@ -170,8 +170,8 @@ impl<'dbus> OrcaManager<'dbus> {
Ok(())
}
pub fn get_voices(&self, locale: &str) -> Option<Vec<String>> {
self.voices_by_language.get(locale).cloned()
pub fn get_voices(&self) -> HashMap<String, Vec<String>> {
self.voices_by_language.clone()
}
pub fn get_voice_locales(&self) -> Vec<String> {