wayvr/wlx-overlay-s/src/overlays/screen/x11.rs

217 lines
6.4 KiB
Rust

use std::sync::Arc;
use glam::vec2;
use wlx_capture::{
WlxCapture,
frame::Transform,
xshm::{XshmCapture, XshmScreen},
};
use crate::{
overlays::screen::create_screen_from_backend,
state::{AppState, ScreenMeta},
};
use super::{
ScreenCreateData,
backend::ScreenBackend,
capture::{MainThreadWlxCapture, new_wlx_capture},
};
#[cfg(feature = "pipewire")]
use wlx_capture::pipewire::PipewireStream;
impl ScreenBackend {
pub fn new_xshm(screen: Arc<XshmScreen>, app: &AppState) -> Self {
let capture = new_wlx_capture!(
app.gfx_extras.queue_capture,
XshmCapture::new(screen.clone())
);
Self::new_raw(screen.name.clone(), capture)
}
}
#[cfg(feature = "pipewire")]
pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateData> {
use glam::vec2;
use wlx_capture::{pipewire::PipewireCapture, xshm::xshm_get_monitors};
use wlx_common::{astr_containers::AStrMapExt, config::PwTokenMap};
use crate::{
overlays::screen::{
create_screen_from_backend,
pw::{load_pw_token_config, save_pw_token_config, select_pw_screen},
},
state::ScreenMeta,
};
use super::ScreenCreateData;
// Load existing Pipewire tokens from file
let mut pw_tokens: PwTokenMap = load_pw_token_config().unwrap_or_default();
let pw_tokens_copy = pw_tokens.clone();
let token = pw_tokens.arc_get("x11").map(std::string::String::as_str);
let embed_mouse = !app.session.config.double_cursor_fix;
let select_screen_result = select_pw_screen(
"Select ALL screens on the screencast pop-up!",
token,
embed_mouse,
true,
true,
true,
)?;
if let Some(restore_token) = select_screen_result.restore_token
&& pw_tokens.arc_set("x11".into(), restore_token.clone())
{
log::info!("Adding Pipewire token {restore_token}");
}
if pw_tokens_copy != pw_tokens {
// Token list changed, re-create token config file
if let Err(err) = save_pw_token_config(pw_tokens) {
log::error!("Failed to save Pipewire token config: {err}");
}
}
let monitors = match xshm_get_monitors() {
Ok(m) => m,
Err(e) => {
anyhow::bail!(e.to_string());
}
};
log::info!("Got {} monitors", monitors.len());
log::info!("Got {} streams", select_screen_result.streams.len());
let mut extent = vec2(0., 0.);
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 f32);
extent.y = extent.y.max((m.monitor.y() + m.monitor.height()) as f32);
let mut backend = ScreenBackend::new_raw(
m.name.clone(),
new_wlx_capture!(
app.gfx_extras.queue_capture,
PipewireCapture::new(m.name.clone(), s.node_id)
),
);
backend.set_mouse_transform(
vec2(m.monitor.x() as f32, m.monitor.y() as f32),
vec2(m.monitor.width() as f32, m.monitor.height() as f32),
Transform::Normal,
);
let window_data = create_screen_from_backend(
m.name.clone(),
Transform::Normal,
&app.session,
Box::new(backend),
);
let meta = ScreenMeta {
name: m.name.clone(),
native_handle: 0,
};
(meta, window_data)
})
.collect();
app.hid_provider.inner.set_desktop_extent(extent);
app.hid_provider.inner.set_desktop_origin(vec2(0.0, 0.0));
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;
let mut extent = vec2(0., 0.);
let monitors = match xshm_get_monitors() {
Ok(m) => m,
Err(e) => {
anyhow::bail!(e.to_string());
}
};
let screens = monitors
.into_iter()
.map(|s| {
extent.x = extent.x.max((s.monitor.x() + s.monitor.width()) as f32);
extent.y = extent.y.max((s.monitor.y() + s.monitor.height()) as f32);
let size = (s.monitor.width(), s.monitor.height());
let pos = (s.monitor.x(), s.monitor.y());
let mut backend = ScreenBackend::new_xshm(s.clone(), app);
log::info!(
"{}: Init X11 screen of res {:?} at {:?}",
s.name.clone(),
size,
pos,
);
backend.set_mouse_transform(
vec2(s.monitor.x() as f32, s.monitor.y() as f32),
vec2(size.0 as f32, size.1 as f32),
Transform::Normal,
);
let window_data = create_screen_from_backend(
s.name.clone(),
Transform::Normal,
&app.session,
Box::new(backend),
);
let meta = ScreenMeta {
name: s.name.clone(),
native_handle: 0,
};
(meta, window_data)
})
.collect();
app.hid_provider.inner.set_desktop_extent(extent);
app.hid_provider.inner.set_desktop_origin(vec2(0.0, 0.0));
Ok(ScreenCreateData { screens })
}