zwlr_screencopy v3 support

This commit is contained in:
galister
2026-01-09 18:47:45 +09:00
parent e6e1764b36
commit 5e77bab588
20 changed files with 578 additions and 192 deletions

View File

@@ -69,12 +69,7 @@
"CAPTURE_METHOD": "Wayland-Bildschirmaufnahme", "CAPTURE_METHOD": "Wayland-Bildschirmaufnahme",
"CAPTURE_METHOD_HELP": "Versuchen Sie, dies zu ändern, wenn Sie\nschwarze oder fehlerhafte Bildschirme erleben", "CAPTURE_METHOD_HELP": "Versuchen Sie, dies zu ändern, wenn Sie\nschwarze oder fehlerhafte Bildschirme erleben",
"KEYBOARD_MIDDLE_CLICK": "Keyboard-Mittelklick", "KEYBOARD_MIDDLE_CLICK": "Keyboard-Mittelklick",
"KEYBOARD_MIDDLE_CLICK_HELP": "Modifikator bei Eingabe mit violettem Laser", "KEYBOARD_MIDDLE_CLICK_HELP": "Modifikator bei Eingabe mit violettem Laser"
"OPTION": {
"PIPEWIRE_HELP": "Schnelle GPU-Aufnahme. Empfohlen",
"PW_FALLBACK_HELP": "Langsam. Versuchen Sie dies, falls PipeWire GPU nicht funktioniert.",
"SCREENCOPY_HELP": "Langsam. Funktioniert mit: Hyprland, Niri, River, Sway"
}
}, },
"HELLO": "Hallo!", "HELLO": "Hallo!",
"AUDIO": { "AUDIO": {

View File

@@ -67,9 +67,12 @@
"NOTIFICATIONS_SOUND_ENABLED": "Notification sounds", "NOTIFICATIONS_SOUND_ENABLED": "Notification sounds",
"OPAQUE_BACKGROUND": "Opaque background", "OPAQUE_BACKGROUND": "Opaque background",
"OPTION": { "OPTION": {
"PIPEWIRE_HELP": "Fast GPU capture. Recommended", "AUTO": "Automatic",
"PW_FALLBACK_HELP": "Slow. Try in case PipeWire GPU doesn't work", "AUTO_HELP": "ScreenCopy GPU if supported,\notherwise PipeWire GPU.",
"SCREENCOPY_HELP": "Slow. Works on: Hyprland, Niri, River, Sway" "PIPEWIRE_HELP": "Fast GPU capture,\nstandard on all desktops.",
"PW_FALLBACK_HELP": "Slow method with high CPU usage.\nTry in case PipeWire GPU doesn't work",
"SCREENCOPY_GPU_HELP": "Fast, no screen share popups.\nWorks on: Hyprland, Niri, River, Sway",
"SCREENCOPY_HELP": "Slow, no screen share popups.\nWorks on: Hyprland, Niri, River, Sway"
}, },
"POINTER_LERP_FACTOR": "Pointer smoothing", "POINTER_LERP_FACTOR": "Pointer smoothing",
"RESTART_SOFTWARE": "Restart software", "RESTART_SOFTWARE": "Restart software",

View File

@@ -69,12 +69,7 @@
"CAPTURE_METHOD": "Captura de pantalla de Wayland", "CAPTURE_METHOD": "Captura de pantalla de Wayland",
"CAPTURE_METHOD_HELP": "Intente cambiar esta opción si\nexperimenta pantallas negras o con fallos", "CAPTURE_METHOD_HELP": "Intente cambiar esta opción si\nexperimenta pantallas negras o con fallos",
"KEYBOARD_MIDDLE_CLICK": "Clic del botón central del teclado", "KEYBOARD_MIDDLE_CLICK": "Clic del botón central del teclado",
"KEYBOARD_MIDDLE_CLICK_HELP": "Modificador para usar al escribir\ncon láser púrpura", "KEYBOARD_MIDDLE_CLICK_HELP": "Modificador para usar al escribir\ncon láser púrpura"
"OPTION": {
"PIPEWIRE_HELP": "Captura de GPU rápida. Recomendado",
"PW_FALLBACK_HELP": "Lento. Intente si GPU de PipeWire no funciona",
"SCREENCOPY_HELP": "Lento. Funciona en: Hyprland, Niri, River, Sway"
}
}, },
"HELLO": "¡Hola!", "HELLO": "¡Hola!",
"AUDIO": { "AUDIO": {

View File

@@ -69,12 +69,7 @@
"CAPTURE_METHOD": "Waylandスクリーンキャプチャ", "CAPTURE_METHOD": "Waylandスクリーンキャプチャ",
"CAPTURE_METHOD_HELP": "画面が黒くなる、または乱れる場合は、\nこの設定を変更してみてください。", "CAPTURE_METHOD_HELP": "画面が黒くなる、または乱れる場合は、\nこの設定を変更してみてください。",
"KEYBOARD_MIDDLE_CLICK": "キーボードの中ボタンクリック", "KEYBOARD_MIDDLE_CLICK": "キーボードの中ボタンクリック",
"KEYBOARD_MIDDLE_CLICK_HELP": "紫色のレーザーで入力する際の修飾キー", "KEYBOARD_MIDDLE_CLICK_HELP": "紫色のレーザーで入力する際の修飾キー"
"OPTION": {
"PIPEWIRE_HELP": "高速なGPUキャプチャ。推奨",
"PW_FALLBACK_HELP": "低速です。PipeWire GPU が動作しない場合に試してください。",
"SCREENCOPY_HELP": "遅い。動作する環境: Hyprland, Niri, River, Sway"
}
}, },
"HELLO": "こんにちは!", "HELLO": "こんにちは!",
"AUDIO": { "AUDIO": {

View File

@@ -63,12 +63,7 @@
"CAPTURE_METHOD": "Przechwytywanie ekranu Wayland", "CAPTURE_METHOD": "Przechwytywanie ekranu Wayland",
"CAPTURE_METHOD_HELP": "Spróbuj zmienić tę opcję, jeśli masz\nproblemy z czarnym lub migoczącym ekranem", "CAPTURE_METHOD_HELP": "Spróbuj zmienić tę opcję, jeśli masz\nproblemy z czarnym lub migoczącym ekranem",
"KEYBOARD_MIDDLE_CLICK": "Środkowy przycisk myszy na klawiaturze", "KEYBOARD_MIDDLE_CLICK": "Środkowy przycisk myszy na klawiaturze",
"KEYBOARD_MIDDLE_CLICK_HELP": "Modyfikator, który ma zostać użyty podczas pisania\nfioletową wiązką lasera", "KEYBOARD_MIDDLE_CLICK_HELP": "Modyfikator, który ma zostać użyty podczas pisania\nfioletową wiązką lasera"
"OPTION": {
"PIPEWIRE_HELP": "Szybkie przechwytywanie GPU. Zalecane",
"PW_FALLBACK_HELP": "Wolno. Wypróbuj w przypadku, gdy PipeWire GPU nie działa.",
"SCREENCOPY_HELP": "Wolne. Działa na: Hyprland, Niri, River, Sway"
}
}, },
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji", "APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
"APPLICATIONS": "Aplikacje", "APPLICATIONS": "Aplikacje",

View File

@@ -7,26 +7,26 @@ use std::{marker::PhantomData, slice::Iter, sync::Arc};
use cmd::{GfxCommandBuffer, XferCommandBuffer}; use cmd::{GfxCommandBuffer, XferCommandBuffer};
use pipeline::WGfxPipeline; use pipeline::WGfxPipeline;
use vulkano::{ use vulkano::{
DeviceSize,
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, IndexBuffer, Subbuffer}, buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, IndexBuffer, Subbuffer},
command_buffer::{ command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage,
allocator::{StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo}, allocator::{StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo},
AutoCommandBufferBuilder, CommandBufferUsage,
}, },
descriptor_set::allocator::{StandardDescriptorSetAllocator, StandardDescriptorSetAllocatorCreateInfo}, descriptor_set::allocator::{StandardDescriptorSetAllocator, StandardDescriptorSetAllocatorCreateInfo},
device::{Device, Queue}, device::{Device, Queue},
format::Format, format::Format,
image::{Image, ImageCreateInfo, ImageType, ImageUsage, sampler::Filter}, image::{sampler::Filter, Image, ImageCreateInfo, ImageType, ImageUsage},
instance::Instance, instance::Instance,
memory::{ memory::{
MemoryPropertyFlags,
allocator::{AllocationCreateInfo, GenericMemoryAllocatorCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, allocator::{AllocationCreateInfo, GenericMemoryAllocatorCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
ExternalMemoryHandleTypes, MemoryPropertyFlags,
}, },
pipeline::graphics::{ pipeline::graphics::{
color_blend::{AttachmentBlend, BlendFactor, BlendOp}, color_blend::{AttachmentBlend, BlendFactor, BlendOp},
vertex_input::Vertex, vertex_input::Vertex,
}, },
shader::ShaderModule, shader::ShaderModule,
DeviceSize,
}; };
use crate::gfx::pipeline::WPipelineCreateInfo; use crate::gfx::pipeline::WPipelineCreateInfo;
@@ -81,7 +81,7 @@ impl WGfx {
queue_xfer: Arc<Queue>, queue_xfer: Arc<Queue>,
surface_format: Format, surface_format: Format,
) -> Arc<Self> { ) -> Arc<Self> {
let memory_allocator = memory_allocator(device.clone()); let memory_allocator = memory_allocator(device.clone(), None);
let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new( let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
device.clone(), device.clone(),
StandardCommandBufferAllocatorCreateInfo { StandardCommandBufferAllocatorCreateInfo {
@@ -222,10 +222,14 @@ impl WGfx {
} }
} }
fn memory_allocator(device: Arc<Device>) -> Arc<StandardMemoryAllocator> { pub fn memory_allocator(
device: Arc<Device>,
export_handle_types: Option<ExternalMemoryHandleTypes>,
) -> Arc<StandardMemoryAllocator> {
let props = device.physical_device().memory_properties(); let props = device.physical_device().memory_properties();
let mut block_sizes = vec![0; props.memory_types.len()]; let mut block_sizes = vec![0; props.memory_types.len()];
let mut memory_type_bits = u32::MAX; let mut memory_type_bits = u32::MAX;
for (index, memory_type) in props.memory_types.iter().enumerate() { for (index, memory_type) in props.memory_types.iter().enumerate() {
@@ -252,9 +256,16 @@ fn memory_allocator(device: Arc<Device>) -> Arc<StandardMemoryAllocator> {
} }
} }
let export_handle_types = if let Some(val) = export_handle_types {
vec![val; props.memory_types.len()]
} else {
vec![]
};
let create_info = GenericMemoryAllocatorCreateInfo { let create_info = GenericMemoryAllocatorCreateInfo {
block_sizes: &block_sizes, block_sizes: &block_sizes,
memory_type_bits, memory_type_bits,
export_handle_types: &export_handle_types,
..Default::default() ..Default::default()
}; };

View File

@@ -16,6 +16,7 @@ pub enum WlxFrame {
Dmabuf(DmabufFrame), Dmabuf(DmabufFrame),
MemFd(MemFdFrame), MemFd(MemFdFrame),
MemPtr(MemPtrFrame), MemPtr(MemPtrFrame),
Implicit,
} }
#[derive(Debug, Clone, Copy, Default, PartialEq)] #[derive(Debug, Clone, Copy, Default, PartialEq)]

View File

@@ -29,6 +29,9 @@ use wayland_client::{
wl_shm::WlShm, wl_shm::WlShm,
}, },
}; };
use wayland_protocols::wp::linux_dmabuf::zv1::client::{
zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1, zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1,
};
use crate::frame; use crate::frame;
@@ -61,6 +64,7 @@ pub struct WlxClient {
pub xdg_output_mgr: ZxdgOutputManagerV1, pub xdg_output_mgr: ZxdgOutputManagerV1,
pub maybe_wlr_dmabuf_mgr: Option<ZwlrExportDmabufManagerV1>, pub maybe_wlr_dmabuf_mgr: Option<ZwlrExportDmabufManagerV1>,
pub maybe_wlr_screencopy_mgr: Option<ZwlrScreencopyManagerV1>, pub maybe_wlr_screencopy_mgr: Option<ZwlrScreencopyManagerV1>,
pub maybe_zwp_linux_dmabuf: Option<ZwpLinuxDmabufV1>,
pub wl_seat: WlSeat, pub wl_seat: WlSeat,
pub wl_shm: WlShm, pub wl_shm: WlShm,
pub outputs: IdMap<u32, WlxOutput>, pub outputs: IdMap<u32, WlxOutput>,
@@ -91,7 +95,8 @@ impl WlxClient {
.expect(WlSeat::interface().name), .expect(WlSeat::interface().name),
wl_shm: globals.bind(&qh, 1..=1, ()).expect(WlShm::interface().name), wl_shm: globals.bind(&qh, 1..=1, ()).expect(WlShm::interface().name),
maybe_wlr_dmabuf_mgr: globals.bind(&qh, 1..=1, ()).ok(), maybe_wlr_dmabuf_mgr: globals.bind(&qh, 1..=1, ()).ok(),
maybe_wlr_screencopy_mgr: globals.bind(&qh, 2..=2, ()).ok(), maybe_wlr_screencopy_mgr: globals.bind(&qh, 3..=3, ()).ok(),
maybe_zwp_linux_dmabuf: globals.bind(&qh, 4..=4, ()).ok(),
outputs: IdMap::new(), outputs: IdMap::new(),
queue: Arc::new(Mutex::new(queue)), queue: Arc::new(Mutex::new(queue)),
globals, globals,
@@ -441,3 +446,27 @@ impl Dispatch<WlShm, ()> for WlxClient {
) { ) {
} }
} }
impl Dispatch<ZwpLinuxDmabufV1, ()> for WlxClient {
fn event(
_state: &mut Self,
_proxy: &ZwpLinuxDmabufV1,
_event: <ZwpLinuxDmabufV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
}
}
impl Dispatch<ZwpLinuxBufferParamsV1, ()> for WlxClient {
fn event(
_state: &mut Self,
_proxy: &ZwpLinuxBufferParamsV1,
_event: <ZwpLinuxBufferParamsV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
}
}

View File

@@ -14,6 +14,7 @@ use wayland_client::{
Connection, Dispatch, Proxy, QueueHandle, WEnum, Connection, Dispatch, Proxy, QueueHandle, WEnum,
protocol::{wl_buffer::WlBuffer, wl_shm::Format, wl_shm_pool::WlShmPool}, protocol::{wl_buffer::WlBuffer, wl_shm::Format, wl_shm_pool::WlShmPool},
}; };
use wayland_protocols::wp::linux_dmabuf::zv1::client::zwp_linux_buffer_params_v1;
use smithay_client_toolkit::reexports::protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::{ZwlrScreencopyFrameV1, self}; use smithay_client_toolkit::reexports::protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::{ZwlrScreencopyFrameV1, self};
@@ -23,59 +24,82 @@ use crate::{
wayland::WlxClient, wayland::WlxClient,
}; };
struct BufData { pub trait DmaExporter {
wl_buffer: WlBuffer, fn next_frame(
wl_pool: WlShmPool, &mut self,
fd: RawFd, width: u32,
height: u32,
fourcc: DrmFourcc,
) -> Option<(FramePlane, DrmModifier)>;
}
enum BufData {
Shm {
wl_buffer: WlBuffer,
wl_pool: WlShmPool,
fd: RawFd,
},
Dma {
wl_buffer: WlBuffer,
},
} }
impl Drop for BufData { impl Drop for BufData {
fn drop(&mut self) { fn drop(&mut self) {
self.wl_buffer.destroy(); match self {
self.wl_pool.destroy(); Self::Shm {
unsafe { wl_buffer,
libc::close(self.fd); wl_pool,
fd,
..
} => {
wl_buffer.destroy();
wl_pool.destroy();
unsafe {
libc::close(*fd);
}
}
Self::Dma { wl_buffer } => {
wl_buffer.destroy();
}
} }
} }
} }
enum ScreenCopyEvent { enum ScreenCopyEvent {
Buffer { Buffer {
data: BufData, shm_format: Format,
drm_format: DrmFormat,
width: u32, width: u32,
height: u32, height: u32,
stride: u32, stride: u32,
}, },
DmaBuf {
format: DrmFourcc,
width: u32,
height: u32,
},
BuffersDone,
Ready, Ready,
Failed, Failed,
} }
struct CaptureData<U, R> struct CaptureData<U, R> {
where
U: Any,
R: Any,
{
sender: mpsc::SyncSender<R>, sender: mpsc::SyncSender<R>,
receiver: mpsc::Receiver<R>, receiver: mpsc::Receiver<R>,
user_data: U, user_data: Option<Box<U>>,
receive_callback: fn(&U, WlxFrame) -> Option<R>, receive_callback: fn(&U, WlxFrame) -> Option<R>,
} }
pub struct WlrScreencopyCapture<U, R> pub struct WlrScreencopyCapture<U, R> {
where
U: Any + Send,
R: Any + Send,
{
output_id: u32, output_id: u32,
wl: Option<Box<WlxClient>>, wl: Option<Box<WlxClient>>,
handle: Option<JoinHandle<Box<WlxClient>>>, handle: Option<JoinHandle<(Box<WlxClient>, Box<U>)>>,
data: Option<CaptureData<U, R>>, data: Option<CaptureData<U, R>>,
} }
impl<U, R> WlrScreencopyCapture<U, R> impl<U, R> WlrScreencopyCapture<U, R>
where where
U: Any + Send, U: Any + Send + DmaExporter,
R: Any + Send, R: Any + Send,
{ {
pub fn new(wl: WlxClient, output_id: u32) -> Self { pub fn new(wl: WlxClient, output_id: u32) -> Self {
@@ -90,7 +114,7 @@ where
impl<U, R> WlxCapture<U, R> for WlrScreencopyCapture<U, R> impl<U, R> WlxCapture<U, R> for WlrScreencopyCapture<U, R>
where where
U: Any + Send + Clone, U: Any + Send + DmaExporter,
R: Any + Send, R: Any + Send,
{ {
fn init( fn init(
@@ -105,7 +129,7 @@ where
self.data = Some(CaptureData { self.data = Some(CaptureData {
sender, sender,
receiver, receiver,
user_data, user_data: Some(Box::new(user_data)),
receive_callback, receive_callback,
}); });
} }
@@ -113,7 +137,7 @@ where
self.data.is_some() self.data.is_some()
} }
fn supports_dmbuf(&self) -> bool { fn supports_dmbuf(&self) -> bool {
false // screencopy v1 true // screencopy v3+
} }
fn receive(&mut self) -> Option<R> { fn receive(&mut self) -> Option<R> {
if let Some(data) = self.data.as_ref() { if let Some(data) = self.data.as_ref() {
@@ -135,7 +159,9 @@ where
if let Some(handle) = self.handle.take() { if let Some(handle) = self.handle.take() {
if handle.is_finished() { if handle.is_finished() {
wait_for_damage = true; wait_for_damage = true;
self.wl = Some(handle.join().unwrap()); // safe to unwrap because we checked is_finished let (wl, u) = handle.join().unwrap(); // safe to unwrap because is_finished
self.wl = Some(wl);
self.data.as_mut().unwrap().user_data = Some(u);
} else { } else {
self.handle = Some(handle); self.handle = Some(handle);
return; return;
@@ -148,12 +174,12 @@ where
let data = self let data = self
.data .data
.as_ref() .as_mut()
.expect("must call init once before request_new_frame"); .expect("must call init once before request_new_frame");
self.handle = Some(std::thread::spawn({ self.handle = Some(std::thread::spawn({
let sender = data.sender.clone(); let sender = data.sender.clone();
let user_data = data.user_data.clone(); let user_data = data.user_data.take().unwrap();
let receive_callback = data.receive_callback; let receive_callback = data.receive_callback;
let output_id = self.output_id; let output_id = self.output_id;
@@ -176,20 +202,20 @@ fn request_screencopy_frame<U, R>(
client: Box<WlxClient>, client: Box<WlxClient>,
output_id: u32, output_id: u32,
sender: SyncSender<R>, sender: SyncSender<R>,
user_data: U, mut user_data: Box<U>,
receive_callback: fn(&U, WlxFrame) -> Option<R>, receive_callback: fn(&U, WlxFrame) -> Option<R>,
wait_for_damage: bool, wait_for_damage: bool,
) -> Box<WlxClient> ) -> (Box<WlxClient>, Box<U>)
where where
U: Any + Send, U: Any + Send + DmaExporter,
R: Any + Send, R: Any + Send,
{ {
let Some(screencopy_manager) = client.maybe_wlr_screencopy_mgr.as_ref() else { let Some(screencopy_manager) = client.maybe_wlr_screencopy_mgr.as_ref() else {
return client; return (client, user_data);
}; };
let Some(output) = client.outputs.get(output_id) else { let Some(output) = client.outputs.get(output_id) else {
return client; return (client, user_data);
}; };
let transform = output.transform; let transform = output.transform;
@@ -206,44 +232,138 @@ where
let mut frame_buffer = None; let mut frame_buffer = None;
let mut maybe_buffer = None;
let mut maybe_dmabuf = None;
'receiver: loop { 'receiver: loop {
for event in rx.try_iter() { for event in rx.try_iter() {
match event { match event {
ScreenCopyEvent::Buffer { ScreenCopyEvent::Buffer { .. } => {
data, log::trace!("{name}: ScreenCopy Buffer event received");
drm_format, maybe_buffer = Some(event);
width, }
height, ScreenCopyEvent::DmaBuf { .. } => {
stride, log::trace!("{name}: ScreenCopy LinuxDmabuf event received");
} => { maybe_dmabuf = Some(event);
let frame = MemFdFrame { }
format: FrameFormat { ScreenCopyEvent::BuffersDone => {
log::trace!("{name}: ScreenCopy BuffersDone event received");
if let Some(zwp_linux_dmabuf) = client.maybe_zwp_linux_dmabuf.as_ref()
&& let Some(ScreenCopyEvent::DmaBuf {
format,
width, width,
height, height,
drm_format, }) = maybe_dmabuf
transform, && let Some((plane, modifier)) = user_data.next_frame(width, height, format)
}, {
plane: FramePlane { let mod_hi = (u64::from(modifier) >> 32) as _;
fd: Some(data.fd), let mod_lo = (u64::from(modifier) & 0xFFFFFFFF) as _;
offset: 0, let fd = unsafe { BorrowedFd::borrow_raw(plane.fd.unwrap()) };
stride: stride as _,
}, let params = zwp_linux_dmabuf.create_params(&client.queue_handle, ());
mouse: None, params.add(fd, 0, plane.offset, plane.stride as _, mod_hi, mod_lo);
};
log::trace!("{}: Received screencopy buffer, copying", name.as_ref()); let wl_buffer = params.create_immed(
if wait_for_damage { width as _,
proxy.copy_with_damage(&data.wl_buffer); height as _,
format as _,
zwp_linux_buffer_params_v1::Flags::empty(),
&client.queue_handle,
(),
);
log::trace!("{name}: ScreenCopy with Dmabuf");
// copy_with_damage seems to not work here
proxy.copy(&wl_buffer);
frame_buffer = Some((WlxFrame::Implicit, BufData::Dma { wl_buffer }));
} else if let Some(ScreenCopyEvent::Buffer {
shm_format,
width,
height,
stride,
}) = maybe_buffer
&& let Some(fourcc) = fourcc_from_wlshm(shm_format)
{
let fd_num = FD_COUNTER.fetch_add(1, Ordering::Relaxed);
let shm_name = CString::new(format!("wlx-{}", fd_num)).unwrap(); // safe
let size = stride * height;
let fd = unsafe {
let fd = libc::shm_open(
shm_name.as_ptr(),
O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR,
);
libc::shm_unlink(shm_name.as_ptr());
libc::ftruncate(fd, size as _);
fd
};
let borrowed_fd = unsafe { BorrowedFd::borrow_raw(fd) };
let wl_pool = client.wl_shm.create_pool(
borrowed_fd,
size as _,
&client.queue_handle,
(),
);
let wl_buffer = wl_pool.create_buffer(
0,
width as _,
height as _,
stride as _,
shm_format,
&client.queue_handle,
(),
);
log::trace!("{name}: ScreenCopy with SHM");
if wait_for_damage {
proxy.copy_with_damage(&wl_buffer);
} else {
proxy.copy(&wl_buffer);
}
let frame = MemFdFrame {
format: FrameFormat {
width,
height,
drm_format: DrmFormat {
code: fourcc,
modifier: DrmModifier::Invalid,
},
transform,
},
plane: FramePlane {
fd: Some(fd),
offset: 0,
stride: stride as _,
},
mouse: None,
};
frame_buffer = Some((
WlxFrame::MemFd(frame),
BufData::Shm {
wl_buffer,
wl_pool,
fd,
},
));
} else { } else {
proxy.copy(&data.wl_buffer); log::error!("{name}: No usable ScreenCopy buffers received.");
proxy.destroy();
break 'receiver;
} }
frame_buffer = Some((frame, data));
client.dispatch(); client.dispatch();
} }
ScreenCopyEvent::Ready => { ScreenCopyEvent::Ready => {
log::trace!("{}: Frame ready?", name.as_ref());
if let Some((frame, buffer)) = frame_buffer { if let Some((frame, buffer)) = frame_buffer {
if let Some(r) = receive_callback(&user_data, WlxFrame::MemFd(frame)) { if let Some(r) = receive_callback(&user_data, frame) {
let _ = sender.send(r); let _ = sender.send(r);
log::trace!("{}: Frame ready", name.as_ref()); log::trace!("{}: Frame ready!", name.as_ref());
} }
drop(buffer); drop(buffer);
} }
@@ -257,19 +377,19 @@ where
} }
} }
client (client, user_data)
} }
static FD_COUNTER: AtomicUsize = AtomicUsize::new(0); static FD_COUNTER: AtomicUsize = AtomicUsize::new(0);
impl Dispatch<ZwlrScreencopyFrameV1, SyncSender<ScreenCopyEvent>> for WlxClient { impl Dispatch<ZwlrScreencopyFrameV1, SyncSender<ScreenCopyEvent>> for WlxClient {
fn event( fn event(
state: &mut Self, _state: &mut Self,
proxy: &ZwlrScreencopyFrameV1, proxy: &ZwlrScreencopyFrameV1,
event: <ZwlrScreencopyFrameV1 as Proxy>::Event, event: <ZwlrScreencopyFrameV1 as Proxy>::Event,
data: &SyncSender<ScreenCopyEvent>, data: &SyncSender<ScreenCopyEvent>,
_conn: &Connection, _conn: &Connection,
qhandle: &QueueHandle<Self>, _qhandle: &QueueHandle<Self>,
) { ) {
match event { match event {
zwlr_screencopy_frame_v1::Event::Failed => { zwlr_screencopy_frame_v1::Event::Failed => {
@@ -289,58 +409,36 @@ impl Dispatch<ZwlrScreencopyFrameV1, SyncSender<ScreenCopyEvent>> for WlxClient
return; return;
}; };
let Some(fourcc) = fourcc_from_wlshm(shm_format) else {
log::warn!("Unsupported screencopy format");
let _ = data.send(ScreenCopyEvent::Failed);
proxy.destroy();
return;
};
let fd_num = FD_COUNTER.fetch_add(1, Ordering::Relaxed);
let name = CString::new(format!("wlx-{}", fd_num)).unwrap(); // safe
let size = stride * height;
let fd = unsafe {
let fd = libc::shm_open(name.as_ptr(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
libc::shm_unlink(name.as_ptr());
libc::ftruncate(fd, size as _);
fd
};
let borrowed_fd = unsafe { BorrowedFd::borrow_raw(fd) };
let wl_pool = state
.wl_shm
.create_pool(borrowed_fd, size as _, qhandle, ());
let wl_buffer = wl_pool.create_buffer(
0,
width as _,
height as _,
stride as _,
shm_format,
qhandle,
(),
);
let _ = data.send(ScreenCopyEvent::Buffer { let _ = data.send(ScreenCopyEvent::Buffer {
data: BufData {
wl_buffer,
wl_pool,
fd,
},
drm_format: DrmFormat {
code: fourcc,
modifier: DrmModifier::Invalid,
},
width, width,
height, height,
stride, stride,
shm_format,
}); });
} }
zwlr_screencopy_frame_v1::Event::Ready { .. } => { zwlr_screencopy_frame_v1::Event::Ready { .. } => {
let _ = data.send(ScreenCopyEvent::Ready); let _ = data.send(ScreenCopyEvent::Ready);
proxy.destroy(); proxy.destroy();
} }
zwlr_screencopy_frame_v1::Event::LinuxDmabuf {
format,
width,
height,
} => {
let Ok(format) = DrmFourcc::try_from(format) else {
log::warn!("{format} is not a known FourCC");
return;
};
let _ = data.send(ScreenCopyEvent::DmaBuf {
width,
height,
format,
});
}
zwlr_screencopy_frame_v1::Event::BufferDone => {
let _ = data.send(ScreenCopyEvent::BuffersDone);
}
_ => {} _ => {}
} }
} }

View File

@@ -18,17 +18,24 @@ pub type SerializedWindowStates = HashMap<Arc<str>, OverlayWindowState>;
#[derive(Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)] #[derive(Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)]
pub enum CaptureMethod { pub enum CaptureMethod {
#[default] #[default]
#[serde(alias = "pipewire", alias = "auto")] #[serde(alias = "auto")]
#[strum(props(Translation = "APP_SETTINGS.OPTION.AUTO", Tooltip = "APP_SETTINGS.OPTION.AUTO_HELP"))]
Auto,
#[serde(alias = "pipewire")]
#[strum(props(Text = "PipeWire GPU", Tooltip = "APP_SETTINGS.OPTION.PIPEWIRE_HELP"))] #[strum(props(Text = "PipeWire GPU", Tooltip = "APP_SETTINGS.OPTION.PIPEWIRE_HELP"))]
PipeWire, PipeWire,
#[strum(props(Text = "ScreenCopy GPU", Tooltip = "APP_SETTINGS.OPTION.SCREENCOPY_GPU_HELP"))]
ScreenCopyGpu,
#[serde(alias = "pw-fallback")] #[serde(alias = "pw-fallback")]
#[strum(props(Text = "PipeWire CPU", Tooltip = "APP_SETTINGS.OPTION.PW_FALLBACK_HELP"))] #[strum(props(Text = "PipeWire CPU", Tooltip = "APP_SETTINGS.OPTION.PW_FALLBACK_HELP"))]
PwFallback, PipeWireCpu,
#[serde(alias = "screencopy")] #[serde(alias = "screencopy")]
#[strum(props(Text = "ScreenCopy CPU", Tooltip = "APP_SETTINGS.OPTION.SCREENCOPY_HELP"))] #[strum(props(Text = "ScreenCopy CPU", Tooltip = "APP_SETTINGS.OPTION.SCREENCOPY_HELP"))]
ScreenCopy, ScreenCopyCpu,
} }
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)] #[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)]

View File

@@ -178,7 +178,7 @@ impl WvrServerState {
// this will throw a compile-time error if smithay's drm-fourcc is out of sync with wlx-capture's // this will throw a compile-time error if smithay's drm-fourcc is out of sync with wlx-capture's
let mut formats: Vec<smithay::backend::allocator::Format> = vec![]; let mut formats: Vec<smithay::backend::allocator::Format> = vec![];
for f in &gfx_extras.drm_formats { for f in &*gfx_extras.drm_formats {
formats.push(*f); formats.push(*f);
} }

View File

@@ -10,11 +10,14 @@ use vulkano::{
VulkanError, VulkanObject, VulkanError, VulkanObject,
device::Device, device::Device,
format::Format, format::Format,
image::{Image, ImageCreateInfo, ImageTiling, ImageUsage, SubresourceLayout, sys::RawImage}, image::{
Image, ImageCreateInfo, ImageMemory, ImageTiling, ImageType, ImageUsage, SubresourceLayout,
sys::RawImage, view::ImageView,
},
memory::{ memory::{
DedicatedAllocation, DeviceMemory, ExternalMemoryHandleType, ExternalMemoryHandleTypes, DedicatedAllocation, DeviceMemory, ExternalMemoryHandleType, ExternalMemoryHandleTypes,
MemoryAllocateInfo, MemoryImportInfo, MemoryPropertyFlags, ResourceMemory, MemoryAllocateInfo, MemoryImportInfo, MemoryPropertyFlags, ResourceMemory,
allocator::{MemoryAllocator, MemoryTypeFilter}, allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter},
}, },
sync::Sharing, sync::Sharing,
}; };
@@ -299,6 +302,70 @@ pub(super) unsafe fn create_dmabuf_image(
} }
} }
pub struct ExportedDmabufImage {
pub view: Arc<ImageView>,
pub fd: std::fs::File,
pub offset: u32,
pub stride: i32,
pub modifier: DrmModifier,
}
pub fn export_dmabuf_image(
allocator: Arc<dyn MemoryAllocator>,
extent: [u32; 3],
format: Format,
modifier: DrmModifier,
) -> anyhow::Result<ExportedDmabufImage> {
let layout = SubresourceLayout {
offset: 0,
size: 0,
row_pitch: align_to(format.block_size() * (extent[0] as u64), 64),
array_pitch: None,
depth_pitch: None,
};
let image = Image::new(
allocator.clone(),
ImageCreateInfo {
image_type: ImageType::Dim2d,
format,
extent,
usage: ImageUsage::TRANSFER_DST | ImageUsage::TRANSFER_SRC | ImageUsage::SAMPLED,
tiling: ImageTiling::DrmFormatModifier,
drm_format_modifiers: vec![modifier.into()],
drm_format_modifier_plane_layouts: vec![layout],
external_memory_handle_types: ExternalMemoryHandleTypes::DMA_BUF,
..Default::default()
},
AllocationCreateInfo {
..Default::default()
},
)
.context("Could not create image to export")?;
let fd = match image.memory() {
ImageMemory::Normal(planes) if planes.len() == 1 => {
let plane = planes.first().unwrap();
plane
.device_memory()
.export_fd(ExternalMemoryHandleType::DmaBuf)?
}
_ => anyhow::bail!("Could not export DMA-buf: invalid ImageMemory"),
};
Ok(ExportedDmabufImage {
view: ImageView::new_default(image)?,
fd,
modifier: DrmModifier::from(modifier),
offset: layout.offset as _,
stride: layout.row_pitch as _,
})
}
fn align_to(value: u64, alignment: u64) -> u64 {
((value + alignment - 1) / alignment) * alignment
}
pub(super) fn get_drm_formats(device: Arc<Device>) -> Vec<DrmFormat> { pub(super) fn get_drm_formats(device: Arc<Device>) -> Vec<DrmFormat> {
let possible_formats = [ let possible_formats = [
DrmFourcc::Abgr8888, DrmFourcc::Abgr8888,

View File

@@ -69,7 +69,7 @@ pub const BLEND_ALPHA: AttachmentBlend = AttachmentBlend {
pub struct WGfxExtras { pub struct WGfxExtras {
pub shaders: HashMap<&'static str, Arc<ShaderModule>>, pub shaders: HashMap<&'static str, Arc<ShaderModule>>,
pub drm_formats: Vec<DrmFormat>, pub drm_formats: Arc<[DrmFormat]>,
pub queue_capture: Option<Arc<Queue>>, pub queue_capture: Option<Arc<Queue>>,
pub quad_verts: Vert2Buf, pub quad_verts: Vert2Buf,
pub fallback_image: Arc<ImageView>, pub fallback_image: Arc<ImageView>,
@@ -95,7 +95,7 @@ impl WGfxExtras {
let shader = frag_screen::load(gfx.device.clone())?; let shader = frag_screen::load(gfx.device.clone())?;
shaders.insert("frag_screen", shader); shaders.insert("frag_screen", shader);
let drm_formats = get_drm_formats(gfx.device.clone()); let drm_formats = get_drm_formats(gfx.device.clone()).into();
let vertices = [ let vertices = [
Vert2Uv { Vert2Uv {

View File

@@ -12,6 +12,7 @@ use crate::{
input::{HoverResult, PointerHit, PointerMode}, input::{HoverResult, PointerHit, PointerMode},
}, },
graphics::ExtentExt, graphics::ExtentExt,
overlays::screen::capture::MyFirstDmaExporter,
state::AppState, state::AppState,
subsystem::hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT, WheelDelta}, subsystem::hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT, WheelDelta},
windowing::backend::{ windowing::backend::{
@@ -41,8 +42,15 @@ fn set_next_move(millis_from_now: u64) {
); );
} }
pub(super) enum CaptureType {
PipeWire,
ScreenCopy,
Xshm,
}
pub struct ScreenBackend { pub struct ScreenBackend {
name: Arc<str>, name: Arc<str>,
capture_type: CaptureType,
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>, capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
pipeline: Option<ScreenPipeline>, pipeline: Option<ScreenPipeline>,
cur_frame: Option<WlxCaptureOut>, cur_frame: Option<WlxCaptureOut>,
@@ -61,10 +69,12 @@ impl ScreenBackend {
pub(super) fn new_raw( pub(super) fn new_raw(
name: Arc<str>, name: Arc<str>,
xr_backend: XrBackend, xr_backend: XrBackend,
capture_type: CaptureType,
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>, capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
) -> Self { ) -> Self {
Self { Self {
name, name,
capture_type,
capture, capture,
pipeline: None, pipeline: None,
cur_frame: None, cur_frame: None,
@@ -151,17 +161,17 @@ impl OverlayBackend for ScreenBackend {
let allow_dmabuf = !matches!( let allow_dmabuf = !matches!(
capture_method, capture_method,
CaptureMethod::PwFallback | CaptureMethod::ScreenCopy CaptureMethod::PipeWireCpu | CaptureMethod::ScreenCopyCpu
); );
let dmabuf_formats = if !supports_dmabuf { let (dmabuf_formats, dma_exporter) = if !supports_dmabuf {
log::info!("Capture method does not support DMA-buf"); log::info!("Capture method does not support DMA-buf");
if app.gfx_extras.queue_capture.is_none() { if app.gfx_extras.queue_capture.is_none() {
log::warn!( log::warn!(
"Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance." "Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance."
); );
} }
&Vec::new() ([].as_slice(), None)
} else if !allow_dmabuf { } else if !allow_dmabuf {
log::info!( log::info!(
"Not using DMA-buf capture due to {}", "Not using DMA-buf capture due to {}",
@@ -172,16 +182,25 @@ impl OverlayBackend for ScreenBackend {
"Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance." "Current GPU does not support multiple queues. Software capture will take place on the main thread. Expect degraded performance."
); );
} }
&Vec::new() ([].as_slice(), None)
} else { } else {
log::warn!( log::warn!(
"Using GPU capture. If you're having issues with screens, go to the Dashboard's Settings tab and switch 'Wayland capture method' to a CPU option!" "Using GPU capture. If you're having issues with screens, go to the Dashboard's Settings tab and switch 'Wayland capture method' to a CPU option!"
); );
&app.gfx_extras.drm_formats let dma_exporter = if matches!(self.capture_type, CaptureType::ScreenCopy) {
Some(MyFirstDmaExporter::new(
app.gfx.clone(),
app.gfx_extras.drm_formats.clone(),
))
} else {
None
};
(&*app.gfx_extras.drm_formats, dma_exporter)
}; };
let user_data = WlxCaptureIn::new(self.name.clone(), app); let user_data = WlxCaptureIn::new(self.name.clone(), app, dma_exporter);
self.capture self.capture
.init(dmabuf_formats, user_data, receive_callback); .init(dmabuf_formats, user_data, receive_callback);
self.capture.request_new_frame(); self.capture.request_new_frame();

View File

@@ -1,4 +1,8 @@
use std::{f32::consts::PI, sync::Arc}; use std::{
f32::consts::PI,
os::fd::AsRawFd,
sync::{Arc, OnceLock},
};
use glam::{Affine3A, Vec3}; use glam::{Affine3A, Vec3};
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
@@ -8,24 +12,30 @@ use vulkano::{
device::Queue, device::Queue,
format::Format, format::Format,
image::{Image, sampler::Filter, view::ImageView}, image::{Image, sampler::Filter, view::ImageView},
memory::{ExternalMemoryHandleTypes, allocator::MemoryAllocator},
pipeline::graphics::color_blend::AttachmentBlend, pipeline::graphics::color_blend::AttachmentBlend,
}; };
use wgui::gfx::{ use wgui::{
WGfx, gfx::{
cmd::WGfxClearMode, WGfx,
pass::WGfxPass, cmd::WGfxClearMode,
pipeline::{WGfxPipeline, WPipelineCreateInfo}, memory_allocator,
pass::WGfxPass,
pipeline::{WGfxPipeline, WPipelineCreateInfo},
},
log::LogErr,
}; };
use wlx_capture::{ use wlx_capture::{
DrmFormat, WlxCapture, DrmFormat, DrmFourcc, DrmModifier, WlxCapture,
frame::{self as wlx_frame, FrameFormat, MouseMeta, WlxFrame}, frame::{self as wlx_frame, FrameFormat, MouseMeta, WlxFrame},
wlr_screencopy::DmaExporter,
}; };
use wlx_common::{config::GeneralConfig, overlays::StereoMode}; use wlx_common::{config::GeneralConfig, overlays::StereoMode};
use crate::{ use crate::{
graphics::{ graphics::{
Vert2Uv, ExtentExt, Vert2Uv,
dmabuf::{WGfxDmabuf, fourcc_to_vk}, dmabuf::{ExportedDmabufImage, WGfxDmabuf, export_dmabuf_image, fourcc_to_vk},
upload_quad_vertices, upload_quad_vertices,
}, },
state::AppState, state::AppState,
@@ -343,15 +353,120 @@ fn stereo_mode_to_verts(stereo: StereoMode, array_index: usize) -> [Vert2Uv; 4]
} }
} }
#[derive(Clone)] static DMA_ALLOCATOR: OnceLock<Arc<dyn MemoryAllocator>> = OnceLock::new();
pub(super) struct MyFirstDmaExporter {
gfx: Arc<WGfx>,
drm_formats: Arc<[DrmFormat]>,
images: SmallVec<[ExportedDmabufImage; 2]>,
fourcc: DrmFourcc,
current: usize,
}
impl MyFirstDmaExporter {
pub(super) fn new(gfx: Arc<WGfx>, drm_formats: Arc<[DrmFormat]>) -> Self {
Self {
gfx,
drm_formats,
images: smallvec![],
fourcc: DrmFourcc::Argb8888,
current: 0,
}
}
fn get_current(&self) -> Option<(Arc<ImageView>, FrameFormat)> {
let image = self.images.get(self.current)?;
let extent = image.view.extent_u32arr();
Some((
image.view.clone(),
FrameFormat {
width: extent[0],
height: extent[1],
drm_format: DrmFormat {
code: self.fourcc,
modifier: image.modifier,
},
transform: wlx_frame::Transform::Undefined,
},
))
}
fn set_format(
&mut self,
width: u32,
height: u32,
fourcc: wlx_capture::DrmFourcc,
) -> Option<()> {
if let Some(image) = self.images.first() {
let extent = image.view.image().extent();
if self.fourcc == fourcc && extent[0] == width && extent[1] == height {
return Some(());
}
}
self.images.clear();
let Some(modifier) = self
.drm_formats
.iter()
.filter(|f| f.code == fourcc)
.map(|f| f.modifier)
.next()
else {
log::error!("Unsupported format requested: {fourcc}");
return None;
};
let format = fourcc_to_vk(fourcc)
.log_err("Could not export new dmabuf due to invalid format")
.ok()?;
let allocator = DMA_ALLOCATOR.get_or_init(|| {
memory_allocator(
self.gfx.device.clone(),
Some(ExternalMemoryHandleTypes::DMA_BUF),
)
});
for _ in 0..2 {
let image =
export_dmabuf_image(allocator.clone(), [width, height, 1], format, modifier)
.log_err("Could not export DMA-buf image")
.ok()?;
self.images.push(image);
}
Some(())
}
fn next_frame(&mut self) -> Option<(wlx_frame::FramePlane, DrmModifier)> {
self.current = 1 - self.current;
let image = self.images.get(self.current)?;
Some((
wlx_frame::FramePlane {
fd: Some(image.fd.as_raw_fd()),
offset: image.offset,
stride: image.stride,
},
image.modifier,
))
}
}
pub struct WlxCaptureIn { pub struct WlxCaptureIn {
name: Arc<str>, name: Arc<str>,
gfx: Arc<WGfx>, gfx: Arc<WGfx>,
queue: Arc<Queue>, queue: Arc<Queue>,
dma_exporter: Option<MyFirstDmaExporter>,
} }
impl WlxCaptureIn { impl WlxCaptureIn {
pub(super) fn new(name: Arc<str>, app: &AppState) -> Self { pub(super) fn new(
name: Arc<str>,
app: &AppState,
dma_exporter: Option<MyFirstDmaExporter>,
) -> Self {
Self { Self {
name, name,
gfx: app.gfx.clone(), gfx: app.gfx.clone(),
@@ -361,10 +476,24 @@ impl WlxCaptureIn {
.as_ref() .as_ref()
.unwrap_or_else(|| &app.gfx.queue_xfer) .unwrap_or_else(|| &app.gfx.queue_xfer)
.clone(), .clone(),
dma_exporter,
} }
} }
} }
impl DmaExporter for WlxCaptureIn {
fn next_frame(
&mut self,
width: u32,
height: u32,
fourcc: DrmFourcc,
) -> Option<(wlx_frame::FramePlane, DrmModifier)> {
let dma_exporter = self.dma_exporter.as_mut()?;
dma_exporter.set_format(width, height, fourcc)?;
dma_exporter.next_frame()
}
}
#[derive(Clone)] #[derive(Clone)]
pub(super) struct WlxCaptureOut { pub(super) struct WlxCaptureOut {
pub(super) image: Arc<ImageView>, pub(super) image: Arc<ImageView>,
@@ -501,6 +630,33 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
mouse: frame.mouse, mouse: frame.mouse,
}) })
} }
WlxFrame::Implicit => {
log::trace!("{}: New Implicit frame", me.name);
let Some((image, format)) = me.dma_exporter.as_ref().unwrap().get_current() else {
log::error!("{}: Implicit frame is missing!", me.name);
return None;
};
Some(WlxCaptureOut {
image,
format,
mouse: None,
})
}
}
}
/// DmaExporter is not used for SHM capture
pub(super) struct DummyDrmExporter;
impl DmaExporter for DummyDrmExporter {
fn next_frame(
&mut self,
_: u32,
_: u32,
_: DrmFourcc,
) -> Option<(wlx_frame::FramePlane, DrmModifier)> {
unreachable!()
} }
} }
@@ -508,7 +664,7 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
// In this case, receive_callback needs to run on the main thread // In this case, receive_callback needs to run on the main thread
pub(super) struct MainThreadWlxCapture<T> pub(super) struct MainThreadWlxCapture<T>
where where
T: WlxCapture<(), WlxFrame>, T: WlxCapture<DummyDrmExporter, WlxFrame>,
{ {
inner: T, inner: T,
data: Option<WlxCaptureIn>, data: Option<WlxCaptureIn>,
@@ -516,7 +672,7 @@ where
impl<T> MainThreadWlxCapture<T> impl<T> MainThreadWlxCapture<T>
where where
T: WlxCapture<(), WlxFrame>, T: WlxCapture<DummyDrmExporter, WlxFrame>,
{ {
pub const fn new(inner: T) -> Self { pub const fn new(inner: T) -> Self {
Self { inner, data: None } Self { inner, data: None }
@@ -525,7 +681,7 @@ where
impl<T> WlxCapture<WlxCaptureIn, WlxCaptureOut> for MainThreadWlxCapture<T> impl<T> WlxCapture<WlxCaptureIn, WlxCaptureOut> for MainThreadWlxCapture<T>
where where
T: WlxCapture<(), WlxFrame>, T: WlxCapture<DummyDrmExporter, WlxFrame>,
{ {
fn init( fn init(
&mut self, &mut self,
@@ -534,7 +690,8 @@ where
_: fn(&WlxCaptureIn, WlxFrame) -> Option<WlxCaptureOut>, _: fn(&WlxCaptureIn, WlxFrame) -> Option<WlxCaptureOut>,
) { ) {
self.data = Some(user_data); self.data = Some(user_data);
self.inner.init(dmabuf_formats, (), receive_callback_dummy); self.inner
.init(dmabuf_formats, DummyDrmExporter, receive_callback_dummy);
} }
fn is_ready(&self) -> bool { fn is_ready(&self) -> bool {
self.inner.is_ready() self.inner.is_ready()
@@ -559,7 +716,7 @@ where
} }
#[allow(clippy::trivially_copy_pass_by_ref, clippy::unnecessary_wraps)] #[allow(clippy::trivially_copy_pass_by_ref, clippy::unnecessary_wraps)]
const fn receive_callback_dummy(_: &(), frame: WlxFrame) -> Option<WlxFrame> { const fn receive_callback_dummy(_: &DummyDrmExporter, frame: WlxFrame) -> Option<WlxFrame> {
Some(frame) Some(frame)
} }

View File

@@ -22,7 +22,10 @@ use crate::{
input::{HoverResult, PointerHit}, input::{HoverResult, PointerHit},
task::{OverlayTask, TaskType, ToggleMode}, task::{OverlayTask, TaskType, ToggleMode},
}, },
overlays::screen::capture::{MainThreadWlxCapture, new_wlx_capture}, overlays::screen::{
backend::CaptureType,
capture::{MainThreadWlxCapture, new_wlx_capture},
},
state::{AppSession, AppState}, state::{AppSession, AppState},
subsystem::hid::WheelDelta, subsystem::hid::WheelDelta,
windowing::{ windowing::{
@@ -94,6 +97,7 @@ impl OverlayBackend for MirrorBackend {
self.renderer = Some(ScreenBackend::new_raw( self.renderer = Some(ScreenBackend::new_raw(
self.name.clone(), self.name.clone(),
app.xr_backend, app.xr_backend,
CaptureType::PipeWire,
capture, capture,
)); ));
app.tasks app.tasks

View File

@@ -25,6 +25,8 @@ impl ScreenBackend {
token: Option<&str>, token: Option<&str>,
app: &mut AppState, app: &mut AppState,
) -> anyhow::Result<(Self, Option<String> /* pipewire restore token */)> { ) -> anyhow::Result<(Self, Option<String> /* pipewire restore token */)> {
use crate::overlays::screen::backend::CaptureType;
let name = output.name.clone(); let name = output.name.clone();
let embed_mouse = !app.session.config.double_cursor_fix; let embed_mouse = !app.session.config.double_cursor_fix;
@@ -57,7 +59,12 @@ impl ScreenBackend {
PipewireCapture::new(name, node_id) PipewireCapture::new(name, node_id)
); );
Ok(( Ok((
Self::new_raw(output.name.clone(), app.xr_backend, capture), Self::new_raw(
output.name.clone(),
app.xr_backend,
CaptureType::PipeWire,
capture,
),
select_screen_result.restore_token, select_screen_result.restore_token,
)) ))
} }

View File

@@ -3,7 +3,6 @@ use wlx_capture::{
WlxCapture, WlxCapture,
frame::Transform, frame::Transform,
wayland::{WlxClient, WlxOutput}, wayland::{WlxClient, WlxOutput},
wlr_dmabuf::WlrDmabufCapture,
wlr_screencopy::WlrScreencopyCapture, wlr_screencopy::WlrScreencopyCapture,
}; };
use wlx_common::{ use wlx_common::{
@@ -12,7 +11,7 @@ use wlx_common::{
}; };
use crate::{ use crate::{
overlays::screen::create_screen_from_backend, overlays::screen::{backend::CaptureType, create_screen_from_backend},
state::{AppState, ScreenMeta}, state::{AppState, ScreenMeta},
}; };
@@ -24,22 +23,18 @@ use super::{
}; };
impl ScreenBackend { impl ScreenBackend {
pub fn new_wlr_dmabuf(output: &WlxOutput, app: &AppState) -> Option<Self> {
let client = WlxClient::new()?;
let capture = new_wlx_capture!(
app.gfx_extras.queue_capture,
WlrDmabufCapture::new(client, output.id)
);
Some(Self::new_raw(output.name.clone(), app.xr_backend, capture))
}
pub fn new_wlr_screencopy(output: &WlxOutput, app: &AppState) -> Option<Self> { pub fn new_wlr_screencopy(output: &WlxOutput, app: &AppState) -> Option<Self> {
let client = WlxClient::new()?; let client = WlxClient::new()?;
let capture = new_wlx_capture!( let capture = new_wlx_capture!(
app.gfx_extras.queue_capture, app.gfx_extras.queue_capture,
WlrScreencopyCapture::new(client, output.id) WlrScreencopyCapture::new(client, output.id)
); );
Some(Self::new_raw(output.name.clone(), app.xr_backend, capture)) Some(Self::new_raw(
output.name.clone(),
app.xr_backend,
CaptureType::ScreenCopy,
capture,
))
} }
} }
@@ -52,9 +47,12 @@ pub fn create_screen_renderer_wl(
) -> Option<ScreenBackend> { ) -> Option<ScreenBackend> {
let mut capture: Option<ScreenBackend> = None; let mut capture: Option<ScreenBackend> = None;
if matches!(app.session.config.capture_method, CaptureMethod::ScreenCopy) && has_wlr_screencopy if matches!(
app.session.config.capture_method,
CaptureMethod::ScreenCopyCpu | CaptureMethod::ScreenCopyGpu | CaptureMethod::Auto
) && has_wlr_screencopy
{ {
log::info!("{}: Using Wlr Screencopy Wl-SHM", &output.name); log::info!("{}: Using ScreenCopy capture", &output.name);
capture = ScreenBackend::new_wlr_screencopy(output, app); capture = ScreenBackend::new_wlr_screencopy(output, app);
} }

View File

@@ -8,7 +8,7 @@ use wlx_capture::{
}; };
use crate::{ use crate::{
overlays::screen::create_screen_from_backend, overlays::screen::{backend::CaptureType, create_screen_from_backend},
state::{AppState, ScreenMeta}, state::{AppState, ScreenMeta},
}; };
@@ -27,7 +27,12 @@ impl ScreenBackend {
app.gfx_extras.queue_capture, app.gfx_extras.queue_capture,
XshmCapture::new(screen.clone()) XshmCapture::new(screen.clone())
); );
Self::new_raw(screen.name.clone(), app.xr_backend, capture) Self::new_raw(
screen.name.clone(),
app.xr_backend,
CaptureType::Xshm,
capture,
)
} }
} }
@@ -97,6 +102,7 @@ pub fn create_screens_x11pw(app: &mut AppState) -> anyhow::Result<ScreenCreateDa
let mut backend = ScreenBackend::new_raw( let mut backend = ScreenBackend::new_raw(
m.name.clone(), m.name.clone(),
app.xr_backend, app.xr_backend,
CaptureType::PipeWire,
new_wlx_capture!( new_wlx_capture!(
app.gfx_extras.queue_capture, app.gfx_extras.queue_capture,
PipewireCapture::new(m.name.clone(), s.node_id) PipewireCapture::new(m.name.clone(), s.node_id)

View File

@@ -1,4 +1,3 @@
use chrono_tz::PST8PDT;
use glam::{Affine2, Affine3A, Quat, Vec2, Vec3, vec2, vec3}; use glam::{Affine2, Affine3A, Quat, Vec2, Vec3, vec2, vec3};
use smithay::{ use smithay::{
desktop::PopupManager, desktop::PopupManager,