mirror of https://github.com/wayvr-org/wayvr.git
Fix x11 pipewire screencast
This commit is contained in:
parent
cae2d7c7e2
commit
f47564eb65
|
|
@ -18,6 +18,9 @@ use wlx_capture::{
|
|||
};
|
||||
use wlx_common::{astr_containers::AStrMapExt, overlays::ToastTopic};
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
use wlx_capture::xshm::XshmScreen;
|
||||
|
||||
use crate::{
|
||||
backend::task::{OverlayTask, TaskType},
|
||||
gui::panel::{GuiPanel, NewGuiPanelParams},
|
||||
|
|
@ -112,6 +115,51 @@ impl ScreenCastBackend {
|
|||
finalize_fn,
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "x11")]
|
||||
pub fn new_x11pw(
|
||||
m: &XshmScreen,
|
||||
token: Option<Rc<str>>,
|
||||
app: &mut AppState,
|
||||
) -> anyhow::Result<Self> {
|
||||
use glam::vec2;
|
||||
|
||||
fn finalize_fn(
|
||||
name: Arc<str>,
|
||||
logical_pos: Vec2,
|
||||
logical_size: Vec2,
|
||||
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
|
||||
app: &mut AppState,
|
||||
) -> Box<dyn OverlayBackend> {
|
||||
use wlx_capture::frame::Transform;
|
||||
|
||||
let mut backend =
|
||||
ScreenBackend::new_raw(name, app.feats.xr_backend, CaptureType::PipeWire, capture);
|
||||
|
||||
backend.logical_pos = logical_pos;
|
||||
backend.logical_size = logical_size;
|
||||
backend.apply_mouse_transform_with_override(Transform::Undefined);
|
||||
|
||||
Box::new(backend)
|
||||
}
|
||||
|
||||
let params = ScreenCastParams {
|
||||
token,
|
||||
embed_mouse: true,
|
||||
screens_only: true,
|
||||
persist: true,
|
||||
allow_multiple: false,
|
||||
};
|
||||
|
||||
Self::new_raw(
|
||||
m.name.clone(),
|
||||
format!("{} {}", m.monitor.x(), m.monitor.y()).into(),
|
||||
vec2(m.monitor.x() as f32, m.monitor.y() as f32),
|
||||
vec2(m.monitor.width() as f32, m.monitor.height() as f32),
|
||||
params,
|
||||
app,
|
||||
finalize_fn,
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn new_raw(
|
||||
name: Arc<str>,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use wgui::log::LogErr;
|
||||
|
||||
use glam::{DVec2, vec2};
|
||||
use wlx_capture::{
|
||||
WlxCapture,
|
||||
frame::Transform,
|
||||
xshm::{XshmCapture, XshmScreen},
|
||||
};
|
||||
use wlx_common::{astr_containers::AStrMapExt, config::CaptureMethod};
|
||||
|
||||
#[cfg(feature = "pipewire")]
|
||||
use crate::state::save_pw_token_config;
|
||||
use crate::{
|
||||
overlays::screen::{backend::CaptureType, create_screen_from_backend},
|
||||
state::{AppState, ScreenMeta},
|
||||
|
|
@ -21,9 +22,7 @@ use super::{
|
|||
};
|
||||
|
||||
#[cfg(feature = "pipewire")]
|
||||
use wlx_capture::pipewire::{
|
||||
PipewireStream, ScreenCastParams, capture::PipewireCapture, screen_cast_select_blocking,
|
||||
};
|
||||
use crate::{overlays::screen::pw::ScreenCastBackend, windowing::backend::OverlayBackend};
|
||||
|
||||
impl ScreenBackend {
|
||||
pub fn new_xshm(screen: Arc<XshmScreen>, app: &AppState) -> Self {
|
||||
|
|
@ -40,45 +39,43 @@ impl ScreenBackend {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pipewire")]
|
||||
pub fn create_screen_renderer_x11pw(
|
||||
output: &XshmScreen,
|
||||
app: &mut AppState,
|
||||
) -> anyhow::Result<Box<dyn OverlayBackend>> {
|
||||
let display_name = &*output.name;
|
||||
|
||||
// Find existing token by display
|
||||
let token = app
|
||||
.session
|
||||
.pw_tokens
|
||||
.arc_get(display_name)
|
||||
.map(|x| x.to_string().into());
|
||||
|
||||
if token.is_some() {
|
||||
log::info!("Found existing Pipewire token for display {display_name}");
|
||||
}
|
||||
|
||||
Ok(Box::new(
|
||||
ScreenCastBackend::new_x11pw(output, token, app)
|
||||
.log_err("Failed to create screen with screen cast backend")?,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "pipewire")]
|
||||
pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateData> {
|
||||
use wlx_capture::xshm::xshm_get_monitors;
|
||||
use wlx_common::astr_containers::AStrMapExt;
|
||||
|
||||
use crate::{overlays::screen::create_screen_from_backend, state::ScreenMeta};
|
||||
|
||||
use super::ScreenCreateData;
|
||||
|
||||
let pw_tokens_copy = app.session.pw_tokens.clone();
|
||||
|
||||
let params = ScreenCastParams {
|
||||
token: app
|
||||
.session
|
||||
.pw_tokens
|
||||
.arc_get("x11")
|
||||
.map(|x| x.clone().into()),
|
||||
embed_mouse: !app.session.config.double_cursor_fix,
|
||||
allow_multiple: true,
|
||||
persist: true,
|
||||
screens_only: true,
|
||||
};
|
||||
|
||||
// this one still blocks
|
||||
let select_screen_result = screen_cast_select_blocking(params)?;
|
||||
|
||||
if let Some(restore_token) = select_screen_result.restore_token
|
||||
&& app
|
||||
.session
|
||||
.pw_tokens
|
||||
.arc_set("x11".into(), restore_token.clone())
|
||||
{
|
||||
log::info!("Adding Pipewire token {restore_token}");
|
||||
}
|
||||
if pw_tokens_copy != app.session.pw_tokens {
|
||||
// Token list changed, re-create token config file
|
||||
if let Err(err) = save_pw_token_config(app.session.pw_tokens.clone()) {
|
||||
log::error!("Failed to save Pipewire token config: {err}");
|
||||
}
|
||||
if !matches!(
|
||||
app.session.config.capture_method,
|
||||
CaptureMethod::PipeWireCpu | CaptureMethod::PipeWire
|
||||
) {
|
||||
anyhow::bail!("Pipewire is not selected as backend");
|
||||
}
|
||||
|
||||
let monitors = match xshm_get_monitors() {
|
||||
|
|
@ -87,49 +84,39 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
|
|||
anyhow::bail!(e.to_string());
|
||||
}
|
||||
};
|
||||
log::info!("Got {} monitors", monitors.len());
|
||||
log::info!("Got {} streams", select_screen_result.streams.len());
|
||||
|
||||
let mut extent = DVec2::ZERO;
|
||||
let screens = select_screen_result
|
||||
.streams
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| {
|
||||
let m = best_match(&s, monitors.iter().map(AsRef::as_ref)).unwrap();
|
||||
log::info!("Stream {i} is {}", m.name);
|
||||
extent.x = extent.x.max((m.monitor.x() + m.monitor.width()) as _);
|
||||
extent.y = extent.y.max((m.monitor.y() + m.monitor.height()) as _);
|
||||
let mut screens = vec![];
|
||||
for m in &monitors {
|
||||
if app.screens.iter().any(|s| s.name == m.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut backend = ScreenBackend::new_raw(
|
||||
m.name.clone(),
|
||||
app.feats.xr_backend,
|
||||
CaptureType::PipeWire,
|
||||
new_wlx_capture!(
|
||||
app.gfx_extras.queue_capture,
|
||||
PipewireCapture::new(m.name.clone(), s.node_id)
|
||||
),
|
||||
);
|
||||
log::info!(
|
||||
"{}: Init screen of res {}x{}, at {}x{}",
|
||||
m.name,
|
||||
m.monitor.width(),
|
||||
m.monitor.height(),
|
||||
m.monitor.x(),
|
||||
m.monitor.y(),
|
||||
);
|
||||
|
||||
backend.logical_pos = vec2(m.monitor.x() as f32, m.monitor.y() as f32);
|
||||
backend.logical_size = vec2(m.monitor.width() as f32, m.monitor.height() as f32);
|
||||
backend.apply_mouse_transform_with_override(Transform::Undefined);
|
||||
extent.x = extent.x.max((m.monitor.x() + m.monitor.width()) as _);
|
||||
extent.y = extent.y.max((m.monitor.y() + m.monitor.height()) as _);
|
||||
|
||||
let window_data = create_screen_from_backend(
|
||||
m.name.clone(),
|
||||
Transform::Normal,
|
||||
&app.session,
|
||||
Box::new(backend),
|
||||
);
|
||||
let backend = create_screen_renderer_x11pw(m, app)?;
|
||||
let window_config =
|
||||
create_screen_from_backend(m.name.clone(), Transform::Normal, &app.session, backend);
|
||||
|
||||
let meta = ScreenMeta {
|
||||
name: m.name.clone(),
|
||||
native_handle: 0,
|
||||
};
|
||||
let meta = ScreenMeta {
|
||||
name: m.name.clone(),
|
||||
native_handle: 0,
|
||||
};
|
||||
|
||||
(meta, window_data)
|
||||
})
|
||||
.collect();
|
||||
screens.push((meta, window_config));
|
||||
}
|
||||
|
||||
log::info!("Got {} monitors", monitors.len());
|
||||
|
||||
app.hid_provider.inner.set_desktop_extent(extent);
|
||||
app.hid_provider.inner.set_desktop_origin(DVec2::ZERO);
|
||||
|
|
@ -137,34 +124,6 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
|
|||
Ok(ScreenCreateData { screens })
|
||||
}
|
||||
|
||||
#[cfg(feature = "pipewire")]
|
||||
fn best_match<'a>(
|
||||
stream: &PipewireStream,
|
||||
mut streams: impl Iterator<Item = &'a XshmScreen>,
|
||||
) -> Option<&'a XshmScreen> {
|
||||
let mut best = streams.next();
|
||||
log::debug!("stream: {:?}", stream.position);
|
||||
log::debug!("first: {:?}", best.map(|b| &b.monitor));
|
||||
let Some(position) = stream.position else {
|
||||
return best;
|
||||
};
|
||||
|
||||
let mut best_dist = best.map_or(i32::MAX, |b| {
|
||||
(b.monitor.x() - position.0).abs() + (b.monitor.y() - position.1).abs()
|
||||
});
|
||||
for stream in streams {
|
||||
log::debug!("checking: {:?}", stream.monitor);
|
||||
let dist =
|
||||
(stream.monitor.x() - position.0).abs() + (stream.monitor.y() - position.1).abs();
|
||||
if dist < best_dist {
|
||||
best = Some(stream);
|
||||
best_dist = dist;
|
||||
}
|
||||
}
|
||||
log::debug!("best: {:?}", best.map(|b| &b.monitor));
|
||||
best
|
||||
}
|
||||
|
||||
pub fn create_screens_xshm(app: &mut AppState) -> anyhow::Result<ScreenCreateData> {
|
||||
use wlx_capture::xshm::xshm_get_monitors;
|
||||
|
||||
|
|
|
|||
|
|
@ -25,11 +25,7 @@ pub enum CaptureMethod {
|
|||
Auto,
|
||||
|
||||
#[serde(alias = "pipewire")]
|
||||
#[strum(props(
|
||||
Text = "PipeWire GPU",
|
||||
Tooltip = "APP_SETTINGS.OPTION.PIPEWIRE_HELP",
|
||||
Desktop = "Wayland"
|
||||
))]
|
||||
#[strum(props(Text = "PipeWire GPU", Tooltip = "APP_SETTINGS.OPTION.PIPEWIRE_HELP"))]
|
||||
PipeWire,
|
||||
|
||||
#[strum(props(
|
||||
|
|
@ -40,11 +36,7 @@ pub enum CaptureMethod {
|
|||
ScreenCopyGpu,
|
||||
|
||||
#[serde(alias = "pw-fallback")]
|
||||
#[strum(props(
|
||||
Text = "PipeWire CPU",
|
||||
Tooltip = "APP_SETTINGS.OPTION.PW_FALLBACK_HELP",
|
||||
Desktop = "Wayland"
|
||||
))]
|
||||
#[strum(props(Text = "PipeWire CPU", Tooltip = "APP_SETTINGS.OPTION.PW_FALLBACK_HELP"))]
|
||||
PipeWireCpu,
|
||||
|
||||
#[serde(alias = "screencopy")]
|
||||
|
|
|
|||
Loading…
Reference in New Issue