zwlr_screencopy v3 support
This commit is contained in:
@@ -69,12 +69,7 @@
|
||||
"CAPTURE_METHOD": "Wayland-Bildschirmaufnahme",
|
||||
"CAPTURE_METHOD_HELP": "Versuchen Sie, dies zu ändern, wenn Sie\nschwarze oder fehlerhafte Bildschirme erleben",
|
||||
"KEYBOARD_MIDDLE_CLICK": "Keyboard-Mittelklick",
|
||||
"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"
|
||||
}
|
||||
"KEYBOARD_MIDDLE_CLICK_HELP": "Modifikator bei Eingabe mit violettem Laser"
|
||||
},
|
||||
"HELLO": "Hallo!",
|
||||
"AUDIO": {
|
||||
@@ -146,4 +141,4 @@
|
||||
"LAUNCH": "Starten"
|
||||
},
|
||||
"DISPLAY_BRIGHTNESS": "Bildschirmhelligkeit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,12 @@
|
||||
"NOTIFICATIONS_SOUND_ENABLED": "Notification sounds",
|
||||
"OPAQUE_BACKGROUND": "Opaque background",
|
||||
"OPTION": {
|
||||
"PIPEWIRE_HELP": "Fast GPU capture. Recommended",
|
||||
"PW_FALLBACK_HELP": "Slow. Try in case PipeWire GPU doesn't work",
|
||||
"SCREENCOPY_HELP": "Slow. Works on: Hyprland, Niri, River, Sway"
|
||||
"AUTO": "Automatic",
|
||||
"AUTO_HELP": "ScreenCopy GPU if supported,\notherwise PipeWire GPU.",
|
||||
"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",
|
||||
"RESTART_SOFTWARE": "Restart software",
|
||||
|
||||
@@ -69,12 +69,7 @@
|
||||
"CAPTURE_METHOD": "Captura de pantalla de Wayland",
|
||||
"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_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"
|
||||
}
|
||||
"KEYBOARD_MIDDLE_CLICK_HELP": "Modificador para usar al escribir\ncon láser púrpura"
|
||||
},
|
||||
"HELLO": "¡Hola!",
|
||||
"AUDIO": {
|
||||
@@ -146,4 +141,4 @@
|
||||
"LAUNCH": "Iniciar"
|
||||
},
|
||||
"DISPLAY_BRIGHTNESS": "Brillo de la pantalla"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,12 +69,7 @@
|
||||
"CAPTURE_METHOD": "Waylandスクリーンキャプチャ",
|
||||
"CAPTURE_METHOD_HELP": "画面が黒くなる、または乱れる場合は、\nこの設定を変更してみてください。",
|
||||
"KEYBOARD_MIDDLE_CLICK": "キーボードの中ボタンクリック",
|
||||
"KEYBOARD_MIDDLE_CLICK_HELP": "紫色のレーザーで入力する際の修飾キー",
|
||||
"OPTION": {
|
||||
"PIPEWIRE_HELP": "高速なGPUキャプチャ。推奨",
|
||||
"PW_FALLBACK_HELP": "低速です。PipeWire GPU が動作しない場合に試してください。",
|
||||
"SCREENCOPY_HELP": "遅い。動作する環境: Hyprland, Niri, River, Sway"
|
||||
}
|
||||
"KEYBOARD_MIDDLE_CLICK_HELP": "紫色のレーザーで入力する際の修飾キー"
|
||||
},
|
||||
"HELLO": "こんにちは!",
|
||||
"AUDIO": {
|
||||
@@ -146,4 +141,4 @@
|
||||
"LAUNCH": "起動"
|
||||
},
|
||||
"DISPLAY_BRIGHTNESS": "ディスプレイの明るさ"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,12 +63,7 @@
|
||||
"CAPTURE_METHOD": "Przechwytywanie ekranu Wayland",
|
||||
"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_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"
|
||||
}
|
||||
"KEYBOARD_MIDDLE_CLICK_HELP": "Modyfikator, który ma zostać użyty podczas pisania\nfioletową wiązką lasera"
|
||||
},
|
||||
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
|
||||
"APPLICATIONS": "Aplikacje",
|
||||
@@ -146,4 +141,4 @@
|
||||
"LAUNCH": "Uruchom"
|
||||
},
|
||||
"DISPLAY_BRIGHTNESS": "Jasność wyświetlacza"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,26 +7,26 @@ use std::{marker::PhantomData, slice::Iter, sync::Arc};
|
||||
use cmd::{GfxCommandBuffer, XferCommandBuffer};
|
||||
use pipeline::WGfxPipeline;
|
||||
use vulkano::{
|
||||
DeviceSize,
|
||||
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, IndexBuffer, Subbuffer},
|
||||
command_buffer::{
|
||||
AutoCommandBufferBuilder, CommandBufferUsage,
|
||||
allocator::{StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo},
|
||||
AutoCommandBufferBuilder, CommandBufferUsage,
|
||||
},
|
||||
descriptor_set::allocator::{StandardDescriptorSetAllocator, StandardDescriptorSetAllocatorCreateInfo},
|
||||
device::{Device, Queue},
|
||||
format::Format,
|
||||
image::{Image, ImageCreateInfo, ImageType, ImageUsage, sampler::Filter},
|
||||
image::{sampler::Filter, Image, ImageCreateInfo, ImageType, ImageUsage},
|
||||
instance::Instance,
|
||||
memory::{
|
||||
MemoryPropertyFlags,
|
||||
allocator::{AllocationCreateInfo, GenericMemoryAllocatorCreateInfo, MemoryTypeFilter, StandardMemoryAllocator},
|
||||
ExternalMemoryHandleTypes, MemoryPropertyFlags,
|
||||
},
|
||||
pipeline::graphics::{
|
||||
color_blend::{AttachmentBlend, BlendFactor, BlendOp},
|
||||
vertex_input::Vertex,
|
||||
},
|
||||
shader::ShaderModule,
|
||||
DeviceSize,
|
||||
};
|
||||
|
||||
use crate::gfx::pipeline::WPipelineCreateInfo;
|
||||
@@ -81,7 +81,7 @@ impl WGfx {
|
||||
queue_xfer: Arc<Queue>,
|
||||
surface_format: Format,
|
||||
) -> 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(
|
||||
device.clone(),
|
||||
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 mut block_sizes = vec![0; props.memory_types.len()];
|
||||
|
||||
let mut memory_type_bits = u32::MAX;
|
||||
|
||||
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 {
|
||||
block_sizes: &block_sizes,
|
||||
memory_type_bits,
|
||||
export_handle_types: &export_handle_types,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ pub enum WlxFrame {
|
||||
Dmabuf(DmabufFrame),
|
||||
MemFd(MemFdFrame),
|
||||
MemPtr(MemPtrFrame),
|
||||
Implicit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq)]
|
||||
|
||||
@@ -29,6 +29,9 @@ use wayland_client::{
|
||||
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;
|
||||
|
||||
@@ -61,6 +64,7 @@ pub struct WlxClient {
|
||||
pub xdg_output_mgr: ZxdgOutputManagerV1,
|
||||
pub maybe_wlr_dmabuf_mgr: Option<ZwlrExportDmabufManagerV1>,
|
||||
pub maybe_wlr_screencopy_mgr: Option<ZwlrScreencopyManagerV1>,
|
||||
pub maybe_zwp_linux_dmabuf: Option<ZwpLinuxDmabufV1>,
|
||||
pub wl_seat: WlSeat,
|
||||
pub wl_shm: WlShm,
|
||||
pub outputs: IdMap<u32, WlxOutput>,
|
||||
@@ -91,7 +95,8 @@ impl WlxClient {
|
||||
.expect(WlSeat::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_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(),
|
||||
queue: Arc::new(Mutex::new(queue)),
|
||||
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>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use wayland_client::{
|
||||
Connection, Dispatch, Proxy, QueueHandle, WEnum,
|
||||
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};
|
||||
|
||||
@@ -23,59 +24,82 @@ use crate::{
|
||||
wayland::WlxClient,
|
||||
};
|
||||
|
||||
struct BufData {
|
||||
wl_buffer: WlBuffer,
|
||||
wl_pool: WlShmPool,
|
||||
fd: RawFd,
|
||||
pub trait DmaExporter {
|
||||
fn next_frame(
|
||||
&mut self,
|
||||
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 {
|
||||
fn drop(&mut self) {
|
||||
self.wl_buffer.destroy();
|
||||
self.wl_pool.destroy();
|
||||
unsafe {
|
||||
libc::close(self.fd);
|
||||
match self {
|
||||
Self::Shm {
|
||||
wl_buffer,
|
||||
wl_pool,
|
||||
fd,
|
||||
..
|
||||
} => {
|
||||
wl_buffer.destroy();
|
||||
wl_pool.destroy();
|
||||
unsafe {
|
||||
libc::close(*fd);
|
||||
}
|
||||
}
|
||||
Self::Dma { wl_buffer } => {
|
||||
wl_buffer.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ScreenCopyEvent {
|
||||
Buffer {
|
||||
data: BufData,
|
||||
drm_format: DrmFormat,
|
||||
shm_format: Format,
|
||||
width: u32,
|
||||
height: u32,
|
||||
stride: u32,
|
||||
},
|
||||
DmaBuf {
|
||||
format: DrmFourcc,
|
||||
width: u32,
|
||||
height: u32,
|
||||
},
|
||||
BuffersDone,
|
||||
Ready,
|
||||
Failed,
|
||||
}
|
||||
|
||||
struct CaptureData<U, R>
|
||||
where
|
||||
U: Any,
|
||||
R: Any,
|
||||
{
|
||||
struct CaptureData<U, R> {
|
||||
sender: mpsc::SyncSender<R>,
|
||||
receiver: mpsc::Receiver<R>,
|
||||
user_data: U,
|
||||
user_data: Option<Box<U>>,
|
||||
receive_callback: fn(&U, WlxFrame) -> Option<R>,
|
||||
}
|
||||
|
||||
pub struct WlrScreencopyCapture<U, R>
|
||||
where
|
||||
U: Any + Send,
|
||||
R: Any + Send,
|
||||
{
|
||||
pub struct WlrScreencopyCapture<U, R> {
|
||||
output_id: u32,
|
||||
wl: Option<Box<WlxClient>>,
|
||||
handle: Option<JoinHandle<Box<WlxClient>>>,
|
||||
handle: Option<JoinHandle<(Box<WlxClient>, Box<U>)>>,
|
||||
data: Option<CaptureData<U, R>>,
|
||||
}
|
||||
|
||||
impl<U, R> WlrScreencopyCapture<U, R>
|
||||
where
|
||||
U: Any + Send,
|
||||
U: Any + Send + DmaExporter,
|
||||
R: Any + Send,
|
||||
{
|
||||
pub fn new(wl: WlxClient, output_id: u32) -> Self {
|
||||
@@ -90,7 +114,7 @@ where
|
||||
|
||||
impl<U, R> WlxCapture<U, R> for WlrScreencopyCapture<U, R>
|
||||
where
|
||||
U: Any + Send + Clone,
|
||||
U: Any + Send + DmaExporter,
|
||||
R: Any + Send,
|
||||
{
|
||||
fn init(
|
||||
@@ -105,7 +129,7 @@ where
|
||||
self.data = Some(CaptureData {
|
||||
sender,
|
||||
receiver,
|
||||
user_data,
|
||||
user_data: Some(Box::new(user_data)),
|
||||
receive_callback,
|
||||
});
|
||||
}
|
||||
@@ -113,7 +137,7 @@ where
|
||||
self.data.is_some()
|
||||
}
|
||||
fn supports_dmbuf(&self) -> bool {
|
||||
false // screencopy v1
|
||||
true // screencopy v3+
|
||||
}
|
||||
fn receive(&mut self) -> Option<R> {
|
||||
if let Some(data) = self.data.as_ref() {
|
||||
@@ -135,7 +159,9 @@ where
|
||||
if let Some(handle) = self.handle.take() {
|
||||
if handle.is_finished() {
|
||||
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 {
|
||||
self.handle = Some(handle);
|
||||
return;
|
||||
@@ -148,12 +174,12 @@ where
|
||||
|
||||
let data = self
|
||||
.data
|
||||
.as_ref()
|
||||
.as_mut()
|
||||
.expect("must call init once before request_new_frame");
|
||||
|
||||
self.handle = Some(std::thread::spawn({
|
||||
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 output_id = self.output_id;
|
||||
@@ -176,20 +202,20 @@ fn request_screencopy_frame<U, R>(
|
||||
client: Box<WlxClient>,
|
||||
output_id: u32,
|
||||
sender: SyncSender<R>,
|
||||
user_data: U,
|
||||
mut user_data: Box<U>,
|
||||
receive_callback: fn(&U, WlxFrame) -> Option<R>,
|
||||
wait_for_damage: bool,
|
||||
) -> Box<WlxClient>
|
||||
) -> (Box<WlxClient>, Box<U>)
|
||||
where
|
||||
U: Any + Send,
|
||||
U: Any + Send + DmaExporter,
|
||||
R: Any + Send,
|
||||
{
|
||||
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 {
|
||||
return client;
|
||||
return (client, user_data);
|
||||
};
|
||||
|
||||
let transform = output.transform;
|
||||
@@ -206,44 +232,138 @@ where
|
||||
|
||||
let mut frame_buffer = None;
|
||||
|
||||
let mut maybe_buffer = None;
|
||||
let mut maybe_dmabuf = None;
|
||||
|
||||
'receiver: loop {
|
||||
for event in rx.try_iter() {
|
||||
match event {
|
||||
ScreenCopyEvent::Buffer {
|
||||
data,
|
||||
drm_format,
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
} => {
|
||||
let frame = MemFdFrame {
|
||||
format: FrameFormat {
|
||||
ScreenCopyEvent::Buffer { .. } => {
|
||||
log::trace!("{name}: ScreenCopy Buffer event received");
|
||||
maybe_buffer = Some(event);
|
||||
}
|
||||
ScreenCopyEvent::DmaBuf { .. } => {
|
||||
log::trace!("{name}: ScreenCopy LinuxDmabuf event received");
|
||||
maybe_dmabuf = Some(event);
|
||||
}
|
||||
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,
|
||||
height,
|
||||
drm_format,
|
||||
transform,
|
||||
},
|
||||
plane: FramePlane {
|
||||
fd: Some(data.fd),
|
||||
offset: 0,
|
||||
stride: stride as _,
|
||||
},
|
||||
mouse: None,
|
||||
};
|
||||
log::trace!("{}: Received screencopy buffer, copying", name.as_ref());
|
||||
if wait_for_damage {
|
||||
proxy.copy_with_damage(&data.wl_buffer);
|
||||
}) = maybe_dmabuf
|
||||
&& let Some((plane, modifier)) = user_data.next_frame(width, height, format)
|
||||
{
|
||||
let mod_hi = (u64::from(modifier) >> 32) as _;
|
||||
let mod_lo = (u64::from(modifier) & 0xFFFFFFFF) as _;
|
||||
let fd = unsafe { BorrowedFd::borrow_raw(plane.fd.unwrap()) };
|
||||
|
||||
let params = zwp_linux_dmabuf.create_params(&client.queue_handle, ());
|
||||
params.add(fd, 0, plane.offset, plane.stride as _, mod_hi, mod_lo);
|
||||
|
||||
let wl_buffer = params.create_immed(
|
||||
width as _,
|
||||
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 {
|
||||
proxy.copy(&data.wl_buffer);
|
||||
log::error!("{name}: No usable ScreenCopy buffers received.");
|
||||
proxy.destroy();
|
||||
break 'receiver;
|
||||
}
|
||||
frame_buffer = Some((frame, data));
|
||||
|
||||
client.dispatch();
|
||||
}
|
||||
ScreenCopyEvent::Ready => {
|
||||
log::trace!("{}: Frame ready?", name.as_ref());
|
||||
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);
|
||||
log::trace!("{}: Frame ready", name.as_ref());
|
||||
log::trace!("{}: Frame ready!", name.as_ref());
|
||||
}
|
||||
drop(buffer);
|
||||
}
|
||||
@@ -257,19 +377,19 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
client
|
||||
(client, user_data)
|
||||
}
|
||||
|
||||
static FD_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
impl Dispatch<ZwlrScreencopyFrameV1, SyncSender<ScreenCopyEvent>> for WlxClient {
|
||||
fn event(
|
||||
state: &mut Self,
|
||||
_state: &mut Self,
|
||||
proxy: &ZwlrScreencopyFrameV1,
|
||||
event: <ZwlrScreencopyFrameV1 as Proxy>::Event,
|
||||
data: &SyncSender<ScreenCopyEvent>,
|
||||
_conn: &Connection,
|
||||
qhandle: &QueueHandle<Self>,
|
||||
_qhandle: &QueueHandle<Self>,
|
||||
) {
|
||||
match event {
|
||||
zwlr_screencopy_frame_v1::Event::Failed => {
|
||||
@@ -289,58 +409,36 @@ impl Dispatch<ZwlrScreencopyFrameV1, SyncSender<ScreenCopyEvent>> for WlxClient
|
||||
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 {
|
||||
data: BufData {
|
||||
wl_buffer,
|
||||
wl_pool,
|
||||
fd,
|
||||
},
|
||||
drm_format: DrmFormat {
|
||||
code: fourcc,
|
||||
modifier: DrmModifier::Invalid,
|
||||
},
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
shm_format,
|
||||
});
|
||||
}
|
||||
zwlr_screencopy_frame_v1::Event::Ready { .. } => {
|
||||
let _ = data.send(ScreenCopyEvent::Ready);
|
||||
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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,24 @@ pub type SerializedWindowStates = HashMap<Arc<str>, OverlayWindowState>;
|
||||
#[derive(Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)]
|
||||
pub enum CaptureMethod {
|
||||
#[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"))]
|
||||
PipeWire,
|
||||
|
||||
#[strum(props(Text = "ScreenCopy GPU", Tooltip = "APP_SETTINGS.OPTION.SCREENCOPY_GPU_HELP"))]
|
||||
ScreenCopyGpu,
|
||||
|
||||
#[serde(alias = "pw-fallback")]
|
||||
#[strum(props(Text = "PipeWire CPU", Tooltip = "APP_SETTINGS.OPTION.PW_FALLBACK_HELP"))]
|
||||
PwFallback,
|
||||
PipeWireCpu,
|
||||
|
||||
#[serde(alias = "screencopy")]
|
||||
#[strum(props(Text = "ScreenCopy CPU", Tooltip = "APP_SETTINGS.OPTION.SCREENCOPY_HELP"))]
|
||||
ScreenCopy,
|
||||
ScreenCopyCpu,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, AsRefStr, EnumString, EnumProperty, VariantArray)]
|
||||
|
||||
@@ -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
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,14 @@ use vulkano::{
|
||||
VulkanError, VulkanObject,
|
||||
device::Device,
|
||||
format::Format,
|
||||
image::{Image, ImageCreateInfo, ImageTiling, ImageUsage, SubresourceLayout, sys::RawImage},
|
||||
image::{
|
||||
Image, ImageCreateInfo, ImageMemory, ImageTiling, ImageType, ImageUsage, SubresourceLayout,
|
||||
sys::RawImage, view::ImageView,
|
||||
},
|
||||
memory::{
|
||||
DedicatedAllocation, DeviceMemory, ExternalMemoryHandleType, ExternalMemoryHandleTypes,
|
||||
MemoryAllocateInfo, MemoryImportInfo, MemoryPropertyFlags, ResourceMemory,
|
||||
allocator::{MemoryAllocator, MemoryTypeFilter},
|
||||
allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter},
|
||||
},
|
||||
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> {
|
||||
let possible_formats = [
|
||||
DrmFourcc::Abgr8888,
|
||||
|
||||
@@ -69,7 +69,7 @@ pub const BLEND_ALPHA: AttachmentBlend = AttachmentBlend {
|
||||
|
||||
pub struct WGfxExtras {
|
||||
pub shaders: HashMap<&'static str, Arc<ShaderModule>>,
|
||||
pub drm_formats: Vec<DrmFormat>,
|
||||
pub drm_formats: Arc<[DrmFormat]>,
|
||||
pub queue_capture: Option<Arc<Queue>>,
|
||||
pub quad_verts: Vert2Buf,
|
||||
pub fallback_image: Arc<ImageView>,
|
||||
@@ -95,7 +95,7 @@ impl WGfxExtras {
|
||||
let shader = frag_screen::load(gfx.device.clone())?;
|
||||
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 = [
|
||||
Vert2Uv {
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::{
|
||||
input::{HoverResult, PointerHit, PointerMode},
|
||||
},
|
||||
graphics::ExtentExt,
|
||||
overlays::screen::capture::MyFirstDmaExporter,
|
||||
state::AppState,
|
||||
subsystem::hid::{MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT, WheelDelta},
|
||||
windowing::backend::{
|
||||
@@ -41,8 +42,15 @@ fn set_next_move(millis_from_now: u64) {
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) enum CaptureType {
|
||||
PipeWire,
|
||||
ScreenCopy,
|
||||
Xshm,
|
||||
}
|
||||
|
||||
pub struct ScreenBackend {
|
||||
name: Arc<str>,
|
||||
capture_type: CaptureType,
|
||||
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
|
||||
pipeline: Option<ScreenPipeline>,
|
||||
cur_frame: Option<WlxCaptureOut>,
|
||||
@@ -61,10 +69,12 @@ impl ScreenBackend {
|
||||
pub(super) fn new_raw(
|
||||
name: Arc<str>,
|
||||
xr_backend: XrBackend,
|
||||
capture_type: CaptureType,
|
||||
capture: Box<dyn WlxCapture<WlxCaptureIn, WlxCaptureOut>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
capture_type,
|
||||
capture,
|
||||
pipeline: None,
|
||||
cur_frame: None,
|
||||
@@ -151,17 +161,17 @@ impl OverlayBackend for ScreenBackend {
|
||||
|
||||
let allow_dmabuf = !matches!(
|
||||
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");
|
||||
if app.gfx_extras.queue_capture.is_none() {
|
||||
log::warn!(
|
||||
"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 {
|
||||
log::info!(
|
||||
"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."
|
||||
);
|
||||
}
|
||||
&Vec::new()
|
||||
([].as_slice(), None)
|
||||
} else {
|
||||
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!"
|
||||
);
|
||||
|
||||
&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
|
||||
.init(dmabuf_formats, user_data, receive_callback);
|
||||
self.capture.request_new_frame();
|
||||
|
||||
@@ -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 smallvec::{SmallVec, smallvec};
|
||||
@@ -8,24 +12,30 @@ use vulkano::{
|
||||
device::Queue,
|
||||
format::Format,
|
||||
image::{Image, sampler::Filter, view::ImageView},
|
||||
memory::{ExternalMemoryHandleTypes, allocator::MemoryAllocator},
|
||||
pipeline::graphics::color_blend::AttachmentBlend,
|
||||
};
|
||||
use wgui::gfx::{
|
||||
WGfx,
|
||||
cmd::WGfxClearMode,
|
||||
pass::WGfxPass,
|
||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||
use wgui::{
|
||||
gfx::{
|
||||
WGfx,
|
||||
cmd::WGfxClearMode,
|
||||
memory_allocator,
|
||||
pass::WGfxPass,
|
||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||
},
|
||||
log::LogErr,
|
||||
};
|
||||
use wlx_capture::{
|
||||
DrmFormat, WlxCapture,
|
||||
DrmFormat, DrmFourcc, DrmModifier, WlxCapture,
|
||||
frame::{self as wlx_frame, FrameFormat, MouseMeta, WlxFrame},
|
||||
wlr_screencopy::DmaExporter,
|
||||
};
|
||||
use wlx_common::{config::GeneralConfig, overlays::StereoMode};
|
||||
|
||||
use crate::{
|
||||
graphics::{
|
||||
Vert2Uv,
|
||||
dmabuf::{WGfxDmabuf, fourcc_to_vk},
|
||||
ExtentExt, Vert2Uv,
|
||||
dmabuf::{ExportedDmabufImage, WGfxDmabuf, export_dmabuf_image, fourcc_to_vk},
|
||||
upload_quad_vertices,
|
||||
},
|
||||
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 {
|
||||
name: Arc<str>,
|
||||
gfx: Arc<WGfx>,
|
||||
queue: Arc<Queue>,
|
||||
dma_exporter: Option<MyFirstDmaExporter>,
|
||||
}
|
||||
|
||||
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 {
|
||||
name,
|
||||
gfx: app.gfx.clone(),
|
||||
@@ -361,10 +476,24 @@ impl WlxCaptureIn {
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| &app.gfx.queue_xfer)
|
||||
.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)]
|
||||
pub(super) struct WlxCaptureOut {
|
||||
pub(super) image: Arc<ImageView>,
|
||||
@@ -501,6 +630,33 @@ pub(super) fn receive_callback(me: &WlxCaptureIn, frame: WlxFrame) -> Option<Wlx
|
||||
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
|
||||
pub(super) struct MainThreadWlxCapture<T>
|
||||
where
|
||||
T: WlxCapture<(), WlxFrame>,
|
||||
T: WlxCapture<DummyDrmExporter, WlxFrame>,
|
||||
{
|
||||
inner: T,
|
||||
data: Option<WlxCaptureIn>,
|
||||
@@ -516,7 +672,7 @@ where
|
||||
|
||||
impl<T> MainThreadWlxCapture<T>
|
||||
where
|
||||
T: WlxCapture<(), WlxFrame>,
|
||||
T: WlxCapture<DummyDrmExporter, WlxFrame>,
|
||||
{
|
||||
pub const fn new(inner: T) -> Self {
|
||||
Self { inner, data: None }
|
||||
@@ -525,7 +681,7 @@ where
|
||||
|
||||
impl<T> WlxCapture<WlxCaptureIn, WlxCaptureOut> for MainThreadWlxCapture<T>
|
||||
where
|
||||
T: WlxCapture<(), WlxFrame>,
|
||||
T: WlxCapture<DummyDrmExporter, WlxFrame>,
|
||||
{
|
||||
fn init(
|
||||
&mut self,
|
||||
@@ -534,7 +690,8 @@ where
|
||||
_: fn(&WlxCaptureIn, WlxFrame) -> Option<WlxCaptureOut>,
|
||||
) {
|
||||
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 {
|
||||
self.inner.is_ready()
|
||||
@@ -559,7 +716,7 @@ where
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,10 @@ use crate::{
|
||||
input::{HoverResult, PointerHit},
|
||||
task::{OverlayTask, TaskType, ToggleMode},
|
||||
},
|
||||
overlays::screen::capture::{MainThreadWlxCapture, new_wlx_capture},
|
||||
overlays::screen::{
|
||||
backend::CaptureType,
|
||||
capture::{MainThreadWlxCapture, new_wlx_capture},
|
||||
},
|
||||
state::{AppSession, AppState},
|
||||
subsystem::hid::WheelDelta,
|
||||
windowing::{
|
||||
@@ -94,6 +97,7 @@ impl OverlayBackend for MirrorBackend {
|
||||
self.renderer = Some(ScreenBackend::new_raw(
|
||||
self.name.clone(),
|
||||
app.xr_backend,
|
||||
CaptureType::PipeWire,
|
||||
capture,
|
||||
));
|
||||
app.tasks
|
||||
|
||||
@@ -25,6 +25,8 @@ impl ScreenBackend {
|
||||
token: Option<&str>,
|
||||
app: &mut AppState,
|
||||
) -> anyhow::Result<(Self, Option<String> /* pipewire restore token */)> {
|
||||
use crate::overlays::screen::backend::CaptureType;
|
||||
|
||||
let name = output.name.clone();
|
||||
let embed_mouse = !app.session.config.double_cursor_fix;
|
||||
|
||||
@@ -57,7 +59,12 @@ impl ScreenBackend {
|
||||
PipewireCapture::new(name, node_id)
|
||||
);
|
||||
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,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ use wlx_capture::{
|
||||
WlxCapture,
|
||||
frame::Transform,
|
||||
wayland::{WlxClient, WlxOutput},
|
||||
wlr_dmabuf::WlrDmabufCapture,
|
||||
wlr_screencopy::WlrScreencopyCapture,
|
||||
};
|
||||
use wlx_common::{
|
||||
@@ -12,7 +11,7 @@ use wlx_common::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
overlays::screen::create_screen_from_backend,
|
||||
overlays::screen::{backend::CaptureType, create_screen_from_backend},
|
||||
state::{AppState, ScreenMeta},
|
||||
};
|
||||
|
||||
@@ -24,22 +23,18 @@ use super::{
|
||||
};
|
||||
|
||||
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> {
|
||||
let client = WlxClient::new()?;
|
||||
let capture = new_wlx_capture!(
|
||||
app.gfx_extras.queue_capture,
|
||||
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> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use wlx_capture::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
overlays::screen::create_screen_from_backend,
|
||||
overlays::screen::{backend::CaptureType, create_screen_from_backend},
|
||||
state::{AppState, ScreenMeta},
|
||||
};
|
||||
|
||||
@@ -27,7 +27,12 @@ impl ScreenBackend {
|
||||
app.gfx_extras.queue_capture,
|
||||
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(
|
||||
m.name.clone(),
|
||||
app.xr_backend,
|
||||
CaptureType::PipeWire,
|
||||
new_wlx_capture!(
|
||||
app.gfx_extras.queue_capture,
|
||||
PipewireCapture::new(m.name.clone(), s.node_id)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use chrono_tz::PST8PDT;
|
||||
use glam::{Affine2, Affine3A, Quat, Vec2, Vec3, vec2, vec3};
|
||||
use smithay::{
|
||||
desktop::PopupManager,
|
||||
|
||||
Reference in New Issue
Block a user