Skip to content

Commit

Permalink
[refactor]
Browse files Browse the repository at this point in the history
- Add error handling
- Add example/demo for wayshot dmabuf API
  • Loading branch information
CheerfulPianissimo committed Jun 24, 2024
1 parent 06e07cb commit e64d2e4
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 55 deletions.
204 changes: 204 additions & 0 deletions libwayshot/examples/waymirror.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
use libwayshot::WayshotConnection;
use wayland_client::{
delegate_noop,
protocol::{
wl_buffer::{self},
wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm, wl_shm_pool, wl_surface,
},
Connection, Dispatch, QueueHandle, WEnum,
};

use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};

fn main() {
let conn = Connection::connect_to_env().unwrap();

let mut event_queue = conn.new_event_queue();
let qhandle = event_queue.handle();

let display = conn.display();
display.get_registry(&qhandle, ());
let wayshot = WayshotConnection::from_connection_with_dmabuf(conn).unwrap();

let mut state = State {
wayshot,
running: true,
base_surface: None,
wm_base: None,
xdg_surface: None,
configured: false,
};

println!("Starting the example wayshot dmabuf demo app, press <ESC> to quit.");

while state.running {
event_queue.blocking_dispatch(&mut state).unwrap();
}
}

struct State {
wayshot: WayshotConnection,
running: bool,
base_surface: Option<wl_surface::WlSurface>,
wm_base: Option<xdg_wm_base::XdgWmBase>,
xdg_surface: Option<(xdg_surface::XdgSurface, xdg_toplevel::XdgToplevel)>,
configured: bool,
}

impl Dispatch<wl_registry::WlRegistry, ()> for State {
fn event(
state: &mut Self,
registry: &wl_registry::WlRegistry,
event: wl_registry::Event,
_: &(),
_: &Connection,
qh: &QueueHandle<Self>,
) {
if let wl_registry::Event::Global {
name, interface, ..
} = event
{
match &interface[..] {
"wl_compositor" => {
let compositor =
registry.bind::<wl_compositor::WlCompositor, _, _>(name, 1, qh, ());
let surface = compositor.create_surface(qh, ());
state.base_surface = Some(surface);

if state.wm_base.is_some() && state.xdg_surface.is_none() {
state.init_xdg_surface(qh);
}
}
"wl_seat" => {
registry.bind::<wl_seat::WlSeat, _, _>(name, 1, qh, ());
}
"xdg_wm_base" => {
let wm_base = registry.bind::<xdg_wm_base::XdgWmBase, _, _>(name, 1, qh, ());
state.wm_base = Some(wm_base);

if state.base_surface.is_some() && state.xdg_surface.is_none() {
state.init_xdg_surface(qh);
}
}
_ => {}
}
}
}
}

// Ignore events from these object types in this example.
delegate_noop!(State: ignore wl_compositor::WlCompositor);
delegate_noop!(State: ignore wl_surface::WlSurface);
delegate_noop!(State: ignore wl_shm::WlShm);
delegate_noop!(State: ignore wl_shm_pool::WlShmPool);
delegate_noop!(State: ignore wl_buffer::WlBuffer);

impl State {
fn init_xdg_surface(&mut self, qh: &QueueHandle<State>) {
let wm_base = self.wm_base.as_ref().unwrap();
let base_surface = self.base_surface.as_ref().unwrap();

let xdg_surface = wm_base.get_xdg_surface(base_surface, qh, ());
let toplevel = xdg_surface.get_toplevel(qh, ());
toplevel.set_title("DMABuf+wlr-screencpy example!".into());

base_surface.commit();

self.xdg_surface = Some((xdg_surface, toplevel));
}
}

impl Dispatch<xdg_wm_base::XdgWmBase, ()> for State {
fn event(
_: &mut Self,
wm_base: &xdg_wm_base::XdgWmBase,
event: xdg_wm_base::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let xdg_wm_base::Event::Ping { serial } = event {
wm_base.pong(serial);
}
}
}

impl Dispatch<xdg_surface::XdgSurface, ()> for State {
fn event(
state: &mut Self,
xdg_surface: &xdg_surface::XdgSurface,
event: xdg_surface::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let xdg_surface::Event::Configure { serial, .. } = event {
xdg_surface.ack_configure(serial);
state.configured = true;
let surface = state.base_surface.as_ref().unwrap();
let (_frame_format, guard, _bo) = state
.wayshot
.capture_output_frame_dmabuf(
true,
&state.wayshot.get_all_outputs()[0].wl_output,
None,
)
.unwrap();
surface.attach(Some(&guard.buffer), 0, 0);
surface.commit();
}
}
}

impl Dispatch<xdg_toplevel::XdgToplevel, ()> for State {
fn event(
state: &mut Self,
_: &xdg_toplevel::XdgToplevel,
event: xdg_toplevel::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let xdg_toplevel::Event::Close {} = event {
state.running = false;
}
}
}

impl Dispatch<wl_seat::WlSeat, ()> for State {
fn event(
_: &mut Self,
seat: &wl_seat::WlSeat,
event: wl_seat::Event,
_: &(),
_: &Connection,
qh: &QueueHandle<Self>,
) {
if let wl_seat::Event::Capabilities {
capabilities: WEnum::Value(capabilities),
} = event
{
if capabilities.contains(wl_seat::Capability::Keyboard) {
seat.get_keyboard(qh, ());
}
}
}
}

