autocommit 2022-04-13 21:01:32 CEST
This commit is contained in:
parent
661a28e5b0
commit
916e3644c3
30 changed files with 681 additions and 73 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -314,6 +314,7 @@ dependencies = [
|
|||
"bitflags",
|
||||
"bstr",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"default-config",
|
||||
|
|
@ -328,6 +329,7 @@ dependencies = [
|
|||
"num-traits",
|
||||
"once_cell",
|
||||
"pin-project",
|
||||
"qoi",
|
||||
"rand",
|
||||
"repc",
|
||||
"smallvec",
|
||||
|
|
@ -515,6 +517,10 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.16"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||
build = "build/build.rs"
|
||||
|
||||
[workspace]
|
||||
members = ["jay-config", "default-config"]
|
||||
members = ["jay-config", "default-config", "qoi"]
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
|
@ -32,12 +32,14 @@ byteorder = "1.4.3"
|
|||
bincode = "2.0.0-rc.1"
|
||||
jay-config = { path = "jay-config" }
|
||||
default-config = { path = "default-config" }
|
||||
qoi = { path = "qoi" }
|
||||
pin-project = "1.0.10"
|
||||
clap = { version = "3.1.6", features = ["derive", "wrap_help"] }
|
||||
clap_complete = "3.1.1"
|
||||
humantime = "2.1.0"
|
||||
dirs = "4.0.0"
|
||||
backtrace = "0.3.64"
|
||||
chrono = "0.4.19"
|
||||
|
||||
[build-dependencies]
|
||||
repc = "0.1.1"
|
||||
|
|
@ -47,5 +49,8 @@ bstr = { version = "0.2.17", default-features = false, features = ["std"] }
|
|||
#[profile.dev.build-override]
|
||||
#opt-level = 3
|
||||
|
||||
[profile.dev.package."qoi"]
|
||||
opt-level = 3
|
||||
|
||||
[features]
|
||||
rc_tracking = []
|
||||
|
|
|
|||
91
qoi/src/lib.rs
Normal file
91
qoi/src/lib.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
pub fn xrgb8888_encode_qoi(bytes: &[u8], width: u32, height: u32, stride: u32) -> Vec<u8> {
|
||||
const OP_RGB: u8 = 0b1111_1110;
|
||||
const OP_INDEX: u8 = 0b0000_0000;
|
||||
const OP_DIFF: u8 = 0b0100_0000;
|
||||
const OP_LUMA: u8 = 0b1000_0000;
|
||||
const OP_RUN: u8 = 0b1100_0000;
|
||||
|
||||
let mut res = vec![];
|
||||
let width_bytes_be = width.to_be_bytes();
|
||||
let height_bytes_be = height.to_be_bytes();
|
||||
let header = [
|
||||
b'q',
|
||||
b'o',
|
||||
b'i',
|
||||
b'f',
|
||||
width_bytes_be[0],
|
||||
width_bytes_be[1],
|
||||
width_bytes_be[2],
|
||||
width_bytes_be[3],
|
||||
height_bytes_be[0],
|
||||
height_bytes_be[1],
|
||||
height_bytes_be[2],
|
||||
height_bytes_be[3],
|
||||
3,
|
||||
0,
|
||||
];
|
||||
res.extend_from_slice(&header);
|
||||
let mut prev_pixel = [0, 0, 0, 0xff];
|
||||
let mut array = [[0; 4]; 64];
|
||||
let mut run_length = 0;
|
||||
for line in bytes.chunks_exact(stride as _) {
|
||||
for &pixel in array_chunks::<_, 4>(&line[..(width * 4) as _]) {
|
||||
let pixel = [pixel[2], pixel[1], pixel[0], 0xff];
|
||||
if pixel == prev_pixel {
|
||||
run_length += 1;
|
||||
if run_length == 62 {
|
||||
res.push(OP_RUN | (run_length - 1));
|
||||
run_length = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if run_length > 0 {
|
||||
res.push(OP_RUN | (run_length - 1));
|
||||
run_length = 0;
|
||||
}
|
||||
let prev = prev_pixel;
|
||||
prev_pixel = pixel;
|
||||
let index = {
|
||||
let sum = 0u8
|
||||
.wrapping_add(pixel[0].wrapping_mul(3))
|
||||
.wrapping_add(pixel[1].wrapping_mul(5))
|
||||
.wrapping_add(pixel[2].wrapping_mul(7))
|
||||
.wrapping_add(255u8.wrapping_mul(11));
|
||||
sum & 63
|
||||
};
|
||||
if array[index as usize] == pixel {
|
||||
res.push(OP_INDEX | index);
|
||||
continue;
|
||||
}
|
||||
array[index as usize] = pixel;
|
||||
let dr = pixel[0].wrapping_sub(prev[0]);
|
||||
let dg = pixel[1].wrapping_sub(prev[1]);
|
||||
let db = pixel[2].wrapping_sub(prev[2]);
|
||||
let dr_2 = dr.wrapping_add(2);
|
||||
let dg_2 = dg.wrapping_add(2);
|
||||
let db_2 = db.wrapping_add(2);
|
||||
if dr_2 | dg_2 | db_2 | 3 == 3 {
|
||||
res.push(OP_DIFF | (dr_2 << 4) | (dg_2 << 2) | db_2);
|
||||
continue;
|
||||
}
|
||||
let dr_dg_8 = dr.wrapping_sub(dg).wrapping_add(8);
|
||||
let db_dg_8 = db.wrapping_sub(dg).wrapping_add(8);
|
||||
let dg_32 = dg.wrapping_add(32);
|
||||
if (dg_32 | 63 == 63) && (dr_dg_8 | db_dg_8 | 15 == 15) {
|
||||
res.extend_from_slice(&[OP_LUMA | dg_32, (dr_dg_8 << 4) | db_dg_8]);
|
||||
continue;
|
||||
}
|
||||
res.extend_from_slice(&[OP_RGB, pixel[0], pixel[1], pixel[2]]);
|
||||
}
|
||||
}
|
||||
if run_length > 0 {
|
||||
res.push(OP_RUN | (run_length - 1));
|
||||
}
|
||||
res.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
res
|
||||
}
|
||||
|
||||
fn array_chunks<T, const N: usize>(slice: &[T]) -> &[[T; N]] {
|
||||
let len = slice.len() / N;
|
||||
unsafe { std::slice::from_raw_parts(slice.as_ptr() as _, len) }
|
||||
}
|
||||
|
|
@ -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)),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
15
src/cli.rs
15
src/cli.rs
|
|
@ -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
108
src/cli/screenshot.rs
Normal 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)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
pub use error::{ClientError, ObjectError};
|
||||
pub use error::{ClientError, MethodError, ObjectError};
|
||||
use {
|
||||
crate::{
|
||||
async_engine::{AsyncFd, SpawnedFuture},
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
57
src/ifs/jay_screenshot.rs
Normal 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);
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ mod object;
|
|||
mod pango;
|
||||
mod rect;
|
||||
mod render;
|
||||
mod screenshoter;
|
||||
mod sighand;
|
||||
mod state;
|
||||
mod tasks;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
59
src/screenshoter.rs
Normal 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 })
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
142
src/video/gbm.rs
142
src/video/gbm.rs
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,3 +14,7 @@ msg quit = 2 {
|
|||
msg set_log_level = 3 {
|
||||
level: u32,
|
||||
}
|
||||
|
||||
msg take_screenshot = 4 {
|
||||
id: id(jay_screenshot),
|
||||
}
|
||||
|
|
|
|||
14
wire/jay_screenshot.txt
Normal file
14
wire/jay_screenshot.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# events
|
||||
|
||||
msg dmabuf = 0 {
|
||||
drm_dev: fd,
|
||||
fd: fd,
|
||||
width: u32,
|
||||
height: u32,
|
||||
offset: u32,
|
||||
stride: u32,
|
||||
}
|
||||
|
||||
msg error = 1 {
|
||||
msg: str,
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue