refact: wayland, pipewire display offset cache to file (#13542)

Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
fufesou
2025-11-18 14:16:59 +08:00
committed by GitHub
parent b2dff336ce
commit 7f804a0e45

View File

@@ -21,8 +21,9 @@ use gstreamer::prelude::*;
use gstreamer_app::AppSink; use gstreamer_app::AppSink;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use hbb_common::{bail, config, platform::linux::CMD_SH, tokio, ResultType}; use hbb_common::{bail, config, platform::linux::CMD_SH, serde_json, tokio, ResultType};
use super::capturable::PixelProvider; use super::capturable::PixelProvider;
use super::capturable::{Capturable, Recorder}; use super::capturable::{Capturable, Recorder};
@@ -33,15 +34,9 @@ use super::screencast_portal::OrgFreedesktopPortalScreenCast as screencast_porta
lazy_static! { lazy_static! {
pub static ref RDP_SESSION_INFO: Mutex<Option<RdpSessionInfo>> = Mutex::new(None); pub static ref RDP_SESSION_INFO: Mutex<Option<RdpSessionInfo>> = Mutex::new(None);
// Maybe it's better to save this cache in config file?
// Because "--server" process may be restarted frequently, then the cache will be lost.
// But the users have to know where to find and delete the config file when they want to clear the cache,
// or we have to add a UI for that.
// For simplicity, we just keep it in memory for now.
static ref PIPEWIRE_DISPLAY_OFFSET_CACHE: Mutex<Option<PipewireDisplayOffsetCache>> =
Mutex::new(None);
} }
#[derive(Serialize, Deserialize)]
// For KDE Plasma only, because GNOME provides position info. // For KDE Plasma only, because GNOME provides position info.
struct PipewireDisplayOffsetCache { struct PipewireDisplayOffsetCache {
// We need to compare the displays, because: // We need to compare the displays, because:
@@ -313,24 +308,28 @@ impl PipeWireRecorder {
); );
pipeline.set_state(gst::State::Playing)?; pipeline.set_state(gst::State::Playing)?;
// Wait for the state change to actually complete before proceeding. // If `is_server_running()` is false, it means using remote_desktop_portal,
// The 2000ms timeout for pipeline state change was chosen based on empirical testing. // which does not use multiple streams, so no need to wait for state change.
let state_change = pipeline.get_state(gst::ClockTime::from_mseconds(2000)); if is_server_running() {
match state_change { // Wait for the state change to actually complete before proceeding.
(Ok(_), gst::State::Playing, _) => { // The 2000ms timeout for pipeline state change was chosen based on empirical testing.
debug!( let state_change = pipeline.get_state(gst::ClockTime::from_mseconds(2000));
"[gstreamer] Pipeline {} state confirmed as PLAYING.", match state_change {
capturable.fd.as_raw_fd() (Ok(_), gst::State::Playing, _) => {
); debug!(
} "[gstreamer] Pipeline {} state confirmed as PLAYING.",
(result, state, pending) => { capturable.fd.as_raw_fd()
warn!( );
}
(result, state, pending) => {
warn!(
"[gstreamer] Pipeline {} state change incomplete: result={:?}, state={:?}, pending={:?}", "[gstreamer] Pipeline {} state change incomplete: result={:?}, state={:?}, pending={:?}",
capturable.fd.as_raw_fd(), result, state, pending capturable.fd.as_raw_fd(), result, state, pending
); );
}
} }
std::thread::sleep(std::time::Duration::from_millis(150));
} }
std::thread::sleep(std::time::Duration::from_millis(150));
Ok(Self { Ok(Self {
pipeline, pipeline,
@@ -589,6 +588,7 @@ fn streams_from_response(response: OrgFreedesktopPortalRequestResponse) -> Vec<P
static mut INIT: bool = false; static mut INIT: bool = false;
const RESTORE_TOKEN: &str = "restore_token"; const RESTORE_TOKEN: &str = "restore_token";
const RESTORE_TOKEN_CONF_KEY: &str = "wayland-restore-token"; const RESTORE_TOKEN_CONF_KEY: &str = "wayland-restore-token";
const PIPEWIRE_DISPLAY_OFFSET_CONF_KEY: &str = "wayland-pipewire-display-offset";
pub fn get_available_cursor_modes() -> Result<u32, dbus::Error> { pub fn get_available_cursor_modes() -> Result<u32, dbus::Error> {
let conn = SyncConnection::new_session()?; let conn = SyncConnection::new_session()?;
@@ -1108,8 +1108,17 @@ fn try_fill_positions(
shared_displays: &mut Vec<crate::Display>, shared_displays: &mut Vec<crate::Display>,
streams: &mut Vec<PwStreamInfo>, streams: &mut Vec<PwStreamInfo>,
) -> ResultType<()> { ) -> ResultType<()> {
if try_fill_positions_from_cache(displays, shared_displays, streams) { let pipewire_display_offset = config::LocalConfig::get_option(PIPEWIRE_DISPLAY_OFFSET_CONF_KEY);
return Ok(()); if !pipewire_display_offset.is_empty() {
if try_fill_positions_from_cache(
pipewire_display_offset,
displays,
shared_displays,
streams,
) {
return Ok(());
}
config::LocalConfig::set_option(PIPEWIRE_DISPLAY_OFFSET_CONF_KEY.to_owned(), "".to_owned());
} }
let mut multi_matched_indices = Vec::new(); let mut multi_matched_indices = Vec::new();
@@ -1155,29 +1164,26 @@ fn try_fill_positions(
} }
fn try_fill_positions_from_cache( fn try_fill_positions_from_cache(
cache_str: String,
displays: &Arc<Displays>, displays: &Arc<Displays>,
shared_displays: &mut Vec<crate::Display>, shared_displays: &mut Vec<crate::Display>,
streams: &mut Vec<PwStreamInfo>, streams: &mut Vec<PwStreamInfo>,
) -> bool { ) -> bool {
let mut lock = PIPEWIRE_DISPLAY_OFFSET_CACHE.lock().unwrap(); let Ok(cache) = serde_json::from_str::<PipewireDisplayOffsetCache>(&cache_str) else {
let Some(cache) = lock.as_ref() else {
return false; return false;
}; };
if cache.offsets.len() != shared_displays.len() { if cache.offsets.len() != shared_displays.len() {
let _ = lock.take();
return false; return false;
} }
let display_key = PipewireDisplayOffsetCache::displays_to_key(displays); let display_key = PipewireDisplayOffsetCache::displays_to_key(displays);
if cache.display_key != display_key { if cache.display_key != display_key {
let _ = lock.take();
return false; return false;
} }
let restore_token = config::LocalConfig::get_option(RESTORE_TOKEN_CONF_KEY); let restore_token = config::LocalConfig::get_option(RESTORE_TOKEN_CONF_KEY);
if cache.restore_token != restore_token { if cache.restore_token != restore_token {
let _ = lock.take();
return false; return false;
} }
@@ -1216,7 +1222,9 @@ fn save_positions_to_cache(displays: &Arc<Displays>, shared_displays: &Vec<crate
offsets, offsets,
}; };
*PIPEWIRE_DISPLAY_OFFSET_CACHE.lock().unwrap() = Some(cache); if let Ok(s) = serde_json::to_string(&cache) {
config::LocalConfig::set_option(PIPEWIRE_DISPLAY_OFFSET_CONF_KEY.to_owned(), s);
}
} }
fn compare_left_up_corner(w: usize, d1: &[u8], d2: &[u8]) -> bool { fn compare_left_up_corner(w: usize, d1: &[u8], d2: &[u8]) -> bool {