impl Dispatch<wl_keyboard::WlKeyboard, ()> for State {
fn event(
state: &mut Self,
_: &wl_keyboard::WlKeyboard,
event: wl_keyboard::Event,
_: &(),
_: &Connection,
_: &QueueHandle<Self>,
) {
if let wl_keyboard::Event::Key { key, .. } = event {
if key == 1 {
// ESC key
state.running = false;
}
}
}
}
10 changes: 10 additions & 0 deletions libwayshot/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::{io, result};

use drm::buffer::UnrecognizedFourcc;
use gbm::{DeviceDestroyedError, FdError};
use thiserror::Error;
use wayland_client::{
globals::{BindError, GlobalError},
Expand Down Expand Up @@ -34,4 +36,12 @@ pub enum Error {
ProtocolNotFound(String),
#[error("error occurred in freeze callback")]
FreezeCallbackError,
#[error("dmabuf configuration not initialized")]
NoDMAStateError,
#[error("dmabuf color format provided by compositor is invalid")]
UnrecognizedColorCode(#[from] UnrecognizedFourcc),
#[error("dmabuf device is destroyed")]
DRMDeviceLost(#[from] DeviceDestroyedError),
#[error("obtaining gbm buffer object file descriptor failed")]
GBMBoFdError(#[from] FdError),
}
68 changes: 13 additions & 55 deletions libwayshot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ impl WayshotConnection {
Ok((frame_format, frame_guard))
}

/// Obtain a screencapture in the form of a WlBuffer backed by a GBM Bufferobject on the GPU.
/// Uses the dma-buf provisions of the wlr-screencopy copy protocol to avoid VRAM->RAM copies
pub fn capture_output_frame_dmabuf(
&self,
cursor_overlay: bool,
Expand All @@ -262,16 +264,14 @@ impl WayshotConnection {
capture_region,
)?;
let gbm = &dmabuf_state.gbmdev;
let bo = gbm
.create_buffer_object::<()>(
frame_format.size.width,
frame_format.size.height,
gbm::Format::try_from(frame_format.format).unwrap(),
BufferObjectFlags::RENDERING | BufferObjectFlags::LINEAR,
)
.unwrap();
let stride = bo.stride().unwrap();
let modifier: u64 = bo.modifier().unwrap().into();
let bo = gbm.create_buffer_object::<()>(
frame_format.size.width,
frame_format.size.height,
gbm::Format::try_from(frame_format.format)?,
BufferObjectFlags::RENDERING | BufferObjectFlags::LINEAR,
)?;
let stride = bo.stride()?;
let modifier: u64 = bo.modifier()?.into();

let frame_guard = self.capture_output_frame_inner_dmabuf(
state,
Expand All @@ -280,12 +280,12 @@ impl WayshotConnection {
frame_format,
stride,
modifier,
bo.fd().unwrap(),
bo.fd()?,
)?;

Ok((frame_format, frame_guard, bo))
}
None => todo!(),
None => Err(Error::NoDMAStateError),
}
}

Expand Down Expand Up @@ -514,7 +514,7 @@ impl WayshotConnection {
event_queue.blocking_dispatch(&mut state)?;
}
}
None => todo!(),
None => Err(Error::NoDMAStateError),
}
}

Expand Down Expand Up @@ -572,48 +572,6 @@ impl WayshotConnection {
}
}

// /// Get a FrameCopy instance with screenshot pixel data for any wl_output object via dmabuf.
// #[tracing::instrument(skip_all, fields(output = format!("{output_info}"), region = capture_region.map(|r| format!("{:}", r)).unwrap_or("fullscreen".to_string())))]
// fn capture_frame_copy_dmabuf(
// &self,
// cursor_overlay: bool,
// output_info: &OutputInfo,
// capture_region: Option<EmbeddedRegion>,
// ) -> Result<(FrameCopy, FrameGuard)> {
// match &self.dmabuf_state {
// Some(dmabuf_state) => {
// let (frame_format, frame_guard, bo) = self.capture_output_frame_dmabuf_from_file(
// cursor_overlay,
// &output_info.wl_output,
// capture_region,
// )?;
// let rotated_physical_size = match output_info.transform {
// Transform::_90
// | Transform::_270
// | Transform::Flipped90
// | Transform::Flipped270 => Size {
// width: frame_format.size.height,
// height: frame_format.size.width,
// },
// _ => frame_format.size,
// };
// let frame_copy = FrameCopy {
// frame_format,
// frame_color_type,
// frame_data: FrameData::GBMBo(bo),
// transform: output_info.transform,
// logical_region: capture_region
// .map(|capture_region| capture_region.logical())
// .unwrap_or(output_info.logical_region),
// physical_size: rotated_physical_size,
// };
// tracing::debug!("Created frame copy: {:#?}", frame_copy);
// Ok((frame_copy, frame_guard))
// }
// None => todo!(),
// }
// }

/// Get a FrameCopy instance with screenshot pixel data for any wl_output object.
#[tracing::instrument(skip_all, fields(output = format!("{output_info}"), region = capture_region.map(|r| format!("{:}", r)).unwrap_or("fullscreen".to_string())))]
fn capture_frame_copy(
Expand Down

0 comments on commit e64d2e4

Please sign in to comment.