1
0
Fork 0
forked from wry/wry

autocommit 2022-04-13 21:01:32 CEST

This commit is contained in:
Julian Orth 2022-04-13 21:01:32 +02:00
parent 661a28e5b0
commit 916e3644c3
30 changed files with 681 additions and 73 deletions

View file

@ -869,11 +869,11 @@ impl MetalBackend {
Ok(b) => b,
Err(e) => return Err(MetalError::ScanoutBuffer(e)),
};
let drm_fb = match dev.dev.master.add_fb(bo.dma()) {
let drm_fb = match dev.dev.master.add_fb(bo.dmabuf()) {
Ok(fb) => Rc::new(fb),
Err(e) => return Err(MetalError::Framebuffer(e)),
};
let egl_fb = match dev.dev.egl.dmabuf_fb(bo.dma()) {
let egl_fb = match dev.dev.egl.dmabuf_fb(bo.dmabuf()) {
Ok(fb) => fb,
Err(e) => return Err(MetalError::ImportFb(e)),
};

View file

@ -346,7 +346,7 @@ impl XBackendData {
let bo = self
.gbm
.create_bo(width, height, &format, GBM_BO_USE_RENDERING)?;
let dma = bo.dma();
let dma = bo.dmabuf();
assert!(dma.planes.len() == 1);
let plane = dma.planes.first().unwrap();
let size = plane.stride * dma.height as u32;

View file

@ -1,6 +1,7 @@
mod generate;
mod log;
mod quit;
mod screenshot;
mod set_log_level;
use {
@ -38,6 +39,19 @@ pub enum Cmd {
SetLogLevel(SetLogArgs),
/// Stop the compositor.
Quit,
/// Take a screenshot.
Screenshot(ScreenshotArgs),
}
#[derive(Args, Debug)]
pub struct ScreenshotArgs {
/// The filename of the saved screenshot
///
/// If no filename is given, the screenshot will be saved under jay-%Y-%m-%d-%H:%M:%S.qoi
/// in the current directory.
///
/// The filename can contain the usual strftime parameters.
pub filename: Option<String>,
}
#[derive(Args, Debug)]
@ -121,5 +135,6 @@ pub fn main() {
Cmd::Log(a) => log::main(cli.global, a),
Cmd::Quit => quit::main(cli.global),
Cmd::SetLogLevel(a) => set_log_level::main(cli.global, a),
Cmd::Screenshot(a) => screenshot::main(cli.global, a),
}
}

108
src/cli/screenshot.rs Normal file
View file

@ -0,0 +1,108 @@
use {
crate::{
cli::{GlobalArgs, ScreenshotArgs},
format::XRGB8888,
tools::tool_client::{Handle, ToolClient},
utils::{errorfmt::ErrorFmt, queue::AsyncQueue},
video::{
dmabuf::{DmaBuf, DmaBufPlane},
drm::Drm,
gbm::{GbmDevice, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
INVALID_MODIFIER,
},
wire::{
jay_compositor::TakeScreenshot,
jay_screenshot::{Dmabuf, Error},
},
},
chrono::Local,
qoi::xrgb8888_encode_qoi,
std::rc::Rc,
};
pub fn main(global: GlobalArgs, args: ScreenshotArgs) {
let tc = ToolClient::new(global.log_level.into());
let screenshot = Rc::new(Screenshot {
tc: tc.clone(),
args,
});
tc.run(run(screenshot));
}
struct Screenshot {
tc: Rc<ToolClient>,
args: ScreenshotArgs,
}
async fn run(screenshot: Rc<Screenshot>) {
let tc = &screenshot.tc;
let comp = tc.jay_compositor().await;
let sid = tc.id();
tc.send(TakeScreenshot {
self_id: comp,
id: sid,
});
let result = Rc::new(AsyncQueue::new());
Error::handle(&tc, sid, result.clone(), |res, err| {
res.push(Err(err.msg.to_owned()));
});
Dmabuf::handle(&tc, sid, result.clone(), |res, buf| {
res.push(Ok(buf));
});
let buf = match result.pop().await {
Ok(b) => b,
Err(e) => {
fatal!("Could not take a screenshot: {}", e);
}
};
let data = buf_to_qoi(&buf);
let filename = screenshot
.args
.filename
.as_deref()
.unwrap_or("jay-%Y-%m-%d-%H:%M:%S.qoi");
let filename = Local::now().format(filename).to_string();
if let Err(e) = std::fs::write(&filename, &data) {
fatal!("Could not write `{}`: {}", filename, ErrorFmt(e));
}
}
fn buf_to_qoi(buf: &Dmabuf) -> Vec<u8> {
let drm = match Drm::reopen(buf.drm_dev.raw(), false) {
Ok(drm) => drm,
Err(e) => {
fatal!("Could not open the drm device: {}", ErrorFmt(e));
}
};
let gbm = match GbmDevice::new(&drm) {
Ok(g) => g,
Err(e) => {
fatal!("Could not create a gbm device: {}", ErrorFmt(e));
}
};
let dmabuf = DmaBuf {
width: buf.width as _,
height: buf.height as _,
format: XRGB8888,
modifier: INVALID_MODIFIER,
planes: vec![DmaBufPlane {
offset: buf.offset,
stride: buf.stride,
fd: buf.fd.clone(),
}],
};
let bo = match gbm.import_dmabuf(&dmabuf, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING) {
Ok(bo) => Rc::new(bo),
Err(e) => {
fatal!("Could not import screenshot dmabuf: {}", ErrorFmt(e));
}
};
let bo_map = match bo.map() {
Ok(map) => map,
Err(e) => {
fatal!("Could not map dmabuf: {}", ErrorFmt(e));
}
};
let data = unsafe { bo_map.data() };
xrgb8888_encode_qoi(data, buf.width, buf.height, buf.stride)
}

View file

@ -1,4 +1,4 @@
pub use error::{ClientError, ObjectError};
pub use error::{ClientError, MethodError, ObjectError};
use {
crate::{
async_engine::{AsyncFd, SpawnedFuture},

View file

@ -57,6 +57,14 @@ impl ClientError {
}
}
#[derive(Debug, Error)]
#[error("Could not process a `{method}` request")]
pub struct MethodError {
pub method: &'static str,
#[source]
pub error: Box<dyn Error + 'static>,
}
#[derive(Debug, Error)]
#[error("An error occurred in a `{}`", .interface.name())]
pub struct ObjectError {

View file

@ -1,6 +1,7 @@
pub mod ipc;
pub mod jay_compositor;
pub mod jay_log_file;
pub mod jay_screenshot;
pub mod org_kde_kwin_server_decoration;
pub mod org_kde_kwin_server_decoration_manager;
pub mod wl_buffer;

View file

@ -3,14 +3,18 @@ use {
cli::CliLogLevel,
client::{Client, ClientError},
globals::{Global, GlobalName},
ifs::jay_log_file::JayLogFile,
ifs::{jay_log_file::JayLogFile, jay_screenshot::JayScreenshot},
leaks::Tracker,
object::Object,
utils::buffd::{MsgParser, MsgParserError},
screenshoter::take_screenshot,
utils::{
buffd::{MsgParser, MsgParserError},
errorfmt::ErrorFmt,
},
wire::{jay_compositor::*, JayCompositorId},
},
log::Level,
std::rc::Rc,
std::{ops::Deref, rc::Rc},
thiserror::Error,
};
@ -65,13 +69,13 @@ pub struct JayCompositor {
}
impl JayCompositor {
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> {
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> {
let _req: Destroy = self.client.parse(self, parser)?;
self.client.remove_obj(self)?;
Ok(())
}
fn get_log_file(&self, parser: MsgParser<'_, '_>) -> Result<(), GetLogFileError> {
fn get_log_file(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> {
let req: GetLogFile = self.client.parse(self, parser)?;
let log_file = Rc::new(JayLogFile::new(req.id, &self.client));
track!(self.client, log_file);
@ -80,14 +84,14 @@ impl JayCompositor {
Ok(())
}
fn quit(&self, parser: MsgParser<'_, '_>) -> Result<(), QuitError> {
fn quit(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> {
let _req: Quit = self.client.parse(self, parser)?;
log::info!("Quitting");
self.client.state.el.stop();
Ok(())
}
fn set_log_level(&self, parser: MsgParser<'_, '_>) -> Result<(), SetLogLevelError> {
fn set_log_level(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> {
let req: SetLogLevel = self.client.parse(self, parser)?;
const ERROR: u32 = CliLogLevel::Error as u32;
const WARN: u32 = CliLogLevel::Warn as u32;
@ -100,25 +104,57 @@ impl JayCompositor {
INFO => Level::Info,
DEBUG => Level::Debug,
TRACE => Level::Trace,
_ => return Err(SetLogLevelError::UnknownLogLevel(req.level)),
_ => return Err(JayCompositorError::UnknownLogLevel(req.level)),
};
self.client.state.logger.set_level(level);
Ok(())
}
fn take_screenshot(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> {
let req: TakeScreenshot = self.client.parse(self, parser)?;
let ss = Rc::new(JayScreenshot {
id: req.id,
client: self.client.clone(),
tracker: Default::default(),
});
track!(self.client, ss);
self.client.add_client_obj(&ss)?;
match take_screenshot(&self.client.state) {
Ok(s) => {
let dmabuf = s.bo.dmabuf();
let plane = &dmabuf.planes[0];
ss.send_dmabuf(
&s.drm,
&plane.fd,
dmabuf.width,
dmabuf.height,
plane.offset,
plane.stride,
);
}
Err(e) => {
let msg = ErrorFmt(e).to_string();
ss.send_error(&msg);
}
}
self.client.remove_obj(ss.deref())?;
Ok(())
}
}
object_base! {
JayCompositor, JayCompositorError;
object_base2! {
JayCompositor;
DESTROY => destroy,
GET_LOG_FILE => get_log_file,
QUIT => quit,
SET_LOG_LEVEL => set_log_level,
TAKE_SCREENSHOT => take_screenshot,
}
impl Object for JayCompositor {
fn num_requests(&self) -> u32 {
SET_LOG_LEVEL + 1
TAKE_SCREENSHOT + 1
}
}
@ -126,51 +162,12 @@ simple_add_obj!(JayCompositor);
#[derive(Debug, Error)]
pub enum JayCompositorError {
#[error("Could not process a `destroy` request")]
DestroyError(#[from] DestroyError),
#[error("Could not process a `get_log_file` request")]
GetLogFileError(#[from] GetLogFileError),
#[error("Could not process a `quit` request")]
QuitError(#[from] QuitError),
#[error("Could not process a `set_log_level` request")]
SetLogLevelError(#[from] SetLogLevelError),
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(JayCompositorError, ClientError);
#[derive(Debug, Error)]
pub enum DestroyError {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(DestroyError, ClientError);
efrom!(DestroyError, MsgParserError);
#[derive(Debug, Error)]
pub enum GetLogFileError {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(GetLogFileError, ClientError);
efrom!(GetLogFileError, MsgParserError);
#[derive(Debug, Error)]
pub enum QuitError {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
}
efrom!(QuitError, MsgParserError);
#[derive(Debug, Error)]
pub enum SetLogLevelError {
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error("Unknown log level {0}")]
UnknownLogLevel(u32),
}
efrom!(SetLogLevelError, MsgParserError);
efrom!(JayCompositorError, ClientError);
efrom!(JayCompositorError, MsgParserError);

57
src/ifs/jay_screenshot.rs Normal file
View file

@ -0,0 +1,57 @@
use {
crate::{
client::Client,
leaks::Tracker,
object::Object,
wire::{jay_screenshot::*, JayScreenshotId},
},
std::rc::Rc,
uapi::OwnedFd,
};
pub struct JayScreenshot {
pub id: JayScreenshotId,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
}
impl JayScreenshot {
pub fn send_dmabuf(
&self,
drm_dev: &Rc<OwnedFd>,
fd: &Rc<OwnedFd>,
width: i32,
height: i32,
offset: u32,
stride: u32,
) {
self.client.event(Dmabuf {
self_id: self.id,
drm_dev: drm_dev.clone(),
fd: fd.clone(),
width: width as _,
height: height as _,
offset,
stride,
});
}
pub fn send_error(&self, msg: &str) {
self.client.event(Error {
self_id: self.id,
msg,
});
}
}
object_base2! {
JayScreenshot;
}
impl Object for JayScreenshot {
fn num_requests(&self) -> u32 {
0
}
}
simple_add_obj!(JayScreenshot);

View file

@ -11,6 +11,44 @@ macro_rules! efrom {
};
}
macro_rules! object_base2 {
($oname:ident; $($code:ident => $f:ident,)*) => {
impl crate::object::ObjectBase for $oname {
fn id(&self) -> crate::object::ObjectId {
self.id.into()
}
#[allow(unused_variables, unreachable_code)]
fn handle_request(
self: std::rc::Rc<Self>,
request: u32,
parser: crate::utils::buffd::MsgParser<'_, '_>,
) -> Result<(), crate::client::ClientError> {
let res: Result<(), crate::client::MethodError> = match request {
$(
$code => $oname::$f(&self, parser).map_err(|e| crate::client::MethodError {
method: stringify!($f),
error: Box::new(e),
}),
)*
_ => unreachable!(),
};
if let Err(e) = res {
return Err(crate::client::ClientError::ObjectError(crate::client::ObjectError {
interface: crate::wire::$oname,
error: Box::new(e),
}));
}
Ok(())
}
fn interface(&self) -> crate::object::Interface {
crate::wire::$oname
}
}
};
}
macro_rules! object_base {
($oname:ident, $ename:ty; $($code:ident => $f:ident,)*) => {
impl crate::object::ObjectBase for $oname {

View file

@ -59,6 +59,7 @@ mod object;
mod pango;
mod rect;
mod render;
mod screenshoter;
mod sighand;
mod state;
mod tasks;

View file

@ -36,7 +36,7 @@ use {
pub struct EglDisplay {
pub exts: DisplayExt,
pub formats: Rc<AHashMap<u32, &'static Format>>,
pub gbm: GbmDevice,
pub gbm: Rc<GbmDevice>,
pub dpy: EGLDisplay,
}
@ -58,7 +58,7 @@ impl EglDisplay {
let mut dpy = EglDisplay {
exts: DisplayExt::empty(),
formats: Rc::new(AHashMap::new()),
gbm,
gbm: Rc::new(gbm),
dpy,
};
let mut major = 0;

View file

@ -12,6 +12,7 @@ use {
video::{
dmabuf::DmaBuf,
drm::{Drm, NodeType},
gbm::GbmDevice,
},
},
ahash::AHashMap,
@ -44,6 +45,7 @@ impl TexProg {
pub struct RenderContext {
pub(super) ctx: Rc<EglContext>,
pub gbm: Rc<GbmDevice>,
pub(super) render_node: Rc<CString>,
@ -97,6 +99,7 @@ impl RenderContext {
)?;
Ok(Self {
ctx: ctx.clone(),
gbm: ctx.dpy.gbm.clone(),
render_node: node.clone(),

View file

@ -47,10 +47,11 @@ impl Framebuffer {
pub fn render(&self, node: &dyn Node, state: &State, cursor_rect: Option<Rect>) {
let _ = self.ctx.ctx.with_current(|| {
let c = state.theme.background_color.get();
unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
glViewport(0, 0, self.gl.width, self.gl.height);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClearColor(c.r, c.g, c.b, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}

View file

@ -25,7 +25,7 @@ use {
},
state::State,
theme::Color,
tree::{ContainerNode, FloatNode, OutputNode, WorkspaceNode},
tree::{ContainerNode, DisplayNode, FloatNode, OutputNode, WorkspaceNode},
utils::rc_eq::rc_eq,
},
std::{ops::Deref, rc::Rc, slice},
@ -38,6 +38,16 @@ pub struct Renderer<'a> {
}
impl Renderer<'_> {
pub fn render_display(&mut self, display: &DisplayNode, x: i32, y: i32) {
let ext = display.extents.get();
let outputs = display.outputs.lock();
for output in outputs.values() {
let opos = output.global.pos.get();
let (ox, oy) = ext.translate(opos.x1(), opos.y1());
self.render_output(output, x + ox, y + oy);
}
}
pub fn render_output(&mut self, output: &OutputNode, x: i32, y: i32) {
let opos = output.global.pos.get();
macro_rules! render_layer {
@ -57,15 +67,22 @@ impl Renderer<'_> {
let theme = &self.state.theme;
let th = theme.title_height.get();
{
let c = Color::BLACK;
self.fill_boxes2(
slice::from_ref(&Rect::new_sized(0, 0, opos.width(), th).unwrap()),
&c,
x,
y,
);
let rd = output.render_data.borrow_mut();
if let Some(aw) = &rd.active_workspace {
let c = theme.active_title_color.get();
self.fill_boxes(slice::from_ref(aw), &c);
self.fill_boxes2(slice::from_ref(aw), &c, x, y);
}
let c = theme.underline_color.get();
self.fill_boxes(slice::from_ref(&rd.underline), &c);
self.fill_boxes2(slice::from_ref(&rd.underline), &c, x, y);
let c = theme.title_color.get();
self.fill_boxes(&rd.inactive_workspaces, &c);
self.fill_boxes2(&rd.inactive_workspaces, &c, x, y);
for title in &rd.titles {
self.render_texture(&title.tex, x + title.x, y + title.y, ARGB8888);
}

59
src/screenshoter.rs Normal file
View file

@ -0,0 +1,59 @@
use {
crate::{
format::XRGB8888,
render::RenderError,
state::State,
video::{
drm::DrmError,
gbm::{GbmBo, GbmError, GBM_BO_USE_LINEAR, GBM_BO_USE_RENDERING},
ModifiedFormat, INVALID_MODIFIER,
},
},
std::{ops::Deref, rc::Rc},
thiserror::Error,
uapi::OwnedFd,
};
#[derive(Debug, Error)]
pub enum ScreenshooterError {
#[error("There is no render context")]
NoRenderContext,
#[error("Display is empty")]
EmptyDisplay,
#[error(transparent)]
GbmError(#[from] GbmError),
#[error(transparent)]
RenderError(#[from] RenderError),
#[error(transparent)]
DrmError(#[from] DrmError),
}
pub struct Screenshot {
pub drm: Rc<OwnedFd>,
pub bo: GbmBo,
}
pub fn take_screenshot(state: &State) -> Result<Screenshot, ScreenshooterError> {
let ctx = match state.render_ctx.get() {
Some(ctx) => ctx,
_ => return Err(ScreenshooterError::NoRenderContext),
};
let extents = state.root.extents.get();
if extents.is_empty() {
return Err(ScreenshooterError::EmptyDisplay);
}
let format = ModifiedFormat {
format: XRGB8888,
modifier: INVALID_MODIFIER,
};
let bo = ctx.gbm.create_bo(
extents.width(),
extents.height(),
&format,
GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR,
)?;
let fb = ctx.dmabuf_fb(bo.dmabuf())?;
fb.render(state.root.deref(), state, Some(state.root.extents.get()));
let drm = ctx.gbm.drm.dup_render()?.fd().clone();
Ok(Screenshot { drm, bo })
}

View file

@ -123,6 +123,7 @@ impl ConnectorHandler {
}
on.update_render_data();
self.state.root.outputs.set(self.id, on.clone());
self.state.root.update_extents();
self.state.add_global(&global);
'outer: loop {
while let Some(event) = self.data.connector.event() {

View file

@ -15,6 +15,13 @@ impl Color {
b: 0.8,
a: 1.0,
};
pub const BLACK: Self = Self {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
}
fn to_f32(c: u8) -> f32 {

View file

@ -6,16 +6,19 @@ use {
wl_seat::{NodeSeatState, WlSeatGlobal},
zwlr_layer_shell_v1::{OVERLAY, TOP},
},
rect::Rect,
render::Renderer,
tree::{
walker::NodeVisitor, FindTreeResult, FoundNode, Node, NodeId, OutputNode, SizedNode,
},
utils::{copyhashmap::CopyHashMap, linkedlist::LinkedList},
},
std::{ops::Deref, rc::Rc},
std::{cell::Cell, ops::Deref, rc::Rc},
};
pub struct DisplayNode {
pub id: NodeId,
pub extents: Cell<Rect>,
pub outputs: CopyHashMap<ConnectorId, Rc<OutputNode>>,
pub stacked: LinkedList<Rc<dyn Node>>,
pub seat_state: NodeSeatState,
@ -25,11 +28,36 @@ impl DisplayNode {
pub fn new(id: NodeId) -> Self {
Self {
id,
extents: Default::default(),
outputs: Default::default(),
stacked: Default::default(),
seat_state: Default::default(),
}
}
pub fn update_extents(&self) {
let outputs = self.outputs.lock();
let mut x1 = i32::MAX;
let mut y1 = i32::MAX;
let mut x2 = i32::MIN;
let mut y2 = i32::MIN;
for output in outputs.values() {
let pos = output.global.pos.get();
x1 = x1.min(pos.x1());
y1 = y1.min(pos.y1());
x2 = x2.max(pos.x2());
y2 = y2.max(pos.y2());
}
if x1 >= x2 {
x1 = 0;
x2 = 0;
}
if y1 >= y2 {
y1 = 0;
y2 = 0;
}
self.extents.set(Rect::new(x1, y1, x2, y2).unwrap());
}
}
impl SizedNode for DisplayNode {
@ -132,4 +160,8 @@ impl SizedNode for DisplayNode {
fn pointer_focus(&self, seat: &Rc<WlSeatGlobal>) {
seat.set_known_cursor(KnownCursor::Default);
}
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_display(self, x, y);
}
}

View file

@ -200,6 +200,7 @@ impl OutputNode {
fn change_extents_(&self, rect: &Rect) {
self.global.pos.set(*rect);
self.state.root.update_extents();
self.update_render_data();
if let Some(c) = self.workspace.get() {
c.node_change_extents(&self.workspace_rect());

View file

@ -187,6 +187,12 @@ impl From<std::io::Error> for OsError {
}
}
impl Default for OsError {
fn default() -> Self {
Errno::default().into()
}
}
impl Error for OsError {}
impl Display for OsError {

View file

@ -7,6 +7,7 @@ pub trait WindowsExt<T> {
T: 'a;
fn array_windows_ext<'a, const N: usize>(&'a self) -> Self::Windows<'a, N>;
fn array_chunks_ext<'a, const N: usize>(&'a self) -> &'a [[T; N]];
}
impl<T> WindowsExt<T> for [T] {
@ -18,6 +19,11 @@ impl<T> WindowsExt<T> for [T] {
fn array_windows_ext<'a, const N: usize>(&'a self) -> Self::Windows<'a, N> {
WindowsIter { slice: self }
}
fn array_chunks_ext<'a, const N: usize>(&'a self) -> &'a [[T; N]] {
let len = self.len() / N;
unsafe { std::slice::from_raw_parts(self.as_ptr() as _, len) }
}
}
pub struct WindowsIter<'a, T, const N: usize> {

View file

@ -7,6 +7,8 @@ pub mod gbm;
pub type Modifier = u64;
pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff;
#[allow(dead_code)]
pub const LINEAR_MODIFIER: Modifier = 0;
#[derive(Copy, Clone)]
pub struct ModifiedFormat {

View file

@ -1,11 +1,13 @@
use {crate::format::Format, std::rc::Rc, uapi::OwnedFd};
#[derive(Clone)]
pub struct DmaBufPlane {
pub offset: u32,
pub stride: u32,
pub fd: Rc<OwnedFd>,
}
#[derive(Clone)]
pub struct DmaBuf {
pub width: i32,
pub height: i32,

View file

@ -1,6 +1,9 @@
#![allow(non_camel_case_types)]
use {
crate::{
format::formats,
utils::oserror::OsError,
video::{
dmabuf::{DmaBuf, DmaBufPlane},
drm::{Drm, DrmError},
@ -11,6 +14,7 @@ use {
fmt::{Debug, Formatter},
ptr,
rc::Rc,
slice,
},
thiserror::Error,
uapi::{c, OwnedFd},
@ -28,6 +32,8 @@ pub enum GbmError {
UnknownFormat,
#[error("Could not retrieve a drm-buf fd")]
DrmFd,
#[error("Could not map bo")]
MapBo(#[source] OsError),
}
pub type Device = u8;
@ -39,16 +45,42 @@ pub const GBM_BO_USE_CURSOR: u32 = 1 << 1;
pub const GBM_BO_USE_RENDERING: u32 = 1 << 2;
#[allow(dead_code)]
pub const GBM_BO_USE_WRITE: u32 = 1 << 3;
#[allow(dead_code)]
pub const GBM_BO_USE_LINEAR: u32 = 1 << 4;
#[allow(dead_code)]
pub const GBM_BO_USE_PROTECTED: u32 = 1 << 5;
#[allow(dead_code)]
const GBM_BO_IMPORT_WL_BUFFER: u32 = 0x5501;
#[allow(dead_code)]
const GBM_BO_IMPORT_EGL_IMAGE: u32 = 0x5502;
#[allow(dead_code)]
const GBM_BO_IMPORT_FD: u32 = 0x5503;
const GBM_BO_IMPORT_FD_MODIFIER: u32 = 0x5504;
const GBM_BO_TRANSFER_READ: u32 = 1 << 0;
#[allow(dead_code)]
const GBM_BO_TRANSFER_WRITE: u32 = 1 << 1;
#[allow(dead_code)]
const GBM_BO_TRANSFER_READ_WRITE: u32 = GBM_BO_TRANSFER_READ | GBM_BO_TRANSFER_WRITE;
#[repr(C)]
struct gbm_import_fd_modifier_data {
width: u32,
height: u32,
format: u32,
num_fds: u32,
fds: [c::c_int; 4],
strides: [c::c_int; 4],
offsets: [c::c_int; 4],
modifier: u64,
}
#[link(name = "gbm")]
extern "C" {
fn gbm_create_device(fd: c::c_int) -> *mut Device;
fn gbm_device_destroy(dev: *mut Device);
fn gbm_bo_import(dev: *mut Device, ty: u32, buffer: *mut u8, flags: u32) -> *mut Bo;
fn gbm_bo_create_with_modifiers2(
dev: *mut Device,
width: u32,
@ -71,10 +103,21 @@ extern "C" {
fn gbm_bo_get_format(bo: *mut Bo) -> u32;
#[allow(dead_code)]
fn gbm_bo_get_bpp(bo: *mut Bo) -> u32;
fn gbm_bo_map(
bo: *mut Bo,
x: u32,
y: u32,
width: u32,
height: u32,
flags: u32,
strid: *mut u32,
map_data: *mut *mut u8,
) -> *mut u8;
fn gbm_bo_unmap(bo: *mut Bo, map_data: *mut u8);
}
pub struct GbmDevice {
_drm: Drm,
pub drm: Drm,
dev: *mut Device,
}
@ -89,8 +132,20 @@ struct BoHolder {
}
pub struct GbmBo {
_bo: BoHolder,
dma: DmaBuf,
bo: BoHolder,
dmabuf: DmaBuf,
}
pub struct GbmBoMap {
bo: Rc<GbmBo>,
data: *mut [u8],
opaque: *mut u8,
}
impl GbmBoMap {
pub unsafe fn data(&self) -> &[u8] {
&*self.data
}
}
unsafe fn export_bo(bo: *mut Bo) -> Result<DmaBuf, GbmError> {
@ -132,7 +187,7 @@ impl GbmDevice {
if dev.is_null() {
Err(GbmError::CreateDevice)
} else {
Ok(Self { _drm: drm, dev })
Ok(Self { drm: drm, dev })
}
}
@ -167,7 +222,44 @@ impl GbmDevice {
}
let bo = BoHolder { bo };
let dma = export_bo(bo.bo)?;
Ok(GbmBo { _bo: bo, dma })
Ok(GbmBo {
bo: bo,
dmabuf: dma,
})
}
}
pub fn import_dmabuf(&self, dmabuf: &DmaBuf, usage: u32) -> Result<GbmBo, GbmError> {
let mut import = gbm_import_fd_modifier_data {
width: dmabuf.width as _,
height: dmabuf.height as _,
format: dmabuf.format.drm as _,
num_fds: dmabuf.planes.len() as _,
fds: [0; 4],
strides: [0; 4],
offsets: [0; 4],
modifier: dmabuf.modifier,
};
for (i, plane) in dmabuf.planes.iter().enumerate() {
import.fds[i] = plane.fd.raw();
import.strides[i] = plane.stride as _;
import.offsets[i] = plane.offset as _;
}
unsafe {
let bo = gbm_bo_import(
self.dev,
GBM_BO_IMPORT_FD_MODIFIER,
&mut import as *const _ as _,
usage,
);
if bo.is_null() {
return Err(GbmError::CreateBo);
}
let bo = BoHolder { bo };
Ok(GbmBo {
bo: bo,
dmabuf: dmabuf.clone(),
})
}
}
}
@ -181,8 +273,42 @@ impl Drop for GbmDevice {
}
impl GbmBo {
pub fn dma(&self) -> &DmaBuf {
&self.dma
pub fn dmabuf(&self) -> &DmaBuf {
&self.dmabuf
}
pub fn map(self: &Rc<Self>) -> Result<GbmBoMap, GbmError> {
let mut stride = 0;
let mut map_data = ptr::null_mut();
unsafe {
let map = gbm_bo_map(
self.bo.bo,
0,
0,
self.dmabuf.width as _,
self.dmabuf.height as _,
GBM_BO_TRANSFER_READ,
&mut stride,
&mut map_data,
);
if map.is_null() {
return Err(GbmError::MapBo(OsError::default()));
}
let map = slice::from_raw_parts_mut(map, (stride * self.dmabuf.height as u32) as usize);
Ok(GbmBoMap {
bo: self.clone(),
data: map,
opaque: map_data,
})
}
}
}
impl Drop for GbmBoMap {
fn drop(&mut self) {
unsafe {
gbm_bo_unmap(self.bo.bo.bo, self.opaque);
}
}
}