use std::{error::Error, path::PathBuf, task, time::Instant}; use serde::{Deserialize, Serialize}; use wlx_capture::{ WlxCapture, pipewire::{PipewireCapture, PipewireSelectScreenResult}, wayland::WlxOutput, }; use crate::{ config::{PwTokenMap, def_pw_tokens}, config_io, state::AppState, }; use super::{ backend::ScreenBackend, capture::{MainThreadWlxCapture, new_wlx_capture}, }; #[cfg(feature = "wayland")] impl ScreenBackend { pub fn new_pw( output: &WlxOutput, token: Option<&str>, app: &AppState, ) -> anyhow::Result<(Self, Option /* pipewire restore token */)> { let name = output.name.clone(); let embed_mouse = !app.session.config.double_cursor_fix; let select_screen_result = select_pw_screen( &format!( "Now select: {} {} {} @ {},{}", &output.name, &output.make, &output.model, &output.logical_pos.0, &output.logical_pos.1 ), token, embed_mouse, true, true, false, )?; let node_id = select_screen_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element let capture = new_wlx_capture!( app.gfx_extras.queue_capture, PipewireCapture::new(name, node_id) ); Ok(( Self::new_raw(output.name.clone(), capture), select_screen_result.restore_token, )) } } #[allow(clippy::fn_params_excessive_bools)] pub(super) fn select_pw_screen( instructions: &str, token: Option<&str>, embed_mouse: bool, screens_only: bool, persist: bool, multiple: bool, ) -> Result { use crate::subsystem::notifications::DbusNotificationSender; use std::time::Duration; use wlx_capture::pipewire::pipewire_select_screen; let future = async move { let print_at = Instant::now() + Duration::from_millis(250); let mut notify = None; let f = pipewire_select_screen(token, embed_mouse, screens_only, persist, multiple); futures::pin_mut!(f); loop { match futures::poll!(&mut f) { task::Poll::Ready(result) => return result, task::Poll::Pending => { if Instant::now() >= print_at { log::info!("{instructions}"); if let Ok(sender) = DbusNotificationSender::new() && let Ok(id) = sender.notify_send(instructions, "", 2, 0, 0, true) { notify = Some((sender, id)); } break; } futures::future::lazy(|_| { std::thread::sleep(Duration::from_millis(10)); }) .await; } } } let result = f.await; if let Some((sender, id)) = notify { let _ = sender.notify_close(id); } result }; futures::executor::block_on(future) } #[derive(Deserialize, Serialize, Default)] pub struct TokenConf { #[serde(default = "def_pw_tokens")] pub pw_tokens: PwTokenMap, } fn get_pw_token_path() -> PathBuf { let mut path = config_io::ConfigRoot::Generic.get_conf_d_path(); path.push("pw_tokens.yaml"); path } pub fn save_pw_token_config(tokens: PwTokenMap) -> Result<(), Box> { let conf = TokenConf { pw_tokens: tokens }; let yaml = serde_yaml::to_string(&conf)?; std::fs::write(get_pw_token_path(), yaml)?; Ok(()) } pub fn load_pw_token_config() -> Result> { let yaml = std::fs::read_to_string(get_pw_token_path())?; let conf: TokenConf = serde_yaml::from_str(yaml.as_str())?; Ok(conf.pw_tokens) }