autocommit 2022-01-28 03:35:35 CET
This commit is contained in:
parent
c340df0d08
commit
a5573b8a3a
36 changed files with 3046 additions and 114 deletions
88
Cargo.lock
generated
88
Cargo.lock
generated
|
|
@ -99,6 +99,15 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.19"
|
||||
|
|
@ -229,6 +238,9 @@ dependencies = [
|
|||
"log",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"renderdoc",
|
||||
"repc",
|
||||
"thiserror",
|
||||
"uapi",
|
||||
|
|
@ -317,6 +329,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
|
|
@ -335,6 +353,46 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
|
|
@ -358,6 +416,27 @@ version = "0.6.25"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "renderdoc"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42e14087d51efd3b42eb341e37b6f320af2b0750519ea849cb68bb7289643ed"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"float-cmp",
|
||||
"libloading",
|
||||
"once_cell",
|
||||
"renderdoc-sys",
|
||||
"winapi",
|
||||
"wio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "renderdoc-sys"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157"
|
||||
|
||||
[[package]]
|
||||
name = "repc"
|
||||
version = "0.1.1"
|
||||
|
|
@ -500,6 +579,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "wio"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcb-dl"
|
||||
version = "0.2.0"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ xcb-dl-util = { version = "0.2.0", features = ["xcb_shm", "xcb_xinput", "xcb_xkb
|
|||
libloading = "0.7.2"
|
||||
bstr = "0.2.17"
|
||||
isnt = "0.1.0"
|
||||
once_cell = "1.9.0"
|
||||
rand = "0.8.4"
|
||||
renderdoc = "0.10.1"
|
||||
|
||||
[build-dependencies]
|
||||
repc = "0.1.1"
|
||||
|
|
|
|||
142
build.rs
142
build.rs
|
|
@ -1,4 +1,5 @@
|
|||
use repc::layout::{Type, TypeVariant};
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::BufWriter;
|
||||
use std::io::Write;
|
||||
|
|
@ -58,6 +59,144 @@ fn write_ty<W: Write>(f: &mut W, vals: &[u32], ty: &str) -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn write_egl_procs<W: Write>(f: &mut W) -> anyhow::Result<()> {
|
||||
let map = [
|
||||
(
|
||||
"eglGetPlatformDisplayEXT",
|
||||
"EGLDisplay",
|
||||
&[
|
||||
("platform", "EGLenum"),
|
||||
("native_display", "*mut u8"),
|
||||
("attrib_list", "*const EGLint"),
|
||||
][..],
|
||||
),
|
||||
(
|
||||
"eglCreateImageKHR",
|
||||
"EGLImageKHR",
|
||||
&[
|
||||
("dpy", "EGLDisplay"),
|
||||
("ctx", "EGLContext"),
|
||||
("target", "EGLenum"),
|
||||
("buffer", "EGLClientBuffer"),
|
||||
("attrib_list", "*const EGLint"),
|
||||
][..],
|
||||
),
|
||||
(
|
||||
"eglDestroyImageKHR",
|
||||
"EGLBoolean",
|
||||
&[("dpy", "EGLDisplay"), ("image", "EGLImageKHR")][..],
|
||||
),
|
||||
(
|
||||
"eglQueryDmaBufFormatsEXT",
|
||||
"EGLBoolean",
|
||||
&[
|
||||
("dpy", "EGLDisplay"),
|
||||
("max_formats", "EGLint"),
|
||||
("formats", "*mut EGLint"),
|
||||
("num_formats", "*mut EGLint"),
|
||||
][..],
|
||||
),
|
||||
(
|
||||
"eglQueryDmaBufModifiersEXT",
|
||||
"EGLBoolean",
|
||||
&[
|
||||
("dpy", "EGLDisplay"),
|
||||
("format", "EGLint"),
|
||||
("max_modifiers", "EGLint"),
|
||||
("modifiers", "*mut EGLuint64KHR"),
|
||||
("external_only", "*mut EGLBoolean"),
|
||||
("num_modifiers", "*mut EGLint"),
|
||||
][..],
|
||||
),
|
||||
(
|
||||
"eglDebugMessageControlKHR",
|
||||
"EGLint",
|
||||
&[
|
||||
("callback", "EGLDEBUGPROCKHR"),
|
||||
("attrib_list", "*const EGLAttrib"),
|
||||
][..],
|
||||
),
|
||||
(
|
||||
"eglQueryDisplayAttribEXT",
|
||||
"EGLBoolean",
|
||||
&[
|
||||
("dpy", "EGLDisplay"),
|
||||
("attribute", "EGLint"),
|
||||
("value", "*mut EGLAttrib"),
|
||||
][..],
|
||||
),
|
||||
(
|
||||
"eglQueryDeviceStringEXT",
|
||||
"*const c::c_char",
|
||||
&[("device", "EGLDeviceEXT"), ("name", "EGLint")][..],
|
||||
),
|
||||
(
|
||||
"eglQueryDevicesEXT",
|
||||
"EGLBoolean",
|
||||
&[
|
||||
("max_devices", "EGLint"),
|
||||
("devices", "*mut EGLDeviceEXT"),
|
||||
("num_devices", "*mut EGLint"),
|
||||
][..],
|
||||
),
|
||||
(
|
||||
"glEGLImageTargetRenderbufferStorageOES",
|
||||
"()",
|
||||
&[("target", "GLenum"), ("image", "GLeglImageOES")][..],
|
||||
),
|
||||
];
|
||||
|
||||
writeln!(f, "use std::ptr;")?;
|
||||
writeln!(f, "use super::sys::*;")?;
|
||||
writeln!(f)?;
|
||||
writeln!(f, "#[derive(Copy, Clone, Debug)]")?;
|
||||
writeln!(f, "pub struct ExtProc {{")?;
|
||||
for (name, _, _) in map.iter().copied() {
|
||||
writeln!(f, " {}: *mut u8,", name)?;
|
||||
}
|
||||
writeln!(f, "}}")?;
|
||||
writeln!(f)?;
|
||||
writeln!(f, "impl ExtProc {{")?;
|
||||
writeln!(f, " pub fn load() -> Self {{")?;
|
||||
writeln!(f, " Self {{")?;
|
||||
for (name, _, _) in map.iter().copied() {
|
||||
writeln!(
|
||||
f,
|
||||
" {}: unsafe {{ eglGetProcAddress(\"{}\\0\".as_ptr() as _) }},",
|
||||
name, name
|
||||
)?;
|
||||
}
|
||||
writeln!(f, " }}")?;
|
||||
writeln!(f, " }}")?;
|
||||
for (name, ret, args) in map.iter().copied() {
|
||||
let mut args_names = String::new();
|
||||
let mut args_full = String::new();
|
||||
let mut args_tys = String::new();
|
||||
for (name, ty) in args.iter().copied() {
|
||||
write!(args_full, "{}: {}, ", name, ty)?;
|
||||
write!(args_names, "{}, ", name)?;
|
||||
write!(args_tys, "{}, ", ty)?;
|
||||
}
|
||||
writeln!(f)?;
|
||||
writeln!(
|
||||
f,
|
||||
" pub(super) unsafe fn {}(&self, {}) -> {} {{",
|
||||
name, args_full, ret
|
||||
)?;
|
||||
writeln!(f, " if self.{}.is_null() {{", name)?;
|
||||
writeln!(f, " panic!(\"Could not load `{}`\");", name)?;
|
||||
writeln!(f, " }}")?;
|
||||
writeln!(
|
||||
f,
|
||||
" ptr::read(&self.{} as *const *mut u8 as *const unsafe extern fn({}) -> {})({})",
|
||||
name, args_tys, ret, args_names
|
||||
)?;
|
||||
writeln!(f, " }}")?;
|
||||
}
|
||||
writeln!(f, "}}")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let mut f = open("pixman_tys.rs")?;
|
||||
write_ty(&mut f, pixman::FORMATS, "PixmanFormat")?;
|
||||
|
|
@ -79,6 +218,9 @@ fn main() -> anyhow::Result<()> {
|
|||
)?;
|
||||
write_ty(&mut f, xkbcommon::XKB_KEY_DIRECTION, "xkb_key_direction")?;
|
||||
|
||||
let mut f = open("egl_procs.rs")?;
|
||||
write_egl_procs(&mut f)?;
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,18 @@
|
|||
use crate::backend::{
|
||||
BackendEvent, KeyState, Output, OutputId, ScrollAxis, Seat, SeatEvent, SeatId,
|
||||
};
|
||||
use crate::drm::drm::{Drm, DrmError};
|
||||
use crate::drm::gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING};
|
||||
use crate::drm::{ModifiedFormat, INVALID_MODIFIER};
|
||||
use crate::egl::{EglContext, EglImage};
|
||||
use crate::event_loop::{EventLoopDispatcher, EventLoopId};
|
||||
use crate::fixed::Fixed;
|
||||
use crate::format::XRGB8888;
|
||||
use crate::gles2::gl::{GlFrameBuffer, GlRenderBuffer, GlTexture};
|
||||
use crate::gles2::sys::{glBindFramebuffer, glFlush, glViewport, GL_FRAMEBUFFER};
|
||||
use crate::gles2::GlesError;
|
||||
use crate::pixman::{Image, PixmanError};
|
||||
use crate::render::gles::{GlesRenderer, RENDERDOC};
|
||||
use crate::render::pixman::PixmanRenderer;
|
||||
use crate::render::Renderer;
|
||||
use crate::servermem::{ServerMem, ServerMemError};
|
||||
|
|
@ -12,8 +21,10 @@ use crate::utils::clonecell::CloneCell;
|
|||
use crate::utils::copyhashmap::CopyHashMap;
|
||||
use crate::utils::ptr_ext::PtrExt;
|
||||
use crate::wheel::{WheelDispatcher, WheelId};
|
||||
use crate::{pixman, EventLoopError, State, WheelError};
|
||||
use crate::{gles2, pixman, EventLoopError, NumCell, State, WheelError};
|
||||
use gles2::egl;
|
||||
use isnt::std_1::primitive::IsntConstPtrExt;
|
||||
use rand::Rng;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::error::Error;
|
||||
|
|
@ -21,7 +32,7 @@ use std::ops::Deref;
|
|||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
use uapi::c;
|
||||
use uapi::{c, OwnedFd};
|
||||
use xcb_dl::{ffi, Xcb, XcbDri3, XcbPresent, XcbShm, XcbXinput, XcbXkb};
|
||||
use xcb_dl_util::error::{XcbError, XcbErrorParser};
|
||||
use xcb_dl_util::xcb_box::XcbBox;
|
||||
|
|
@ -30,6 +41,20 @@ use xcb_dl_util::xcb_box::XcbBox;
|
|||
pub enum XorgBackendError {
|
||||
#[error("The xcb connection is in an error state")]
|
||||
ErrorEvent,
|
||||
#[error("The drm subsystem returned an error")]
|
||||
DrmError(#[from] DrmError),
|
||||
#[error("The gbm subsystem returned an error")]
|
||||
GbmError(#[from] GbmError),
|
||||
#[error("Could not find an EGL device that matches the display's DRM device")]
|
||||
MissingEglDevice,
|
||||
#[error("Could not import a dma-buf")]
|
||||
ImportBuffer(#[source] XcbError),
|
||||
#[error("The EGL device does not support the XRGB8888 format")]
|
||||
XRGB8888,
|
||||
#[error("Could not create an EGL context")]
|
||||
CreateEgl(#[source] Box<Self>),
|
||||
#[error(transparent)]
|
||||
EglError(#[from] GlesError),
|
||||
#[error("Could not select input events")]
|
||||
CannotSelectInputEvents(#[source] XcbError),
|
||||
#[error("Could not select present events")]
|
||||
|
|
@ -73,6 +98,7 @@ struct XcbCon {
|
|||
input_opcode: u8,
|
||||
present_opcode: u8,
|
||||
xkb: Box<XcbXkb>,
|
||||
screen: ffi::xcb_screen_t,
|
||||
c: *mut ffi::xcb_connection_t,
|
||||
errors: XcbErrorParser,
|
||||
}
|
||||
|
|
@ -85,15 +111,18 @@ impl XcbCon {
|
|||
let input = Box::new(XcbXinput::load_loose()?);
|
||||
let xkb = Box::new(XcbXkb::load_loose()?);
|
||||
let dri = Box::new(XcbDri3::load_loose()?);
|
||||
let present = Box::new(XcbPresent::load_loose()?);
|
||||
|
||||
let c = xcb.xcb_connect(ptr::null(), ptr::null_mut());
|
||||
let errors = XcbErrorParser::new(&xcb, c);
|
||||
|
||||
let mut con = Self {
|
||||
screen: *xcb.xcb_setup_roots_iterator(xcb.xcb_get_setup(c)).data,
|
||||
xcb,
|
||||
shm,
|
||||
input,
|
||||
dri,
|
||||
present,
|
||||
input_opcode: 0,
|
||||
present_opcode: 0,
|
||||
xkb,
|
||||
|
|
@ -192,6 +221,28 @@ pub struct XorgBackend {
|
|||
outputs: CopyHashMap<ffi::xcb_window_t, Rc<XorgOutput>>,
|
||||
seats: CopyHashMap<ffi::xcb_input_device_id_t, Rc<XorgSeat>>,
|
||||
mouse_seats: CopyHashMap<ffi::xcb_input_device_id_t, Rc<XorgSeat>>,
|
||||
ctx: Rc<EglContext>,
|
||||
renderer: GlesRenderer,
|
||||
gbm: GbmDevice,
|
||||
r: Cell<f32>,
|
||||
g: Cell<f32>,
|
||||
b: Cell<f32>,
|
||||
}
|
||||
|
||||
fn get_drm(con: &XcbCon) -> Result<Drm, XorgBackendError> {
|
||||
unsafe {
|
||||
let mut err = ptr::null_mut();
|
||||
let res = con.dri.xcb_dri3_open_reply(
|
||||
con.c,
|
||||
con.dri.xcb_dri3_open(con.c, con.screen.root, 0),
|
||||
&mut err,
|
||||
);
|
||||
let mut res = con.check(res, err)?;
|
||||
assert!(res.nfd == 1);
|
||||
let fd = *con.dri.xcb_dri3_open_reply_fds(con.c, &mut *res);
|
||||
let fd = OwnedFd::new(fd);
|
||||
Ok(Drm::new(fd.raw(), true)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl XorgBackend {
|
||||
|
|
@ -199,6 +250,26 @@ impl XorgBackend {
|
|||
unsafe {
|
||||
let con = XcbCon::new()?;
|
||||
|
||||
let drm = get_drm(&con)?;
|
||||
let res = (|| {
|
||||
let egl_dev = match egl::find_drm_device(&drm)? {
|
||||
Some(d) => d,
|
||||
None => return Err(XorgBackendError::MissingEglDevice),
|
||||
};
|
||||
let dpy = egl_dev.create_display()?;
|
||||
if !dpy.formats.contains_key(&XRGB8888.drm) {
|
||||
return Err(XorgBackendError::XRGB8888);
|
||||
}
|
||||
let ctx = dpy.create_context()?;
|
||||
Ok(ctx)
|
||||
})();
|
||||
let ctx = match res {
|
||||
Ok(r) => r,
|
||||
Err(e) => return Err(XorgBackendError::CreateEgl(Box::new(e))),
|
||||
};
|
||||
let renderer = ctx.with_current(|| GlesRenderer::new(&ctx))?;
|
||||
let gbm = GbmDevice::new(&drm)?;
|
||||
|
||||
let fd = con.xcb.xcb_get_file_descriptor(con.c);
|
||||
|
||||
let wheel_id = state.wheel.id();
|
||||
|
|
@ -211,6 +282,12 @@ impl XorgBackend {
|
|||
outputs: Default::default(),
|
||||
seats: Default::default(),
|
||||
mouse_seats: Default::default(),
|
||||
ctx: ctx.clone(),
|
||||
renderer,
|
||||
gbm,
|
||||
r: Cell::new(0.0),
|
||||
g: Cell::new(0.0),
|
||||
b: Cell::new(0.0),
|
||||
});
|
||||
|
||||
{
|
||||
|
|
@ -226,7 +303,7 @@ impl XorgBackend {
|
|||
}
|
||||
}
|
||||
|
||||
state.wheel.periodic(wheel_id, 16_667, slf.clone())?;
|
||||
// state.wheel.periodic(wheel_id, 16_667, slf.clone())?;
|
||||
// state.wheel.periodic(wheel_id, 1000_000, slf.clone())?;
|
||||
state.el.insert(slf.id, Some(fd), c::EPOLLIN, slf.clone())?;
|
||||
|
||||
|
|
@ -234,10 +311,69 @@ impl XorgBackend {
|
|||
slf.query_devices(ffi::XCB_INPUT_DEVICE_ALL_MASTER as _)?;
|
||||
slf.handle_events()?;
|
||||
|
||||
state.egl.set(Some(ctx.clone()));
|
||||
|
||||
Ok(slf)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_images(
|
||||
&self,
|
||||
window: ffi::xcb_window_t,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<[XorgImage; 2], XorgBackendError> {
|
||||
let format = ModifiedFormat {
|
||||
format: XRGB8888,
|
||||
modifier: INVALID_MODIFIER,
|
||||
};
|
||||
let mut images = [None, None];
|
||||
for i in 0..2 {
|
||||
let bo = self
|
||||
.gbm
|
||||
.create_bo(width, height, &format, GBM_BO_USE_RENDERING)?;
|
||||
let dma = bo.dma();
|
||||
assert!(dma.planes.len() == 1);
|
||||
let plane = dma.planes.first().unwrap();
|
||||
let size = plane.stride * dma.height as u32;
|
||||
let fd = uapi::fcntl_dupfd_cloexec(plane.fd.raw(), 0).unwrap();
|
||||
let fb = if RENDERDOC {
|
||||
unsafe { GlTexture::new(&self.ctx, XRGB8888, width, height)?.to_framebuffer()? }
|
||||
} else {
|
||||
let egl_img = self.ctx.dpy.import_dmabuf(dma)?;
|
||||
let rb = GlRenderBuffer::from_image(&egl_img, &self.ctx)?;
|
||||
rb.create_framebuffer()?
|
||||
};
|
||||
let pixmap = unsafe {
|
||||
let pixmap = self.con.xcb.xcb_generate_id(self.con.c);
|
||||
let cookie = self.con.dri.xcb_dri3_pixmap_from_buffer_checked(
|
||||
self.con.c,
|
||||
pixmap,
|
||||
window,
|
||||
size,
|
||||
dma.width as _,
|
||||
dma.height as _,
|
||||
plane.stride as _,
|
||||
24,
|
||||
32,
|
||||
fd.unwrap(),
|
||||
);
|
||||
if let Err(e) = self.con.check_cookie(cookie) {
|
||||
return Err(XorgBackendError::ImportBuffer(e));
|
||||
}
|
||||
pixmap
|
||||
};
|
||||
images[i] = Some(XorgImage {
|
||||
pixmap: Cell::new(pixmap),
|
||||
fb: CloneCell::new(fb),
|
||||
idle: Cell::new(true),
|
||||
render_on_idle: Cell::new(false),
|
||||
last_serial: Cell::new(0),
|
||||
});
|
||||
}
|
||||
Ok([images[0].take().unwrap(), images[1].take().unwrap()])
|
||||
}
|
||||
|
||||
fn add_output(self: &Rc<Self>) -> Result<(), XorgBackendError> {
|
||||
unsafe {
|
||||
let con = &self.con;
|
||||
|
|
@ -247,6 +383,8 @@ impl XorgBackend {
|
|||
.data
|
||||
.deref();
|
||||
let window_id = con.xcb.xcb_generate_id(con.c);
|
||||
const WIDTH: i32 = 800;
|
||||
const HEIGHT: i32 = 600;
|
||||
{
|
||||
let cookie = con.xcb.xcb_create_window_checked(
|
||||
con.c,
|
||||
|
|
@ -255,8 +393,8 @@ impl XorgBackend {
|
|||
screen.root,
|
||||
0,
|
||||
0,
|
||||
800,
|
||||
600,
|
||||
WIDTH as _,
|
||||
HEIGHT as _,
|
||||
0,
|
||||
ffi::XCB_WINDOW_CLASS_INPUT_OUTPUT as _,
|
||||
0,
|
||||
|
|
@ -267,6 +405,7 @@ impl XorgBackend {
|
|||
return Err(XorgBackendError::CreateWindow(e));
|
||||
}
|
||||
}
|
||||
let images = self.create_images(window_id, WIDTH, HEIGHT).unwrap();
|
||||
let output = Rc::new(XorgOutput {
|
||||
id: self.state.output_ids.next(),
|
||||
backend: self.clone(),
|
||||
|
|
@ -274,8 +413,12 @@ impl XorgBackend {
|
|||
removed: Cell::new(false),
|
||||
width: Cell::new(0),
|
||||
height: Cell::new(0),
|
||||
serial: Default::default(),
|
||||
next_msc: Cell::new(0),
|
||||
next_image: Default::default(),
|
||||
image: RefCell::new(None),
|
||||
cb: CloneCell::new(None),
|
||||
images,
|
||||
});
|
||||
{
|
||||
let class = "i4\0i4\0";
|
||||
|
|
@ -356,6 +499,7 @@ impl XorgBackend {
|
|||
self.state
|
||||
.backend_events
|
||||
.push(BackendEvent::NewOutput(output.clone()));
|
||||
self.present(&output);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -495,14 +639,104 @@ impl XorgBackend {
|
|||
let event = unsafe {
|
||||
(event as *const _ as *const ffi::xcb_present_complete_notify_event_t).deref()
|
||||
};
|
||||
let output = match self.outputs.get(&event.window) {
|
||||
let window = event.window;
|
||||
let output = match self.outputs.get(&window) {
|
||||
Some(o) => o,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
output.next_msc.set(event.msc + 1);
|
||||
let image = &output.images[output.next_image.get() % output.images.len()];
|
||||
if image.idle.get() {
|
||||
self.present(&output);
|
||||
} else {
|
||||
image.render_on_idle.set(true);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_present_idle(
|
||||
self: &Rc<Self>,
|
||||
event: &ffi::xcb_ge_generic_event_t,
|
||||
) -> Result<(), XorgBackendError> {
|
||||
let event =
|
||||
unsafe { (event as *const _ as *const ffi::xcb_present_idle_notify_event_t).deref() };
|
||||
let output = match self.outputs.get(&event.window) {
|
||||
Some(o) => o,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
for image in &output.images {
|
||||
if image.last_serial.get() == event.serial {
|
||||
image.idle.set(true);
|
||||
if image.render_on_idle.replace(false) {
|
||||
self.present(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn present(&self, output: &Rc<XorgOutput>) {
|
||||
// {
|
||||
// let clients = self.state.clients.clients.borrow();
|
||||
// for client in clients.values() {
|
||||
// let s = client.data.objects.surfaces.lock();
|
||||
// for s in s.values() {
|
||||
// let mut fr = s.frame_requests.borrow_mut();
|
||||
// for cb in fr.drain(..) {
|
||||
// s.client.dispatch_frame_requests.push(cb);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
|
||||
let image = &output.images[output.next_image.fetch_add(1) % output.images.len()];
|
||||
let serial = output.serial.fetch_add(1);
|
||||
|
||||
if let Some(node) = self.state.root.outputs.get(&output.id) {
|
||||
let fb = image.fb.get();
|
||||
let mut renderer = self.renderer.render_fb(&fb);
|
||||
self.ctx
|
||||
.with_current(|| unsafe {
|
||||
fb.bind();
|
||||
glViewport(0, 0, fb.width, fb.height);
|
||||
// fb.clear(0.0, 0.0, 0.0, 1.0);
|
||||
renderer.render_output(&node);
|
||||
glFlush();
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let cookie = self.con.present.xcb_present_pixmap_checked(
|
||||
self.con.c,
|
||||
output.window,
|
||||
image.pixmap.get(),
|
||||
serial,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
output.next_msc.get(),
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
ptr::null(),
|
||||
);
|
||||
if let Err(e) = self.con.check_cookie(cookie) {
|
||||
log::error!("Could not present image: {:?}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
image.idle.set(false);
|
||||
image.last_serial.set(serial);
|
||||
}
|
||||
|
||||
fn handle_input_event(
|
||||
self: &Rc<Self>,
|
||||
event: &ffi::xcb_ge_generic_event_t,
|
||||
|
|
@ -567,6 +801,12 @@ impl XorgBackend {
|
|||
event: &ffi::xcb_ge_generic_event_t,
|
||||
state: KeyState,
|
||||
) -> Result<(), XorgBackendError> {
|
||||
if state == KeyState::Pressed {
|
||||
let mut rng = rand::thread_rng();
|
||||
self.r.set(rng.gen_range(0.0..1.0));
|
||||
self.g.set(rng.gen_range(0.0..1.0));
|
||||
self.b.set(rng.gen_range(0.0..1.0));
|
||||
}
|
||||
let event =
|
||||
unsafe { (event as *const _ as *const ffi::xcb_input_key_press_event_t).deref() };
|
||||
if let Some(seat) = self.seats.get(&event.deviceid) {
|
||||
|
|
@ -626,7 +866,10 @@ impl XorgBackend {
|
|||
event: &ffi::xcb_ge_generic_event_t,
|
||||
) -> Result<(), XorgBackendError> {
|
||||
let event = unsafe { (event as *const _ as *const ffi::xcb_input_motion_event_t).deref() };
|
||||
let (win, seat) = match (self.outputs.get(&event.event), self.mouse_seats.get(&event.deviceid)) {
|
||||
let (win, seat) = match (
|
||||
self.outputs.get(&event.event),
|
||||
self.mouse_seats.get(&event.deviceid),
|
||||
) {
|
||||
(Some(a), Some(b)) => (a, b),
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
|
@ -639,6 +882,7 @@ impl XorgBackend {
|
|||
}
|
||||
|
||||
fn handle_destroy(&self, event: &ffi::xcb_generic_event_t) -> Result<(), XorgBackendError> {
|
||||
self.state.el.stop();
|
||||
let event =
|
||||
unsafe { (event as *const _ as *const ffi::xcb_destroy_notify_event_t).deref() };
|
||||
let output = match self.outputs.remove(&event.event) {
|
||||
|
|
@ -658,56 +902,67 @@ impl XorgBackend {
|
|||
Some(o) => o,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let width = event.width as u32;
|
||||
let height = event.height as u32;
|
||||
let width = event.width as i32;
|
||||
let height = event.height as i32;
|
||||
let mut changed = false;
|
||||
changed |= output.width.replace(width as i32) != width as i32;
|
||||
changed |= output.height.replace(height as i32) != height as i32;
|
||||
changed |= output.width.replace(width) != width;
|
||||
changed |= output.height.replace(height) != height;
|
||||
if changed {
|
||||
let shm = Rc::new(ServerMem::new((width * height * 4) as usize)?);
|
||||
let fd = shm.fd();
|
||||
let image = Image::new(shm, pixman::X8R8G8B8, width, height, width * 4)?;
|
||||
*output.image.borrow_mut() = Some(image);
|
||||
unsafe {
|
||||
let fd = match uapi::fcntl_dupfd_cloexec(fd, 0) {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => return Err(XorgBackendError::DupfdFailed(e.into())),
|
||||
};
|
||||
let shmseg = self.con.xcb.xcb_generate_id(self.con.c);
|
||||
let cookie =
|
||||
self.con
|
||||
.shm
|
||||
.xcb_shm_attach_fd_checked(self.con.c, shmseg, fd.unwrap(), 0);
|
||||
self.con.check_cookie(cookie)?;
|
||||
let pixmap = self.con.xcb.xcb_generate_id(self.con.c);
|
||||
let cookie = self.con.shm.xcb_shm_create_pixmap(
|
||||
self.con.c,
|
||||
pixmap,
|
||||
output.window,
|
||||
width as _,
|
||||
height as _,
|
||||
24,
|
||||
shmseg,
|
||||
0,
|
||||
);
|
||||
self.con.check_cookie(cookie)?;
|
||||
let cookie = self.con.xcb.xcb_change_window_attributes_checked(
|
||||
self.con.c,
|
||||
output.window,
|
||||
ffi::XCB_CW_BACK_PIXMAP,
|
||||
&pixmap as *const _ as _,
|
||||
);
|
||||
self.con.check_cookie(cookie)?;
|
||||
self.con.xcb.xcb_free_pixmap(self.con.c, pixmap);
|
||||
self.con.shm.xcb_shm_detach(self.con.c, shmseg);
|
||||
let images = self.create_images(output.window, width, height).unwrap();
|
||||
for (new, old) in images.iter().zip(output.images.iter()) {
|
||||
unsafe {
|
||||
self.con.xcb.xcb_free_pixmap(self.con.c, old.pixmap.get());
|
||||
}
|
||||
old.fb.set(new.fb.get());
|
||||
old.pixmap.set(new.pixmap.get());
|
||||
}
|
||||
}
|
||||
// let shm = Rc::new(ServerMem::new((width * height * 4) as usize)?);
|
||||
// let fd = shm.fd();
|
||||
// let image = Image::new(shm, pixman::X8R8G8B8, width, height, width * 4)?;
|
||||
// *output.image.borrow_mut() = Some(image);
|
||||
// unsafe {
|
||||
// let fd = match uapi::fcntl_dupfd_cloexec(fd, 0) {
|
||||
// Ok(fd) => fd,
|
||||
// Err(e) => return Err(XorgBackendError::DupfdFailed(e.into())),
|
||||
// };
|
||||
// let shmseg = self.con.xcb.xcb_generate_id(self.con.c);
|
||||
// let cookie =
|
||||
// self.con
|
||||
// .shm
|
||||
// .xcb_shm_attach_fd_checked(self.con.c, shmseg, fd.unwrap(), 0);
|
||||
// self.con.check_cookie(cookie)?;
|
||||
// let pixmap = self.con.xcb.xcb_generate_id(self.con.c);
|
||||
// let cookie = self.con.shm.xcb_shm_create_pixmap(
|
||||
// self.con.c,
|
||||
// pixmap,
|
||||
// output.window,
|
||||
// width as _,
|
||||
// height as _,
|
||||
// 24,
|
||||
// shmseg,
|
||||
// 0,
|
||||
// );
|
||||
// self.con.check_cookie(cookie)?;
|
||||
// let cookie = self.con.xcb.xcb_change_window_attributes_checked(
|
||||
// self.con.c,
|
||||
// output.window,
|
||||
// ffi::XCB_CW_BACK_PIXMAP,
|
||||
// &pixmap as *const _ as _,
|
||||
// );
|
||||
// self.con.check_cookie(cookie)?;
|
||||
// self.con.xcb.xcb_free_pixmap(self.con.c, pixmap);
|
||||
// self.con.shm.xcb_shm_detach(self.con.c, shmseg);
|
||||
// }
|
||||
output.changed();
|
||||
self.render()?;
|
||||
// self.render()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render(&self) -> Result<(), XorgBackendError> {
|
||||
return Ok(());
|
||||
let outputs = self.outputs.lock();
|
||||
for output in outputs.values() {
|
||||
let image = output.image.borrow();
|
||||
|
|
@ -774,15 +1029,20 @@ struct XorgOutput {
|
|||
removed: Cell<bool>,
|
||||
width: Cell<i32>,
|
||||
height: Cell<i32>,
|
||||
serial: NumCell<u32>,
|
||||
next_msc: Cell<u64>,
|
||||
next_image: Cell<usize>,
|
||||
// images: [XorgImage; 2],
|
||||
next_image: NumCell<usize>,
|
||||
images: [XorgImage; 2],
|
||||
image: RefCell<Option<Image<Rc<ServerMem>>>>,
|
||||
cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||
}
|
||||
|
||||
struct XorgImage {
|
||||
|
||||
pixmap: Cell<ffi::xcb_pixmap_t>,
|
||||
fb: CloneCell<Rc<GlFrameBuffer>>,
|
||||
idle: Cell<bool>,
|
||||
render_on_idle: Cell<bool>,
|
||||
last_serial: Cell<u32>,
|
||||
}
|
||||
|
||||
impl Drop for XorgOutput {
|
||||
|
|
|
|||
16
src/drm/dma.rs
Normal file
16
src/drm/dma.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
use crate::format::Format;
|
||||
use uapi::OwnedFd;
|
||||
|
||||
pub struct DmaBufPlane {
|
||||
pub offset: u32,
|
||||
pub stride: u32,
|
||||
pub fd: OwnedFd,
|
||||
}
|
||||
|
||||
pub struct DmaBuf {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub format: &'static Format,
|
||||
pub modifier: u64,
|
||||
pub planes: Vec<DmaBufPlane>,
|
||||
}
|
||||
395
src/drm/drm.rs
Normal file
395
src/drm/drm.rs
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
use crate::utils::bitflags::BitflagsExt;
|
||||
use crate::utils::debug_fn::debug_fn;
|
||||
use crate::utils::ptr_ext::PtrExt;
|
||||
use bstr::ByteSlice;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::ptr;
|
||||
use thiserror::Error;
|
||||
use uapi::c::c_char;
|
||||
use uapi::{c, Errno, OwnedFd, Ustring};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DrmError {
|
||||
#[error("Could not create a lease")]
|
||||
CreateLeaseError(#[source] std::io::Error),
|
||||
#[error("Could not reopen a node")]
|
||||
ReopenNode(#[source] std::io::Error),
|
||||
#[error("Could not retrieve the render node name")]
|
||||
RenderNodeName,
|
||||
#[error("Could not retrieve the device node name")]
|
||||
DeviceNodeName,
|
||||
#[error("Could not retrieve device")]
|
||||
GetDevice(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
const DRM_NODE_PRIMARY: c::c_int = 0;
|
||||
const DRM_NODE_CONTROL: c::c_int = 1;
|
||||
const DRM_NODE_RENDER: c::c_int = 2;
|
||||
const DRM_NODE_MAX: c::c_int = 3;
|
||||
|
||||
const DRM_BUS_PCI: c::c_int = 0;
|
||||
const DRM_BUS_USB: c::c_int = 1;
|
||||
const DRM_BUS_PLATFORM: c::c_int = 2;
|
||||
const DRM_BUS_HOST1X: c::c_int = 3;
|
||||
|
||||
#[link(name = "drm")]
|
||||
extern "C" {
|
||||
fn drmIsMaster(fd: c::c_int) -> c::c_int;
|
||||
fn drmModeCreateLease(
|
||||
fd: c::c_int,
|
||||
o: *const u32,
|
||||
num_objects: c::c_int,
|
||||
flags: c::c_int,
|
||||
lessee_id: *mut u32,
|
||||
) -> c::c_int;
|
||||
fn drmGetNodeTypeFromFd(fd: c::c_int) -> c::c_int;
|
||||
fn drmGetRenderDeviceNameFromFd(fd: c::c_int) -> *mut c::c_char;
|
||||
fn drmGetDeviceNameFromFd2(fd: c::c_int) -> *mut c::c_char;
|
||||
fn drmFreeDevice(device: *mut *mut drmDevice);
|
||||
fn drmGetDevice(fd: c::c_int, device: *mut *mut drmDevice) -> c::c_int;
|
||||
}
|
||||
|
||||
fn render_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
||||
unsafe {
|
||||
let name = drmGetRenderDeviceNameFromFd(fd);
|
||||
if name.is_null() {
|
||||
Err(DrmError::RenderNodeName)
|
||||
} else {
|
||||
Ok(CString::from_raw(name).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn device_node_name(fd: c::c_int) -> Result<Ustring, DrmError> {
|
||||
unsafe {
|
||||
let name = drmGetDeviceNameFromFd2(fd);
|
||||
if name.is_null() {
|
||||
Err(DrmError::DeviceNodeName)
|
||||
} else {
|
||||
Ok(CString::from_raw(name).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn reopen(fd: c::c_int, allow_downgrade: bool) -> Result<OwnedFd, DrmError> {
|
||||
unsafe {
|
||||
if drmIsMaster(fd) != 0 {
|
||||
let mut lessee = 0;
|
||||
let lease_fd = drmModeCreateLease(fd, ptr::null(), 0, c::O_CLOEXEC, &mut lessee);
|
||||
if lease_fd >= 0 {
|
||||
return Ok(OwnedFd::new(lease_fd));
|
||||
}
|
||||
}
|
||||
let path = if drmGetNodeTypeFromFd(fd) == DRM_NODE_RENDER {
|
||||
uapi::format_ustr!("/proc/self/fd/{}", fd)
|
||||
} else if allow_downgrade {
|
||||
render_node_name(fd)?
|
||||
} else {
|
||||
device_node_name(fd)?
|
||||
};
|
||||
match uapi::open(&path, c::O_RDWR | c::O_CLOEXEC, 0) {
|
||||
Ok(f) => Ok(f),
|
||||
Err(e) => Err(DrmError::ReopenNode(e.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Drm {
|
||||
fd: OwnedFd,
|
||||
}
|
||||
|
||||
impl Drm {
|
||||
pub fn new(fd: c::c_int, allow_downgrade: bool) -> Result<Self, DrmError> {
|
||||
Ok(Self {
|
||||
fd: reopen(fd, allow_downgrade)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> c::c_int {
|
||||
self.fd.raw()
|
||||
}
|
||||
|
||||
pub fn dup_unprivileged(&self) -> Result<Self, DrmError> {
|
||||
Self::new(self.fd.raw(), true)
|
||||
}
|
||||
|
||||
pub fn get_device(&self) -> Result<DrmDevice, DrmError> {
|
||||
unsafe {
|
||||
let mut dev = ptr::null_mut();
|
||||
if drmGetDevice(self.fd.raw(), &mut dev) < 0 {
|
||||
return Err(DrmError::GetDevice(Errno::default().into()));
|
||||
}
|
||||
Ok(DrmDevice { dev })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmPciBusInfo {
|
||||
domain: u16,
|
||||
bus: u8,
|
||||
dev: u8,
|
||||
func: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmUsbBusInfo {
|
||||
bus: u8,
|
||||
dev: u8,
|
||||
}
|
||||
|
||||
const DRM_PLATFORM_DEVICE_NAME_LEN: usize = 512;
|
||||
|
||||
#[repr(C)]
|
||||
struct drmPlatformBusInfo {
|
||||
fullname: [c::c_char; DRM_PLATFORM_DEVICE_NAME_LEN],
|
||||
}
|
||||
|
||||
const DRM_HOST1X_DEVICE_NAME_LEN: usize = 512;
|
||||
|
||||
#[repr(C)]
|
||||
struct drmHost1xBusInfo {
|
||||
fullname: [c::c_char; DRM_HOST1X_DEVICE_NAME_LEN],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
union businfo {
|
||||
pci: *mut drmPciBusInfo,
|
||||
usb: *mut drmUsbBusInfo,
|
||||
platform: *mut drmPlatformBusInfo,
|
||||
host1x: *mut drmHost1xBusInfo,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmPciDeviceInfo {
|
||||
vendor_id: u16,
|
||||
device_id: u16,
|
||||
subvendor_id: u16,
|
||||
subdevice_id: u16,
|
||||
revision_id: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmUsbDeviceInfo {
|
||||
vendor: u16,
|
||||
product: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmPlatformDeviceInfo {
|
||||
compatible: *mut *mut c::c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmHost1xDeviceInfo {
|
||||
compatible: *mut *mut c::c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
union deviceinfo {
|
||||
pci: *mut drmPciDeviceInfo,
|
||||
usb: *mut drmUsbDeviceInfo,
|
||||
platform: *mut drmPlatformDeviceInfo,
|
||||
host1x: *mut drmHost1xDeviceInfo,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct drmDevice {
|
||||
nodes: *mut *mut c::c_char,
|
||||
available_nodes: c::c_int,
|
||||
bustype: c::c_int,
|
||||
businfo: businfo,
|
||||
deviceinfo: deviceinfo,
|
||||
}
|
||||
|
||||
pub struct DrmDevice {
|
||||
dev: *mut drmDevice,
|
||||
}
|
||||
|
||||
impl Drop for DrmDevice {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
drmFreeDevice(&mut self.dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DrmDevice {
|
||||
pub fn nodes<'a>(&'a self) -> impl Iterator<Item = (c::c_int, &'a CStr)> + 'a {
|
||||
struct Iter<'a> {
|
||||
next: usize,
|
||||
dev: &'a DrmDevice,
|
||||
}
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = (c::c_int, &'a CStr);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
let dev = self.dev.dev.deref();
|
||||
while self.next < DRM_NODE_MAX as _ {
|
||||
let idx = self.next;
|
||||
self.next += 1;
|
||||
if dev.available_nodes.contains(1 << idx) {
|
||||
return Some((idx as _, CStr::from_ptr(*dev.nodes.add(idx))));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
Iter { next: 0, dev: self }
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DrmDevice {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
struct StrStr<'a> {
|
||||
v: &'a [*mut c::c_char],
|
||||
}
|
||||
impl Debug for StrStr<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let mut list = f.debug_list();
|
||||
for &v in self.v {
|
||||
if v.is_null() {
|
||||
list.entry(&v);
|
||||
} else {
|
||||
unsafe {
|
||||
list.entry(&CStr::from_ptr(v));
|
||||
}
|
||||
}
|
||||
}
|
||||
list.finish()
|
||||
}
|
||||
}
|
||||
impl<'a> StrStr<'a> {
|
||||
fn from_nt(nt: *const *mut c_char) -> Self {
|
||||
unsafe {
|
||||
let mut num = 0;
|
||||
let mut tmp = nt;
|
||||
while !tmp.deref().is_null() {
|
||||
num += 1;
|
||||
tmp = tmp.add(1);
|
||||
}
|
||||
Self {
|
||||
v: std::slice::from_raw_parts(nt, num),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut ds = f.debug_struct("DrmDevice");
|
||||
unsafe {
|
||||
let dev = self.dev.deref();
|
||||
let nodes = std::slice::from_raw_parts(dev.nodes, DRM_NODE_MAX as _);
|
||||
ds.field(
|
||||
"available_nodes",
|
||||
&debug_fn(|f| write!(f, "0b{:b}", dev.available_nodes)),
|
||||
);
|
||||
ds.field("nodes", &StrStr { v: nodes });
|
||||
ds.field("bustype", &dev.bustype);
|
||||
match dev.bustype {
|
||||
DRM_BUS_PCI => {
|
||||
ds.field(
|
||||
"businfo",
|
||||
&debug_fn(|f| {
|
||||
let pci = dev.businfo.pci.deref();
|
||||
f.debug_struct("drmPciBusInfo")
|
||||
.field("domain", &pci.domain)
|
||||
.field("bus", &pci.bus)
|
||||
.field("dev", &pci.dev)
|
||||
.field("func", &pci.func)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
ds.field(
|
||||
"deviceinfo",
|
||||
&debug_fn(|f| {
|
||||
let pci = dev.deviceinfo.pci.deref();
|
||||
f.debug_struct("drmPciDeviceInfo")
|
||||
.field("vendor_id", &pci.vendor_id)
|
||||
.field("device_id", &pci.device_id)
|
||||
.field("subvendor_id", &pci.subvendor_id)
|
||||
.field("subdevice_id", &pci.subdevice_id)
|
||||
.field("revision_id", &pci.revision_id)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
}
|
||||
DRM_BUS_USB => {
|
||||
ds.field(
|
||||
"businfo",
|
||||
&debug_fn(|f| {
|
||||
let usb = dev.businfo.usb.deref();
|
||||
f.debug_struct("drmUsbBusInfo")
|
||||
.field("bus", &usb.bus)
|
||||
.field("dev", &usb.dev)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
ds.field(
|
||||
"deviceinfo",
|
||||
&debug_fn(|f| {
|
||||
let usb = dev.deviceinfo.usb.deref();
|
||||
f.debug_struct("drmUsbDeviceInfo")
|
||||
.field("vendor", &usb.vendor)
|
||||
.field("product", &usb.product)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
}
|
||||
DRM_BUS_PLATFORM => {
|
||||
ds.field(
|
||||
"businfo",
|
||||
&debug_fn(|f| {
|
||||
let platform = dev.businfo.platform.deref();
|
||||
f.debug_struct("drmPlatformBusInfo")
|
||||
.field(
|
||||
"fullname",
|
||||
&CStr::from_ptr(platform.fullname.as_ptr())
|
||||
.to_bytes()
|
||||
.as_bstr(),
|
||||
)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
ds.field(
|
||||
"deviceinfo",
|
||||
&debug_fn(|f| {
|
||||
let platform = dev.deviceinfo.platform.deref();
|
||||
f.debug_struct("drmPlatformDeviceInfo")
|
||||
.field("compatible", &StrStr::from_nt(platform.compatible))
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
}
|
||||
DRM_BUS_HOST1X => {
|
||||
ds.field(
|
||||
"businfo",
|
||||
&debug_fn(|f| {
|
||||
let host1x = dev.businfo.host1x.deref();
|
||||
f.debug_struct("drmHost1xBusInfo")
|
||||
.field(
|
||||
"fullname",
|
||||
&CStr::from_ptr(host1x.fullname.as_ptr())
|
||||
.to_bytes()
|
||||
.as_bstr(),
|
||||
)
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
ds.field(
|
||||
"deviceinfo",
|
||||
&debug_fn(|f| {
|
||||
let host1x = dev.deviceinfo.host1x.deref();
|
||||
f.debug_struct("drmHost1xDeviceInfo")
|
||||
.field("compatible", &StrStr::from_nt(host1x.compatible))
|
||||
.finish()
|
||||
}),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ds.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
169
src/drm/gbm.rs
Normal file
169
src/drm/gbm.rs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
use crate::drm::dma::{DmaBuf, DmaBufPlane};
|
||||
use crate::drm::drm::{Drm, DrmError};
|
||||
use crate::drm::{ModifiedFormat, INVALID_MODIFIER};
|
||||
use crate::format::formats;
|
||||
use std::ptr;
|
||||
use thiserror::Error;
|
||||
use uapi::{c, OwnedFd};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GbmError {
|
||||
#[error("The drm subsystem returned an error")]
|
||||
Drm(#[from] DrmError),
|
||||
#[error("Cloud not create a gbm device")]
|
||||
CreateDevice,
|
||||
#[error("Cloud not create a gbm buffer")]
|
||||
CreateBo,
|
||||
#[error("gbm buffer has an unknown format")]
|
||||
UnknownFormat,
|
||||
#[error("Could not retrieve a drm-buf fd")]
|
||||
DrmFd,
|
||||
}
|
||||
|
||||
type Device = u8;
|
||||
type Bo = u8;
|
||||
|
||||
pub const GBM_BO_USE_SCANOUT: u32 = 1 << 0;
|
||||
pub const GBM_BO_USE_CURSOR: u32 = 1 << 1;
|
||||
pub const GBM_BO_USE_RENDERING: u32 = 1 << 2;
|
||||
pub const GBM_BO_USE_WRITE: u32 = 1 << 3;
|
||||
pub const GBM_BO_USE_LINEAR: u32 = 1 << 4;
|
||||
pub const GBM_BO_USE_PROTECTED: u32 = 1 << 5;
|
||||
|
||||
#[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_create_with_modifiers2(
|
||||
dev: *mut Device,
|
||||
width: u32,
|
||||
height: u32,
|
||||
format: u32,
|
||||
modifiers: *const u64,
|
||||
count: c::c_uint,
|
||||
flags: u32,
|
||||
) -> *mut Bo;
|
||||
fn gbm_bo_destroy(bo: *mut Bo);
|
||||
fn gbm_bo_get_plane_count(bo: *mut Bo) -> c::c_int;
|
||||
fn gbm_bo_get_width(bo: *mut Bo) -> u32;
|
||||
fn gbm_bo_get_height(bo: *mut Bo) -> u32;
|
||||
fn gbm_bo_get_stride(bo: *mut Bo) -> u32;
|
||||
fn gbm_bo_get_modifier(bo: *mut Bo) -> u64;
|
||||
fn gbm_bo_get_stride_for_plane(bo: *mut Bo, plane: c::c_int) -> u32;
|
||||
fn gbm_bo_get_fd_for_plane(bo: *mut Bo, plane: c::c_int) -> c::c_int;
|
||||
fn gbm_bo_get_offset(bo: *mut Bo, plane: c::c_int) -> u32;
|
||||
fn gbm_bo_get_format(bo: *mut Bo) -> u32;
|
||||
fn gbm_bo_get_bpp(bo: *mut Bo) -> u32;
|
||||
}
|
||||
|
||||
pub struct GbmDevice {
|
||||
drm: Drm,
|
||||
dev: *mut Device,
|
||||
}
|
||||
|
||||
struct BoHolder {
|
||||
bo: *mut Bo,
|
||||
}
|
||||
|
||||
pub struct GbmBo {
|
||||
bo: BoHolder,
|
||||
dma: DmaBuf,
|
||||
}
|
||||
|
||||
unsafe fn export_bo(bo: *mut Bo) -> Result<DmaBuf, GbmError> {
|
||||
Ok(DmaBuf {
|
||||
width: gbm_bo_get_width(bo) as _,
|
||||
height: gbm_bo_get_height(bo) as _,
|
||||
modifier: gbm_bo_get_modifier(bo),
|
||||
format: {
|
||||
let format = gbm_bo_get_format(bo);
|
||||
match formats().get(&format).copied() {
|
||||
Some(f) => f,
|
||||
_ => return Err(GbmError::UnknownFormat),
|
||||
}
|
||||
},
|
||||
planes: {
|
||||
let mut planes = vec![];
|
||||
for plane in 0..gbm_bo_get_plane_count(bo) {
|
||||
let offset = gbm_bo_get_offset(bo, plane);
|
||||
let stride = gbm_bo_get_stride_for_plane(bo, plane);
|
||||
let fd = gbm_bo_get_fd_for_plane(bo, plane);
|
||||
if fd < 0 {
|
||||
return Err(GbmError::DrmFd);
|
||||
}
|
||||
planes.push(DmaBufPlane {
|
||||
offset,
|
||||
stride,
|
||||
fd: OwnedFd::new(fd),
|
||||
})
|
||||
}
|
||||
planes
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
impl GbmDevice {
|
||||
pub fn new(drm: &Drm) -> Result<Self, GbmError> {
|
||||
let drm = drm.dup_unprivileged()?;
|
||||
let dev = unsafe { gbm_create_device(drm.raw()) };
|
||||
if dev.is_null() {
|
||||
Err(GbmError::CreateDevice)
|
||||
} else {
|
||||
Ok(Self { drm, dev })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_bo(
|
||||
&self,
|
||||
width: i32,
|
||||
height: i32,
|
||||
format: &ModifiedFormat,
|
||||
usage: u32,
|
||||
) -> Result<GbmBo, GbmError> {
|
||||
unsafe {
|
||||
let (modifiers, n_modifiers) = if format.modifier == INVALID_MODIFIER {
|
||||
(ptr::null(), 0)
|
||||
} else {
|
||||
(&format.modifier as _, 1)
|
||||
};
|
||||
let bo = gbm_bo_create_with_modifiers2(
|
||||
self.dev,
|
||||
width as _,
|
||||
height as _,
|
||||
format.format.drm,
|
||||
modifiers,
|
||||
n_modifiers,
|
||||
usage,
|
||||
);
|
||||
if bo.is_null() {
|
||||
return Err(GbmError::CreateBo);
|
||||
}
|
||||
let bo = BoHolder { bo };
|
||||
let dma = export_bo(bo.bo)?;
|
||||
Ok(GbmBo { bo, dma })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GbmDevice {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gbm_device_destroy(self.dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GbmBo {
|
||||
pub fn dma(&self) -> &DmaBuf {
|
||||
&self.dma
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BoHolder {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gbm_bo_destroy(self.bo);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/drm/mod.rs
Normal file
14
src/drm/mod.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
use crate::format::Format;
|
||||
|
||||
pub mod dma;
|
||||
pub mod drm;
|
||||
pub mod gbm;
|
||||
|
||||
pub type Modifier = u64;
|
||||
|
||||
pub const INVALID_MODIFIER: Modifier = 0x00ff_ffff_ffff_ffff;
|
||||
|
||||
pub struct ModifiedFormat {
|
||||
pub format: &'static Format,
|
||||
pub modifier: Modifier,
|
||||
}
|
||||
|
|
@ -1,20 +1,29 @@
|
|||
use crate::gles2::sys::{GLint, GL_BGRA_EXT, GL_UNSIGNED_BYTE};
|
||||
use crate::pixman;
|
||||
use ahash::AHashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Format {
|
||||
pub id: u32,
|
||||
pub name: &'static str,
|
||||
pub bpp: u32,
|
||||
pub pixman: pixman::Format,
|
||||
pub pixman: Option<pixman::Format>,
|
||||
pub gl_format: GLint,
|
||||
pub gl_type: GLint,
|
||||
pub drm: u32,
|
||||
pub wl_id: Option<u32>,
|
||||
}
|
||||
|
||||
pub fn formats() -> AHashMap<u32, &'static Format> {
|
||||
static FORMATS_MAP: Lazy<AHashMap<u32, &'static Format>> = Lazy::new(|| {
|
||||
let mut map = AHashMap::new();
|
||||
for format in FORMATS {
|
||||
assert!(map.insert(format.id, format).is_none());
|
||||
assert!(map.insert(format.drm, format).is_none());
|
||||
}
|
||||
map
|
||||
});
|
||||
|
||||
pub fn formats() -> &'static AHashMap<u32, &'static Format> {
|
||||
&*FORMATS_MAP
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -22,18 +31,41 @@ const fn fourcc_code(a: char, b: char, c: char, d: char) -> u32 {
|
|||
(a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
|
||||
}
|
||||
|
||||
static FORMATS: &[Format] = &[
|
||||
const ARGB8888_ID: u32 = 0;
|
||||
const ARGB8888_DRM: u32 = fourcc_code('A', 'R', '2', '4');
|
||||
|
||||
const XRGB8888_ID: u32 = 1;
|
||||
const XRGB8888_DRM: u32 = fourcc_code('X', 'R', '2', '4');
|
||||
|
||||
pub fn map_wayland_format_id(id: u32) -> u32 {
|
||||
match id {
|
||||
ARGB8888_ID => ARGB8888_DRM,
|
||||
XRGB8888_ID => XRGB8888_DRM,
|
||||
_ => id,
|
||||
}
|
||||
}
|
||||
|
||||
pub static ARGB8888: &Format = &FORMATS[0];
|
||||
pub static XRGB8888: &Format = &FORMATS[1];
|
||||
|
||||
pub static FORMATS: &[Format] = &[
|
||||
Format {
|
||||
id: 0,
|
||||
name: "argb8888",
|
||||
bpp: 4,
|
||||
pixman: pixman::A8R8G8B8,
|
||||
pixman: Some(pixman::A8R8G8B8),
|
||||
gl_format: GL_BGRA_EXT,
|
||||
gl_type: GL_UNSIGNED_BYTE,
|
||||
drm: ARGB8888_DRM,
|
||||
wl_id: Some(ARGB8888_ID),
|
||||
},
|
||||
Format {
|
||||
id: 1,
|
||||
name: "xrgb8888",
|
||||
bpp: 4,
|
||||
pixman: pixman::X8R8G8B8,
|
||||
pixman: Some(pixman::X8R8G8B8),
|
||||
gl_format: GL_BGRA_EXT,
|
||||
gl_type: GL_UNSIGNED_BYTE,
|
||||
drm: XRGB8888_DRM,
|
||||
wl_id: Some(XRGB8888_ID),
|
||||
},
|
||||
// Format {
|
||||
// id: fourcc_code('C', '8', ' ', ' '),
|
||||
|
|
|
|||
487
src/gles2/egl.rs
Normal file
487
src/gles2/egl.rs
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
use super::ext::{get_client_ext, ClientExt};
|
||||
use super::ext_proc::ExtProc;
|
||||
use super::sys::{
|
||||
eglBindAPI, EGLAttrib, EGLLabelKHR, EGLenum, EGLint, EGL_BAD_ACCESS, EGL_BAD_ALLOC,
|
||||
EGL_BAD_ATTRIBUTE, EGL_BAD_CONFIG, EGL_BAD_CONTEXT, EGL_BAD_CURRENT_SURFACE,
|
||||
EGL_BAD_DEVICE_EXT, EGL_BAD_DISPLAY, EGL_BAD_MATCH, EGL_BAD_NATIVE_PIXMAP,
|
||||
EGL_BAD_NATIVE_WINDOW, EGL_BAD_PARAMETER, EGL_BAD_SURFACE, EGL_CONTEXT_LOST,
|
||||
EGL_DEBUG_MSG_CRITICAL_KHR, EGL_DEBUG_MSG_ERROR_KHR, EGL_DEBUG_MSG_INFO_KHR,
|
||||
EGL_DEBUG_MSG_WARN_KHR, EGL_NONE, EGL_NOT_INITIALIZED, EGL_OPENGL_ES_API, EGL_SUCCESS,
|
||||
EGL_TRUE,
|
||||
};
|
||||
use super::GlesError;
|
||||
use crate::drm::dma::DmaBuf;
|
||||
use crate::drm::drm::Drm;
|
||||
use crate::drm::INVALID_MODIFIER;
|
||||
use crate::format::{formats, Format};
|
||||
use crate::gles2::ext::{
|
||||
get_device_ext, get_display_ext, get_gl_ext, DeviceExt, DisplayExt, GlExt,
|
||||
};
|
||||
use crate::gles2::gl::GlTexture;
|
||||
use crate::gles2::sys::{
|
||||
eglCreateContext, eglDestroyContext, eglInitialize, eglMakeCurrent, eglTerminate,
|
||||
glBindFramebuffer, glBindRenderbuffer, glBindTexture, glCheckFramebufferStatus, glClear,
|
||||
glClearColor, glDeleteFramebuffers, glDeleteRenderbuffers, glFlush, glFramebufferRenderbuffer,
|
||||
glGenFramebuffers, glGenRenderbuffers, glGenTextures, glPixelStorei, glTexImage2D,
|
||||
glTexParameteri, EGLClientBuffer, EGLConfig, EGLContext, EGLDeviceEXT, EGLDisplay, EGLImageKHR,
|
||||
EGLSurface, GLeglImageOES, GLint, GLuint, EGL_CONTEXT_CLIENT_VERSION,
|
||||
EGL_DMA_BUF_PLANE0_FD_EXT, EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
|
||||
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE0_OFFSET_EXT,
|
||||
EGL_DMA_BUF_PLANE0_PITCH_EXT, EGL_DMA_BUF_PLANE1_FD_EXT, EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
|
||||
EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE1_OFFSET_EXT,
|
||||
EGL_DMA_BUF_PLANE1_PITCH_EXT, EGL_DMA_BUF_PLANE2_FD_EXT, EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
|
||||
EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE2_OFFSET_EXT,
|
||||
EGL_DMA_BUF_PLANE2_PITCH_EXT, EGL_DMA_BUF_PLANE3_FD_EXT, EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
|
||||
EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGL_DMA_BUF_PLANE3_OFFSET_EXT,
|
||||
EGL_DMA_BUF_PLANE3_PITCH_EXT, EGL_DRM_DEVICE_FILE_EXT, EGL_FALSE, EGL_HEIGHT,
|
||||
EGL_IMAGE_PRESERVED_KHR, EGL_LINUX_DMA_BUF_EXT, EGL_LINUX_DRM_FOURCC_EXT,
|
||||
EGL_PLATFORM_DEVICE_EXT, EGL_WIDTH, GL_CLAMP_TO_EDGE, GL_COLOR_ATTACHMENT0,
|
||||
GL_COLOR_BUFFER_BIT, GL_FRAMEBUFFER, GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER, GL_TEXTURE_2D,
|
||||
GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_UNPACK_ROW_LENGTH_EXT,
|
||||
};
|
||||
use crate::rect::Rect;
|
||||
use ahash::AHashMap;
|
||||
use bstr::ByteSlice;
|
||||
use log::Level;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::cell::Cell;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use uapi::c;
|
||||
|
||||
#[thread_local]
|
||||
pub(super) static PROCS: Lazy<ExtProc> = Lazy::new(|| ExtProc::load());
|
||||
|
||||
#[thread_local]
|
||||
pub(super) static EXTS: Lazy<ClientExt> = Lazy::new(|| get_client_ext());
|
||||
|
||||
pub fn init() -> Result<(), GlesError> {
|
||||
if !EXTS.contains(ClientExt::EXT_PLATFORM_BASE) {
|
||||
return Err(GlesError::ExtPlatformBase);
|
||||
}
|
||||
if !EXTS.device_query() {
|
||||
return Err(GlesError::DeviceQuery);
|
||||
}
|
||||
if !EXTS.device_enumeration() {
|
||||
return Err(GlesError::DeviceEnumeration);
|
||||
}
|
||||
if EXTS.contains(ClientExt::KHR_DEBUG) {
|
||||
let attrib: &[EGLAttrib] = &[
|
||||
EGL_DEBUG_MSG_CRITICAL_KHR as _,
|
||||
EGL_TRUE as _,
|
||||
EGL_DEBUG_MSG_ERROR_KHR as _,
|
||||
EGL_TRUE as _,
|
||||
EGL_DEBUG_MSG_WARN_KHR as _,
|
||||
EGL_TRUE as _,
|
||||
EGL_DEBUG_MSG_INFO_KHR as _,
|
||||
EGL_TRUE as _,
|
||||
EGL_NONE as _,
|
||||
];
|
||||
unsafe {
|
||||
PROCS.eglDebugMessageControlKHR(egl_log, attrib.as_ptr());
|
||||
}
|
||||
}
|
||||
if unsafe { eglBindAPI(EGL_OPENGL_ES_API) } != EGL_TRUE {
|
||||
return Err(GlesError::BindFailed);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn find_drm_device(drm: &Drm) -> Result<Option<EglDevice>, GlesError> {
|
||||
let drm_dev = drm.get_device()?;
|
||||
for device in query_devices()? {
|
||||
if device.exts.contains(DeviceExt::EXT_DEVICE_DRM) {
|
||||
let device_file = device.query_string(EGL_DRM_DEVICE_FILE_EXT)?;
|
||||
for (_, name) in drm_dev.nodes() {
|
||||
if device_file == name {
|
||||
return Ok(Some(device));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn query_devices() -> Result<Vec<EglDevice>, GlesError> {
|
||||
if !EXTS.device_enumeration() {
|
||||
return Err(GlesError::DeviceEnumeration);
|
||||
}
|
||||
unsafe {
|
||||
let mut devices = vec![];
|
||||
let mut num_devices = 0;
|
||||
let res = PROCS.eglQueryDevicesEXT(num_devices, ptr::null_mut(), &mut num_devices);
|
||||
if res != EGL_TRUE {
|
||||
return Err(GlesError::QueryDevices);
|
||||
}
|
||||
devices.reserve_exact(num_devices as usize);
|
||||
let res = PROCS.eglQueryDevicesEXT(num_devices, devices.as_mut_ptr(), &mut num_devices);
|
||||
if res != EGL_TRUE {
|
||||
return Err(GlesError::QueryDevices);
|
||||
}
|
||||
devices.set_len(num_devices as usize);
|
||||
Ok(devices
|
||||
.into_iter()
|
||||
.map(|d| EglDevice {
|
||||
exts: get_device_ext(d),
|
||||
dev: d,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EglDevice {
|
||||
pub exts: DeviceExt,
|
||||
dev: EGLDeviceEXT,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EglDisplay {
|
||||
pub exts: DisplayExt,
|
||||
pub formats: AHashMap<u32, &'static Format>,
|
||||
dev: EglDevice,
|
||||
dpy: EGLDisplay,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EglContext {
|
||||
pub dpy: Rc<EglDisplay>,
|
||||
pub ext: GlExt,
|
||||
ctx: EGLContext,
|
||||
|
||||
pub tex_prog: GLuint,
|
||||
pub tex_tex: GLint,
|
||||
pub tex_texcoord: GLint,
|
||||
pub tex_pos: GLint,
|
||||
}
|
||||
|
||||
impl EglDisplay {
|
||||
pub fn create_context(self: &Rc<Self>) -> Result<Rc<EglContext>, GlesError> {
|
||||
let attrib = [EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE];
|
||||
unsafe {
|
||||
let ctx = eglCreateContext(
|
||||
self.dpy,
|
||||
EGLConfig::none(),
|
||||
EGLContext::none(),
|
||||
attrib.as_ptr(),
|
||||
);
|
||||
if ctx.is_none() {
|
||||
return Err(GlesError::CreateContext);
|
||||
}
|
||||
let mut ctx = EglContext {
|
||||
dpy: self.clone(),
|
||||
ext: GlExt::empty(),
|
||||
ctx,
|
||||
tex_prog: 0,
|
||||
tex_tex: 0,
|
||||
tex_texcoord: 0,
|
||||
tex_pos: 0,
|
||||
};
|
||||
ctx.ext = ctx.with_current(|| Ok(get_gl_ext()))?;
|
||||
// if !ctx.ext.contains(GlExt::GL_OES_EGL_IMAGE) {
|
||||
// return Err(GlesError::OesEglImage);
|
||||
// }
|
||||
Ok(Rc::new(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import_dmabuf(self: &Rc<Self>, buf: &DmaBuf) -> Result<Rc<EglImage>, GlesError> {
|
||||
struct PlaneKey {
|
||||
fd: EGLint,
|
||||
offset: EGLint,
|
||||
pitch: EGLint,
|
||||
mod_lo: EGLint,
|
||||
mod_hi: EGLint,
|
||||
}
|
||||
const PLANE_KEYS: [PlaneKey; 4] = [
|
||||
PlaneKey {
|
||||
fd: EGL_DMA_BUF_PLANE0_FD_EXT,
|
||||
offset: EGL_DMA_BUF_PLANE0_OFFSET_EXT,
|
||||
pitch: EGL_DMA_BUF_PLANE0_PITCH_EXT,
|
||||
mod_lo: EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
|
||||
mod_hi: EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
|
||||
},
|
||||
PlaneKey {
|
||||
fd: EGL_DMA_BUF_PLANE1_FD_EXT,
|
||||
offset: EGL_DMA_BUF_PLANE1_OFFSET_EXT,
|
||||
pitch: EGL_DMA_BUF_PLANE1_PITCH_EXT,
|
||||
mod_lo: EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT,
|
||||
mod_hi: EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
|
||||
},
|
||||
PlaneKey {
|
||||
fd: EGL_DMA_BUF_PLANE2_FD_EXT,
|
||||
offset: EGL_DMA_BUF_PLANE2_OFFSET_EXT,
|
||||
pitch: EGL_DMA_BUF_PLANE2_PITCH_EXT,
|
||||
mod_lo: EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT,
|
||||
mod_hi: EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT,
|
||||
},
|
||||
PlaneKey {
|
||||
fd: EGL_DMA_BUF_PLANE3_FD_EXT,
|
||||
offset: EGL_DMA_BUF_PLANE3_OFFSET_EXT,
|
||||
pitch: EGL_DMA_BUF_PLANE3_PITCH_EXT,
|
||||
mod_lo: EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT,
|
||||
mod_hi: EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT,
|
||||
},
|
||||
];
|
||||
|
||||
let mut attribs = vec![];
|
||||
attribs.extend_from_slice(&[EGL_WIDTH, buf.width]);
|
||||
attribs.extend_from_slice(&[EGL_HEIGHT, buf.height]);
|
||||
attribs.extend_from_slice(&[EGL_LINUX_DRM_FOURCC_EXT, buf.format.drm as _]);
|
||||
attribs.extend_from_slice(&[EGL_IMAGE_PRESERVED_KHR, EGL_TRUE as _]);
|
||||
for (key, plane) in PLANE_KEYS.iter().zip(buf.planes.iter()) {
|
||||
attribs.extend_from_slice(&[key.fd, plane.fd.raw()]);
|
||||
attribs.extend_from_slice(&[key.pitch, plane.stride as _]);
|
||||
attribs.extend_from_slice(&[key.offset, plane.offset as _]);
|
||||
if buf.modifier != INVALID_MODIFIER {
|
||||
attribs.extend_from_slice(&[key.mod_lo, buf.modifier as i32]);
|
||||
attribs.extend_from_slice(&[key.mod_hi, (buf.modifier >> 32) as i32]);
|
||||
}
|
||||
}
|
||||
attribs.push(EGL_NONE);
|
||||
let img = unsafe {
|
||||
PROCS.eglCreateImageKHR(
|
||||
self.dpy,
|
||||
EGLContext::none(),
|
||||
EGL_LINUX_DMA_BUF_EXT as _,
|
||||
EGLClientBuffer::none(),
|
||||
attribs.as_ptr(),
|
||||
)
|
||||
};
|
||||
if img.is_none() {
|
||||
return Err(GlesError::CreateImage);
|
||||
}
|
||||
Ok(Rc::new(EglImage {
|
||||
dpy: self.clone(),
|
||||
img,
|
||||
width: buf.width,
|
||||
height: buf.height,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EglImage {
|
||||
dpy: Rc<EglDisplay>,
|
||||
pub(super) img: EGLImageKHR,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
impl Drop for EglImage {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if PROCS.eglDestroyImageKHR(self.dpy.dpy, self.img) == EGL_FALSE {
|
||||
log::warn!("`eglDestroyImageKHR` failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EglContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if eglDestroyContext(self.dpy.dpy, self.ctx) != EGL_TRUE {
|
||||
log::warn!("`eglDestroyContext` failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[thread_local]
|
||||
static mut CURRENT: EGLContext = EGLContext::none();
|
||||
|
||||
impl EglContext {
|
||||
#[inline]
|
||||
pub fn with_current<T, F: FnOnce() -> Result<T, GlesError>>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> Result<T, GlesError> {
|
||||
unsafe {
|
||||
if CURRENT == self.ctx {
|
||||
return f();
|
||||
}
|
||||
self.with_current_slow(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
unsafe fn with_current_slow<T, F: FnOnce() -> Result<T, GlesError>>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> Result<T, GlesError> {
|
||||
if eglMakeCurrent(
|
||||
self.dpy.dpy,
|
||||
EGLSurface::none(),
|
||||
EGLSurface::none(),
|
||||
self.ctx,
|
||||
) == EGL_FALSE
|
||||
{
|
||||
return Err(GlesError::MakeCurrent);
|
||||
}
|
||||
let prev = CURRENT;
|
||||
CURRENT = self.ctx;
|
||||
let res = f();
|
||||
if eglMakeCurrent(self.dpy.dpy, EGLSurface::none(), EGLSurface::none(), prev) == EGL_FALSE {
|
||||
panic!("Could not restore EGLContext");
|
||||
}
|
||||
CURRENT = prev;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EglDisplay {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if eglTerminate(self.dpy) != EGL_TRUE {
|
||||
log::warn!("`eglTerminate` failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EglDevice {
|
||||
pub fn query_string(&self, name: EGLint) -> Result<&'static CStr, GlesError> {
|
||||
unsafe {
|
||||
let res = PROCS.eglQueryDeviceStringEXT(self.dev, name);
|
||||
if res.is_null() {
|
||||
return Err(GlesError::DeviceQueryString);
|
||||
}
|
||||
Ok(CStr::from_ptr(res))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_display(&self) -> Result<Rc<EglDisplay>, GlesError> {
|
||||
unsafe {
|
||||
let dpy = PROCS.eglGetPlatformDisplayEXT(
|
||||
EGL_PLATFORM_DEVICE_EXT as _,
|
||||
self.dev.0,
|
||||
ptr::null(),
|
||||
);
|
||||
if dpy.is_none() {
|
||||
return Err(GlesError::GetDisplay);
|
||||
}
|
||||
let mut dpy = EglDisplay {
|
||||
exts: DisplayExt::empty(),
|
||||
formats: AHashMap::new(),
|
||||
dev: *self,
|
||||
dpy,
|
||||
};
|
||||
let mut major = 0;
|
||||
let mut minor = 0;
|
||||
if eglInitialize(dpy.dpy, &mut major, &mut minor) != EGL_TRUE {
|
||||
return Err(GlesError::Initialize);
|
||||
}
|
||||
dpy.exts = get_display_ext(dpy.dpy);
|
||||
if !dpy.exts.intersects(DisplayExt::KHR_IMAGE_BASE) {
|
||||
return Err(GlesError::ImageBase);
|
||||
}
|
||||
if !dpy
|
||||
.exts
|
||||
.intersects(DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS)
|
||||
{
|
||||
return Err(GlesError::DmaBufImport);
|
||||
}
|
||||
if !dpy
|
||||
.exts
|
||||
.intersects(DisplayExt::KHR_NO_CONFIG_CONTEXT | DisplayExt::MESA_CONFIGLESS_CONTEXT)
|
||||
{
|
||||
return Err(GlesError::ConfiglessContext);
|
||||
}
|
||||
if !dpy.exts.intersects(DisplayExt::KHR_SURFACELESS_CONTEXT) {
|
||||
return Err(GlesError::SurfacelessContext);
|
||||
}
|
||||
dpy.formats = query_formats(dpy.dpy)?;
|
||||
|
||||
Ok(Rc::new(dpy))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, &'static Format>, GlesError> {
|
||||
let mut vec = vec![];
|
||||
let mut num = 0;
|
||||
let res = PROCS.eglQueryDmaBufFormatsEXT(dpy, num, ptr::null_mut(), &mut num);
|
||||
if res != EGL_TRUE {
|
||||
return Err(GlesError::QueryDmaBufFormats);
|
||||
}
|
||||
vec.reserve_exact(num as usize);
|
||||
let res = PROCS.eglQueryDmaBufFormatsEXT(dpy, num, vec.as_mut_ptr(), &mut num);
|
||||
if res != EGL_TRUE {
|
||||
return Err(GlesError::QueryDmaBufFormats);
|
||||
}
|
||||
vec.set_len(num as usize);
|
||||
let mut res = AHashMap::new();
|
||||
let formats = formats();
|
||||
for fmt in vec {
|
||||
if let Some(format) = formats.get(&(fmt as u32)) {
|
||||
res.insert(format.drm, *format);
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
unsafe extern "C" fn egl_log(
|
||||
error: EGLenum,
|
||||
command: *const c::c_char,
|
||||
message_type: EGLint,
|
||||
_thread_label: EGLLabelKHR,
|
||||
_object_label: EGLLabelKHR,
|
||||
message: *const c::c_char,
|
||||
) {
|
||||
let level = match message_type {
|
||||
EGL_DEBUG_MSG_CRITICAL_KHR => Level::Error,
|
||||
EGL_DEBUG_MSG_ERROR_KHR => Level::Error,
|
||||
EGL_DEBUG_MSG_WARN_KHR => Level::Warn,
|
||||
EGL_DEBUG_MSG_INFO_KHR => Level::Info,
|
||||
_ => Level::Warn,
|
||||
};
|
||||
let command = if !command.is_null() {
|
||||
CStr::from_ptr(command).to_bytes()
|
||||
} else {
|
||||
b"none"
|
||||
};
|
||||
let message = if !message.is_null() {
|
||||
CStr::from_ptr(message).to_bytes()
|
||||
} else {
|
||||
b"none"
|
||||
};
|
||||
let err_name = error_name(error);
|
||||
log::log!(
|
||||
level,
|
||||
"EGL: command: {}, error: {} (0x{:x}), message: {}",
|
||||
command.as_bstr(),
|
||||
err_name,
|
||||
error,
|
||||
message.as_bstr()
|
||||
);
|
||||
}
|
||||
|
||||
fn error_name(error: EGLenum) -> &'static str {
|
||||
macro_rules! en {
|
||||
($($name:ident,)*) => {
|
||||
match error as _ {
|
||||
$($name => stringify!($name),)*
|
||||
_ => "unknown",
|
||||
}
|
||||
}
|
||||
}
|
||||
en! {
|
||||
EGL_SUCCESS,
|
||||
EGL_NOT_INITIALIZED,
|
||||
EGL_BAD_ACCESS,
|
||||
EGL_BAD_ALLOC,
|
||||
EGL_BAD_ATTRIBUTE,
|
||||
EGL_BAD_CONTEXT,
|
||||
EGL_BAD_CONFIG,
|
||||
EGL_BAD_CURRENT_SURFACE,
|
||||
EGL_BAD_DISPLAY,
|
||||
EGL_BAD_DEVICE_EXT,
|
||||
EGL_BAD_SURFACE,
|
||||
EGL_BAD_MATCH,
|
||||
EGL_BAD_PARAMETER,
|
||||
EGL_BAD_NATIVE_PIXMAP,
|
||||
EGL_BAD_NATIVE_WINDOW,
|
||||
EGL_CONTEXT_LOST,
|
||||
}
|
||||
}
|
||||
172
src/gles2/ext.rs
Normal file
172
src/gles2/ext.rs
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
use crate::gles2::egl::PROCS;
|
||||
use crate::gles2::sys::{
|
||||
eglQueryString, glGetString, EGLDeviceEXT, EGLDisplay, EGL_EXTENSIONS, GL_EXTENSIONS,
|
||||
};
|
||||
use ahash::AHashSet;
|
||||
use bstr::ByteSlice;
|
||||
use std::ffi::CStr;
|
||||
use std::ops::BitOrAssign;
|
||||
use std::str;
|
||||
use uapi::c;
|
||||
|
||||
unsafe fn get_extensions(ext: *const c::c_char) -> Option<AHashSet<String>> {
|
||||
if ext.is_null() {
|
||||
return None;
|
||||
}
|
||||
let mut res = AHashSet::new();
|
||||
let ext = CStr::from_ptr(ext).to_bytes();
|
||||
for part in ext.split_str(" ") {
|
||||
let name = part.trim();
|
||||
if name.len() > 0 {
|
||||
if let Ok(s) = str::from_utf8(name) {
|
||||
res.insert(s.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
unsafe fn get_dpy_extensions(dpy: EGLDisplay) -> Option<AHashSet<String>> {
|
||||
let ext = eglQueryString(dpy, EGL_EXTENSIONS);
|
||||
get_extensions(ext)
|
||||
}
|
||||
|
||||
fn get_typed_ext<T>(exts: &AHashSet<String>, mut base: T, map: &[(&str, T)]) -> T
|
||||
where
|
||||
T: BitOrAssign + Copy,
|
||||
{
|
||||
for (name, ext) in map.iter().copied() {
|
||||
if exts.contains(name) {
|
||||
base |= ext;
|
||||
}
|
||||
}
|
||||
base
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct ClientExt: u32 {
|
||||
const EXT_CLIENT_EXTENSION = 1 << 0;
|
||||
const EXT_PLATFORM_BASE = 1 << 1;
|
||||
const KHR_PLATFORM_GBM = 1 << 2;
|
||||
const EXT_PLATFORM_DEVICE = 1 << 3;
|
||||
const EXT_DEVICE_BASE = 1 << 4;
|
||||
const EXT_DEVICE_ENUMERATION = 1 << 5;
|
||||
const EXT_DEVICE_QUERY = 1 << 6;
|
||||
const KHR_DEBUG = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientExt {
|
||||
pub fn device_enumeration(self) -> bool {
|
||||
self.intersects(Self::EXT_DEVICE_BASE | Self::EXT_DEVICE_ENUMERATION)
|
||||
}
|
||||
|
||||
pub fn device_query(self) -> bool {
|
||||
self.intersects(Self::EXT_DEVICE_BASE | Self::EXT_DEVICE_QUERY)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_client_ext() -> ClientExt {
|
||||
let map = [
|
||||
("EGL_EXT_platform_base", ClientExt::EXT_PLATFORM_BASE),
|
||||
("EGL_KHR_platform_gbm", ClientExt::KHR_PLATFORM_GBM),
|
||||
("EGL_EXT_platform_device", ClientExt::EXT_PLATFORM_DEVICE),
|
||||
("EGL_EXT_device_base", ClientExt::EXT_DEVICE_BASE),
|
||||
(
|
||||
"EGL_EXT_device_enumeration",
|
||||
ClientExt::EXT_DEVICE_ENUMERATION,
|
||||
),
|
||||
("EGL_EXT_device_query", ClientExt::EXT_DEVICE_QUERY),
|
||||
("EGL_KHR_debug", ClientExt::KHR_DEBUG),
|
||||
];
|
||||
match unsafe { get_dpy_extensions(EGLDisplay::none()) } {
|
||||
Some(exts) => get_typed_ext(&exts, ClientExt::EXT_CLIENT_EXTENSION, &map),
|
||||
_ => ClientExt::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct DisplayExt: u32 {
|
||||
const KHR_IMAGE_BASE = 1 << 0;
|
||||
const EXT_IMAGE_DMA_BUF_IMPORT = 1 << 1;
|
||||
const EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS = 1 << 2;
|
||||
const KHR_NO_CONFIG_CONTEXT = 1 << 3;
|
||||
const MESA_CONFIGLESS_CONTEXT = 1 << 4;
|
||||
const KHR_SURFACELESS_CONTEXT = 1 << 5;
|
||||
const IMG_CONTEXT_PRIORITY = 1 << 6;
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn get_display_ext(dpy: EGLDisplay) -> DisplayExt {
|
||||
let map = [
|
||||
("EGL_KHR_image_base", DisplayExt::KHR_IMAGE_BASE),
|
||||
(
|
||||
"EGL_EXT_image_dma_buf_import",
|
||||
DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT,
|
||||
),
|
||||
(
|
||||
"EGL_EXT_image_dma_buf_import_modifiers",
|
||||
DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS,
|
||||
),
|
||||
(
|
||||
"EGL_KHR_no_config_context",
|
||||
DisplayExt::KHR_NO_CONFIG_CONTEXT,
|
||||
),
|
||||
(
|
||||
"EGL_MESA_configless_context",
|
||||
DisplayExt::MESA_CONFIGLESS_CONTEXT,
|
||||
),
|
||||
(
|
||||
"EGL_KHR_surfaceless_context",
|
||||
DisplayExt::KHR_SURFACELESS_CONTEXT,
|
||||
),
|
||||
("EGL_IMG_context_priority", DisplayExt::IMG_CONTEXT_PRIORITY),
|
||||
];
|
||||
match get_dpy_extensions(dpy) {
|
||||
Some(exts) => get_typed_ext(&exts, DisplayExt::empty(), &map),
|
||||
_ => DisplayExt::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct DeviceExt: u32 {
|
||||
const MESA_DEVICE_SOFTWARE = 1 << 0;
|
||||
const EXT_DEVICE_PERSISTENT_ID = 1 << 1;
|
||||
const EXT_DEVICE_DRM = 1 << 2;
|
||||
const EXT_DEVICE_DRM_RENDER_NODE = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) unsafe fn get_device_ext(dev: EGLDeviceEXT) -> DeviceExt {
|
||||
let map = [
|
||||
("EGL_MESA_device_software", DeviceExt::MESA_DEVICE_SOFTWARE),
|
||||
(
|
||||
"EGL_EXT_device_persistent_id",
|
||||
DeviceExt::EXT_DEVICE_PERSISTENT_ID,
|
||||
),
|
||||
("EGL_EXT_device_drm", DeviceExt::EXT_DEVICE_DRM),
|
||||
(
|
||||
"EGL_EXT_device_drm_render_node",
|
||||
DeviceExt::EXT_DEVICE_DRM_RENDER_NODE,
|
||||
),
|
||||
];
|
||||
let ext = PROCS.eglQueryDeviceStringEXT(dev, EGL_EXTENSIONS);
|
||||
match get_extensions(ext) {
|
||||
Some(exts) => get_typed_ext(&exts, DeviceExt::empty(), &map),
|
||||
_ => DeviceExt::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
pub struct GlExt: u32 {
|
||||
const GL_OES_EGL_IMAGE = 1 << 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_gl_ext() -> GlExt {
|
||||
let map = [("GL_OES_EGL_image", GlExt::GL_OES_EGL_IMAGE)];
|
||||
match unsafe { get_extensions(glGetString(GL_EXTENSIONS) as _) } {
|
||||
Some(exts) => get_typed_ext(&exts, GlExt::empty(), &map),
|
||||
_ => GlExt::empty(),
|
||||
}
|
||||
}
|
||||
3
src/gles2/ext_proc.rs
Normal file
3
src/gles2/ext_proc.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/egl_procs.rs"));
|
||||
363
src/gles2/gl.rs
Normal file
363
src/gles2/gl.rs
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
use crate::egl::{EglContext, EglImage, PROCS};
|
||||
use crate::format::Format;
|
||||
use crate::gles2::sys::{
|
||||
glAttachShader, glBindFramebuffer, glBindRenderbuffer, glBindTexture, glCheckFramebufferStatus,
|
||||
glClear, glClearColor, glCompileShader, glCreateProgram, glCreateShader, glDeleteFramebuffers,
|
||||
glDeleteProgram, glDeleteRenderbuffers, glDeleteShader, glDeleteTextures, glDetachShader,
|
||||
glDisable, glEnable, glFramebufferRenderbuffer, glFramebufferTexture2D, glGenFramebuffers,
|
||||
glGenRenderbuffers, glGenTextures, glGetAttribLocation, glGetProgramiv, glGetShaderiv,
|
||||
glGetUniformLocation, glLinkProgram, glPixelStorei, glScissor, glShaderSource, glTexImage2D,
|
||||
glTexParameteri, glUseProgram, EGLContext, GLeglImageOES, GLenum, GLint, GLuint,
|
||||
GL_CLAMP_TO_EDGE, GL_COLOR_ATTACHMENT0, GL_COLOR_BUFFER_BIT, GL_COMPILE_STATUS, GL_FALSE,
|
||||
GL_FRAMEBUFFER, GL_FRAMEBUFFER_COMPLETE, GL_LINEAR, GL_LINK_STATUS, GL_RENDERBUFFER,
|
||||
GL_SCISSOR_TEST, GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER,
|
||||
GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_UNPACK_ROW_LENGTH_EXT,
|
||||
};
|
||||
use crate::gles2::GlesError;
|
||||
use crate::rect::Rect;
|
||||
use crate::utils::ptr_ext::PtrExt;
|
||||
use std::cell::Cell;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use uapi::{ustr, Ustr};
|
||||
|
||||
pub struct GlTexture {
|
||||
pub(super) ctx: Rc<EglContext>,
|
||||
pub tex: GLuint,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
}
|
||||
|
||||
impl GlTexture {
|
||||
pub fn new(
|
||||
ctx: &Rc<EglContext>,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<Rc<GlTexture>, GlesError> {
|
||||
let tex = ctx.with_current(|| unsafe {
|
||||
let mut tex = 0;
|
||||
glGenTextures(1, &mut tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
format.gl_format,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
format.gl_format as _,
|
||||
format.gl_type as _,
|
||||
ptr::null(),
|
||||
);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
Ok(tex)
|
||||
})?;
|
||||
Ok(Rc::new(GlTexture {
|
||||
ctx: ctx.clone(),
|
||||
tex,
|
||||
width,
|
||||
height,
|
||||
}))
|
||||
}
|
||||
|
||||
pub unsafe fn to_framebuffer(self: &Rc<Self>) -> Result<Rc<GlFrameBuffer>, GlesError> {
|
||||
self.ctx.with_current(|| unsafe {
|
||||
let mut fbo = 0;
|
||||
glGenFramebuffers(1, &mut fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_2D,
|
||||
self.tex,
|
||||
0,
|
||||
);
|
||||
let fb = GlFrameBuffer {
|
||||
_rb: None,
|
||||
_tex: Some(self.clone()),
|
||||
ctx: self.ctx.clone(),
|
||||
fbo,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
};
|
||||
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
if status != GL_FRAMEBUFFER_COMPLETE {
|
||||
return Err(GlesError::CreateFramebuffer);
|
||||
}
|
||||
Ok(Rc::new(fb))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn import_texture(
|
||||
ctx: &Rc<EglContext>,
|
||||
data: &[Cell<u8>],
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
) -> Result<Rc<GlTexture>, GlesError> {
|
||||
if (stride * height) as usize > data.len() {
|
||||
return Err(GlesError::SmallImageBuffer);
|
||||
}
|
||||
let tex = ctx.with_current(|| unsafe {
|
||||
let mut tex = 0;
|
||||
glGenTextures(1, &mut tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / format.bpp as GLint);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
format.gl_format,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
format.gl_format as _,
|
||||
format.gl_type as _,
|
||||
data.as_ptr() as _,
|
||||
);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
Ok(tex)
|
||||
})?;
|
||||
Ok(Rc::new(GlTexture {
|
||||
ctx: ctx.clone(),
|
||||
tex,
|
||||
width,
|
||||
height,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GlTexture {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.ctx.with_current(|| {
|
||||
glDeleteTextures(1, &self.tex);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_scissor<T, F: FnOnce() -> T>(scissor: &Rect, f: F) -> T {
|
||||
return f();
|
||||
|
||||
#[thread_local]
|
||||
static mut SCISSOR: *const Rect = ptr::null();
|
||||
|
||||
unsafe {
|
||||
let prev = SCISSOR;
|
||||
if prev.is_null() {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
glScissor(
|
||||
scissor.x1(),
|
||||
scissor.x2(),
|
||||
scissor.width(),
|
||||
scissor.height(),
|
||||
);
|
||||
SCISSOR = scissor;
|
||||
let res = f();
|
||||
if prev.is_null() {
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
} else {
|
||||
let prev = prev.deref();
|
||||
glScissor(prev.x1(), prev.x2(), prev.width(), prev.height());
|
||||
}
|
||||
SCISSOR = prev;
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlShader {
|
||||
_ctx: Rc<EglContext>,
|
||||
shader: GLuint,
|
||||
}
|
||||
|
||||
impl GlShader {
|
||||
pub unsafe fn compile(ctx: &Rc<EglContext>, ty: GLenum, src: &str) -> Result<Self, GlesError> {
|
||||
let shader = glCreateShader(ty);
|
||||
let res = GlShader {
|
||||
_ctx: ctx.clone(),
|
||||
shader,
|
||||
};
|
||||
let len = src.len() as _;
|
||||
glShaderSource(shader, 1, &(src.as_ptr() as _), &len);
|
||||
glCompileShader(shader);
|
||||
|
||||
let mut ok = 0;
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &mut ok);
|
||||
if ok == GL_FALSE as _ {
|
||||
return Err(GlesError::ShaderCompileFailed);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GlShader {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self._ctx.with_current(|| {
|
||||
glDeleteShader(self.shader);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlProgram {
|
||||
_ctx: Rc<EglContext>,
|
||||
prog: GLuint,
|
||||
}
|
||||
|
||||
impl GlProgram {
|
||||
pub unsafe fn link(vert: &GlShader, frag: &GlShader) -> Result<Self, GlesError> {
|
||||
let res = GlProgram {
|
||||
_ctx: vert._ctx.clone(),
|
||||
prog: glCreateProgram(),
|
||||
};
|
||||
glAttachShader(res.prog, vert.shader);
|
||||
glAttachShader(res.prog, frag.shader);
|
||||
glLinkProgram(res.prog);
|
||||
glDetachShader(res.prog, vert.shader);
|
||||
glDetachShader(res.prog, frag.shader);
|
||||
|
||||
let mut ok = 0;
|
||||
glGetProgramiv(res.prog, GL_LINK_STATUS, &mut ok);
|
||||
if ok == GL_FALSE as _ {
|
||||
return Err(GlesError::ProgramLink);
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub unsafe fn get_uniform_location(&self, name: &Ustr) -> GLint {
|
||||
glGetUniformLocation(self.prog, name.as_ptr() as _)
|
||||
}
|
||||
|
||||
pub unsafe fn get_attrib_location(&self, name: &Ustr) -> GLint {
|
||||
glGetAttribLocation(self.prog, name.as_ptr() as _)
|
||||
}
|
||||
|
||||
pub unsafe fn use_(&self) {
|
||||
glUseProgram(self.prog);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GlProgram {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self._ctx.with_current(|| {
|
||||
glDeleteProgram(self.prog);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlRenderBuffer {
|
||||
pub img: Rc<EglImage>,
|
||||
pub ctx: Rc<EglContext>,
|
||||
rbo: GLuint,
|
||||
}
|
||||
|
||||
impl GlRenderBuffer {
|
||||
pub fn from_image(
|
||||
img: &Rc<EglImage>,
|
||||
ctx: &Rc<EglContext>,
|
||||
) -> Result<Rc<GlRenderBuffer>, GlesError> {
|
||||
ctx.with_current(|| unsafe {
|
||||
let mut rbo = 0;
|
||||
glGenRenderbuffers(1, &mut rbo);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
||||
PROCS.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, GLeglImageOES(img.img.0));
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
Ok(Rc::new(GlRenderBuffer {
|
||||
img: img.clone(),
|
||||
ctx: ctx.clone(),
|
||||
rbo,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_framebuffer(self: &Rc<Self>) -> Result<Rc<GlFrameBuffer>, GlesError> {
|
||||
self.ctx.with_current(|| unsafe {
|
||||
let mut fbo = 0;
|
||||
glGenFramebuffers(1, &mut fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferRenderbuffer(
|
||||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_RENDERBUFFER,
|
||||
self.rbo,
|
||||
);
|
||||
let status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
let fb = GlFrameBuffer {
|
||||
_rb: Some(self.clone()),
|
||||
_tex: None,
|
||||
ctx: self.ctx.clone(),
|
||||
fbo,
|
||||
width: self.img.width,
|
||||
height: self.img.height,
|
||||
};
|
||||
if status != GL_FRAMEBUFFER_COMPLETE {
|
||||
return Err(GlesError::CreateFramebuffer);
|
||||
}
|
||||
Ok(Rc::new(fb))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GlRenderBuffer {
|
||||
fn drop(&mut self) {
|
||||
self.ctx.with_current(|| {
|
||||
unsafe {
|
||||
glDeleteRenderbuffers(1, &self.rbo);
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlFrameBuffer {
|
||||
pub _rb: Option<Rc<GlRenderBuffer>>,
|
||||
pub _tex: Option<Rc<GlTexture>>,
|
||||
pub ctx: Rc<EglContext>,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
fbo: GLuint,
|
||||
}
|
||||
|
||||
impl GlFrameBuffer {
|
||||
pub unsafe fn bind(&self) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo);
|
||||
}
|
||||
|
||||
pub fn clear(&self, r: f32, g: f32, b: f32, a: f32) -> Result<(), GlesError> {
|
||||
self.ctx.with_current(|| unsafe {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, self.fbo);
|
||||
glClearColor(r, g, b, a);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
// glFlush();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for GlFrameBuffer {
|
||||
fn drop(&mut self) {
|
||||
self.ctx.with_current(|| {
|
||||
unsafe {
|
||||
glDeleteFramebuffers(1, &self.fbo);
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
58
src/gles2/mod.rs
Normal file
58
src/gles2/mod.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
pub mod egl;
|
||||
pub mod ext;
|
||||
mod ext_proc;
|
||||
pub mod gl;
|
||||
pub mod sys;
|
||||
|
||||
use crate::drm::drm::DrmError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GlesError {
|
||||
#[error("EGL library does not support `EGL_EXT_platform_base`")]
|
||||
ExtPlatformBase,
|
||||
#[error("Could not compile a shader")]
|
||||
ShaderCompileFailed,
|
||||
#[error("Could not link a program")]
|
||||
ProgramLink,
|
||||
#[error("Could not bind to `EGL_OPENGL_ES_API`")]
|
||||
BindFailed,
|
||||
#[error("EGL library does not support device enumeration")]
|
||||
DeviceEnumeration,
|
||||
#[error("EGL library does not support device querying")]
|
||||
DeviceQuery,
|
||||
#[error("`eglQueryDeviceStringEXT` failed")]
|
||||
DeviceQueryString,
|
||||
#[error("`eglCreateContext` failed")]
|
||||
CreateContext,
|
||||
#[error("`eglMakeCurrent` failed")]
|
||||
MakeCurrent,
|
||||
#[error("`eglCreateImageKHR` failed")]
|
||||
CreateImage,
|
||||
#[error("Image buffer is too small")]
|
||||
SmallImageBuffer,
|
||||
#[error("Binding a renderbuffer to a framebuffer failed")]
|
||||
CreateFramebuffer,
|
||||
#[error("`eglQueryDevicesEXT` failed")]
|
||||
QueryDevices,
|
||||
#[error("`eglGetPlatformDisplayEXT` failed")]
|
||||
GetDisplay,
|
||||
#[error("`eglInitialize` failed")]
|
||||
Initialize,
|
||||
#[error("EGL display does not support `EGL_EXT_image_dma_buf_import_modifiers`")]
|
||||
DmaBufImport,
|
||||
#[error("GLES driver does not support `GL_OES_EGL_image`")]
|
||||
OesEglImage,
|
||||
#[error("EGL display does not support `EGL_KHR_image_base`")]
|
||||
ImageBase,
|
||||
#[error(
|
||||
"EGL display does not support `EGL_KHR_no_config_context` or `EGL_MESA_configless_context`"
|
||||
)]
|
||||
ConfiglessContext,
|
||||
#[error("EGL display does not support `EGL_KHR_surfaceless_context`")]
|
||||
SurfacelessContext,
|
||||
#[error("`eglQueryDmaBufFormatsEXT` failed")]
|
||||
QueryDmaBufFormats,
|
||||
#[error(transparent)]
|
||||
DrmError(#[from] DrmError),
|
||||
}
|
||||
256
src/gles2/sys.rs
Normal file
256
src/gles2/sys.rs
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
pub use uapi::c;
|
||||
|
||||
macro_rules! egl_transparent {
|
||||
($name:ident) => {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct $name(pub *mut u8);
|
||||
|
||||
impl $name {
|
||||
#[allow(dead_code)]
|
||||
pub const fn none() -> Self {
|
||||
Self(std::ptr::null_mut())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_none(self) -> bool {
|
||||
self.0.is_null()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub type EGLint = i32;
|
||||
pub type EGLenum = c::c_uint;
|
||||
pub type EGLBoolean = c::c_uint;
|
||||
pub type EGLuint64KHR = u64;
|
||||
pub type EGLAttrib = isize;
|
||||
|
||||
egl_transparent!(EGLDisplay);
|
||||
egl_transparent!(EGLSurface);
|
||||
egl_transparent!(EGLConfig);
|
||||
egl_transparent!(EGLImageKHR);
|
||||
egl_transparent!(EGLContext);
|
||||
egl_transparent!(EGLClientBuffer);
|
||||
egl_transparent!(EGLLabelKHR);
|
||||
egl_transparent!(EGLDeviceEXT);
|
||||
|
||||
pub type EGLDEBUGPROCKHR = unsafe extern "C" fn(
|
||||
error: EGLenum,
|
||||
command: *const c::c_char,
|
||||
message_type: EGLint,
|
||||
thread_label: EGLLabelKHR,
|
||||
object_label: EGLLabelKHR,
|
||||
message: *const c::c_char,
|
||||
);
|
||||
|
||||
pub const EGL_EXTENSIONS: EGLint = 0x3055;
|
||||
pub const EGL_DEBUG_MSG_CRITICAL_KHR: EGLint = 0x33B9;
|
||||
pub const EGL_DEBUG_MSG_ERROR_KHR: EGLint = 0x33BA;
|
||||
pub const EGL_DEBUG_MSG_WARN_KHR: EGLint = 0x33BB;
|
||||
pub const EGL_DEBUG_MSG_INFO_KHR: EGLint = 0x33BC;
|
||||
pub const EGL_TRUE: EGLBoolean = 1;
|
||||
pub const EGL_FALSE: EGLBoolean = 0;
|
||||
pub const EGL_NONE: EGLint = 0x3038;
|
||||
pub const EGL_SUCCESS: EGLint = 0x3000;
|
||||
pub const EGL_NOT_INITIALIZED: EGLint = 0x3001;
|
||||
pub const EGL_BAD_ACCESS: EGLint = 0x3002;
|
||||
pub const EGL_BAD_ALLOC: EGLint = 0x3003;
|
||||
pub const EGL_BAD_ATTRIBUTE: EGLint = 0x3004;
|
||||
pub const EGL_BAD_CONFIG: EGLint = 0x3005;
|
||||
pub const EGL_BAD_CONTEXT: EGLint = 0x3006;
|
||||
pub const EGL_BAD_CURRENT_SURFACE: EGLint = 0x3007;
|
||||
pub const EGL_BAD_DISPLAY: EGLint = 0x3008;
|
||||
pub const EGL_BAD_MATCH: EGLint = 0x3009;
|
||||
pub const EGL_BAD_NATIVE_PIXMAP: EGLint = 0x300A;
|
||||
pub const EGL_BAD_NATIVE_WINDOW: EGLint = 0x300B;
|
||||
pub const EGL_BAD_PARAMETER: EGLint = 0x300C;
|
||||
pub const EGL_BAD_SURFACE: EGLint = 0x300D;
|
||||
pub const EGL_CONTEXT_LOST: EGLint = 0x300E;
|
||||
pub const EGL_BAD_DEVICE_EXT: EGLint = 0x322B;
|
||||
pub const EGL_OPENGL_ES_API: EGLenum = 0x30A0;
|
||||
pub const EGL_DRM_DEVICE_FILE_EXT: EGLint = 0x3233;
|
||||
pub const EGL_PLATFORM_DEVICE_EXT: EGLint = 0x313F;
|
||||
pub const EGL_CONTEXT_CLIENT_VERSION: EGLint = 0x3098;
|
||||
|
||||
pub const EGL_WIDTH: EGLint = 0x3057;
|
||||
pub const EGL_HEIGHT: EGLint = 0x3056;
|
||||
pub const EGL_LINUX_DRM_FOURCC_EXT: EGLint = 0x3271;
|
||||
pub const EGL_DMA_BUF_PLANE0_FD_EXT: EGLint = 0x3272;
|
||||
pub const EGL_DMA_BUF_PLANE0_OFFSET_EXT: EGLint = 0x3273;
|
||||
pub const EGL_DMA_BUF_PLANE0_PITCH_EXT: EGLint = 0x3274;
|
||||
pub const EGL_DMA_BUF_PLANE1_FD_EXT: EGLint = 0x3275;
|
||||
pub const EGL_DMA_BUF_PLANE1_OFFSET_EXT: EGLint = 0x3276;
|
||||
pub const EGL_DMA_BUF_PLANE1_PITCH_EXT: EGLint = 0x3277;
|
||||
pub const EGL_DMA_BUF_PLANE2_FD_EXT: EGLint = 0x3278;
|
||||
pub const EGL_DMA_BUF_PLANE2_OFFSET_EXT: EGLint = 0x3279;
|
||||
pub const EGL_DMA_BUF_PLANE2_PITCH_EXT: EGLint = 0x327A;
|
||||
pub const EGL_DMA_BUF_PLANE3_FD_EXT: EGLint = 0x3440;
|
||||
pub const EGL_DMA_BUF_PLANE3_OFFSET_EXT: EGLint = 0x3441;
|
||||
pub const EGL_DMA_BUF_PLANE3_PITCH_EXT: EGLint = 0x3442;
|
||||
pub const EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT: EGLint = 0x3443;
|
||||
pub const EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT: EGLint = 0x3444;
|
||||
pub const EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT: EGLint = 0x3445;
|
||||
pub const EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT: EGLint = 0x3446;
|
||||
pub const EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT: EGLint = 0x3447;
|
||||
pub const EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT: EGLint = 0x3448;
|
||||
pub const EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT: EGLint = 0x3449;
|
||||
pub const EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT: EGLint = 0x344A;
|
||||
pub const EGL_IMAGE_PRESERVED_KHR: EGLint = 0x30D2;
|
||||
pub const EGL_LINUX_DMA_BUF_EXT: EGLint = 0x3270;
|
||||
|
||||
pub type GLenum = c::c_uint;
|
||||
pub type GLbitfield = c::c_uint;
|
||||
pub type GLfloat = f32;
|
||||
pub type GLubyte = u8;
|
||||
pub type GLsizei = c::c_int;
|
||||
pub type GLuint = c::c_uint;
|
||||
pub type GLint = c::c_int;
|
||||
pub type GLchar = c::c_char;
|
||||
pub type GLboolean = c::c_uchar;
|
||||
|
||||
egl_transparent!(GLeglImageOES);
|
||||
|
||||
pub const GL_EXTENSIONS: GLenum = 0x1F03;
|
||||
pub const GL_RENDERBUFFER: GLenum = 0x8D41;
|
||||
pub const GL_FRAMEBUFFER: GLenum = 0x8D40;
|
||||
pub const GL_COLOR_ATTACHMENT0: GLenum = 0x8CE0;
|
||||
pub const GL_FRAMEBUFFER_COMPLETE: GLenum = 0x8CD5;
|
||||
|
||||
pub const GL_COLOR_BUFFER_BIT: GLbitfield = 0x00004000;
|
||||
pub const GL_TEXTURE_2D: GLenum = 0x0DE1;
|
||||
pub const GL_TEXTURE_WRAP_S: GLenum = 0x2802;
|
||||
pub const GL_TEXTURE_WRAP_T: GLenum = 0x2803;
|
||||
pub const GL_CLAMP_TO_EDGE: GLint = 0x812F;
|
||||
pub const GL_UNPACK_ROW_LENGTH_EXT: GLenum = 0x0CF2;
|
||||
|
||||
pub const GL_BGRA_EXT: GLint = 0x80E1;
|
||||
pub const GL_UNSIGNED_BYTE: GLint = 0x1401;
|
||||
pub const GL_SCISSOR_TEST: GLenum = 0x0C11;
|
||||
|
||||
pub const GL_COMPILE_STATUS: GLenum = 0x8B81;
|
||||
pub const GL_LINK_STATUS: GLenum = 0x8B82;
|
||||
|
||||
pub const GL_FALSE: GLboolean = 0;
|
||||
pub const GL_FRAGMENT_SHADER: GLenum = 0x8B30;
|
||||
pub const GL_VERTEX_SHADER: GLenum = 0x8B31;
|
||||
|
||||
pub const GL_TEXTURE0: GLenum = 0x84C0;
|
||||
pub const GL_TEXTURE_MIN_FILTER: GLenum = 0x2801;
|
||||
pub const GL_TEXTURE_MAG_FILTER: GLenum = 0x2800;
|
||||
pub const GL_LINEAR: GLint = 0x2601;
|
||||
pub const GL_FLOAT: GLenum = 0x1406;
|
||||
|
||||
pub const GL_TRIANGLE_STRIP: GLenum = 0x0005;
|
||||
|
||||
#[link(name = "GLESv2")]
|
||||
extern "C" {
|
||||
pub fn glGetString(name: GLenum) -> *const u8;
|
||||
pub fn glGenRenderbuffers(n: GLsizei, renderbuffers: *mut GLuint);
|
||||
pub fn glDeleteRenderbuffers(n: GLsizei, renderbuffers: *const GLuint);
|
||||
pub fn glBindRenderbuffer(target: GLenum, renderbuffer: GLuint);
|
||||
pub fn glGenFramebuffers(n: GLsizei, framebuffers: *mut GLuint);
|
||||
pub fn glDeleteFramebuffers(n: GLsizei, framebuffers: *const GLuint);
|
||||
pub fn glBindFramebuffer(target: GLenum, framebuffer: GLuint);
|
||||
pub fn glFramebufferRenderbuffer(
|
||||
target: GLenum,
|
||||
attachment: GLenum,
|
||||
renderbuffertarget: GLenum,
|
||||
renderbuffer: GLuint,
|
||||
);
|
||||
pub fn glFramebufferTexture2D(
|
||||
target: GLenum,
|
||||
attachment: GLenum,
|
||||
textarget: GLenum,
|
||||
texture: GLenum,
|
||||
level: GLint,
|
||||
);
|
||||
pub fn glCheckFramebufferStatus(target: GLenum) -> GLenum;
|
||||
pub fn glClear(mask: GLbitfield);
|
||||
pub fn glClearColor(red: GLfloat, green: GLfloat, blue: GLfloat, alpha: GLfloat);
|
||||
pub fn glFlush();
|
||||
|
||||
pub fn glGenTextures(n: GLsizei, textures: *mut GLuint);
|
||||
pub fn glDeleteTextures(n: GLsizei, textures: *const GLuint);
|
||||
pub fn glBindTexture(target: GLenum, texture: GLuint);
|
||||
pub fn glTexParameteri(target: GLenum, pname: GLenum, param: GLint);
|
||||
|
||||
pub fn glPixelStorei(pname: GLenum, param: GLint);
|
||||
|
||||
pub fn glTexImage2D(
|
||||
target: GLenum,
|
||||
level: GLint,
|
||||
internalformat: GLint,
|
||||
width: GLsizei,
|
||||
height: GLsizei,
|
||||
border: GLint,
|
||||
format: GLenum,
|
||||
ty: GLenum,
|
||||
pixels: *const c::c_void,
|
||||
);
|
||||
|
||||
pub fn glScissor(x: GLint, y: GLint, width: GLsizei, height: GLsizei);
|
||||
pub fn glEnable(cap: GLenum);
|
||||
pub fn glDisable(cap: GLenum);
|
||||
pub fn glViewport(x: GLint, y: GLint, width: GLsizei, height: GLsizei);
|
||||
|
||||
pub fn glCreateShader(ty: GLenum) -> GLuint;
|
||||
pub fn glDeleteShader(shader: GLuint);
|
||||
pub fn glShaderSource(
|
||||
shader: GLuint,
|
||||
count: GLsizei,
|
||||
string: *const *const GLchar,
|
||||
length: *const GLint,
|
||||
);
|
||||
pub fn glCompileShader(shader: GLuint);
|
||||
pub fn glGetShaderiv(shader: GLuint, pname: GLenum, params: *mut GLint);
|
||||
|
||||
pub fn glCreateProgram() -> GLuint;
|
||||
pub fn glDeleteProgram(prog: GLuint);
|
||||
pub fn glAttachShader(prog: GLuint, shader: GLuint);
|
||||
pub fn glDetachShader(prog: GLuint, shader: GLuint);
|
||||
pub fn glLinkProgram(prog: GLuint);
|
||||
pub fn glGetProgramiv(program: GLuint, pname: GLenum, params: *mut GLint);
|
||||
pub fn glUseProgram(program: GLuint);
|
||||
|
||||
pub fn glGetUniformLocation(prog: GLuint, name: *const GLchar) -> GLint;
|
||||
pub fn glGetAttribLocation(prog: GLuint, name: *const GLchar) -> GLint;
|
||||
pub fn glUniform1i(location: GLint, v0: GLint);
|
||||
pub fn glUniform1f(location: GLint, v0: GLfloat);
|
||||
pub fn glVertexAttribPointer(
|
||||
index: GLuint,
|
||||
size: GLint,
|
||||
ty: GLenum,
|
||||
normalized: GLboolean,
|
||||
stride: GLsizei,
|
||||
pointer: *const u8,
|
||||
);
|
||||
|
||||
pub fn glActiveTexture(texture: GLuint);
|
||||
|
||||
pub fn glEnableVertexAttribArray(idx: GLuint);
|
||||
pub fn glDisableVertexAttribArray(idx: GLuint);
|
||||
pub fn glDrawArrays(mode: GLenum, first: GLint, count: GLsizei);
|
||||
}
|
||||
|
||||
#[link(name = "EGL")]
|
||||
extern "C" {
|
||||
pub fn eglQueryString(dpy: EGLDisplay, name: EGLint) -> *const c::c_char;
|
||||
pub fn eglGetProcAddress(procname: *const c::c_char) -> *mut u8;
|
||||
pub fn eglBindAPI(api: EGLenum) -> EGLBoolean;
|
||||
pub fn eglTerminate(dpy: EGLDisplay) -> EGLBoolean;
|
||||
pub fn eglInitialize(dpy: EGLDisplay, major: *mut EGLint, minor: *mut EGLint) -> EGLBoolean;
|
||||
pub fn eglCreateContext(
|
||||
dpy: EGLDisplay,
|
||||
config: EGLConfig,
|
||||
share_context: EGLContext,
|
||||
attrib_list: *const EGLint,
|
||||
) -> EGLContext;
|
||||
pub fn eglDestroyContext(dpy: EGLDisplay, ctx: EGLContext) -> EGLBoolean;
|
||||
pub fn eglMakeCurrent(
|
||||
dpy: EGLDisplay,
|
||||
draw: EGLSurface,
|
||||
read: EGLSurface,
|
||||
ctx: EGLContext,
|
||||
) -> EGLBoolean;
|
||||
}
|
||||
|
|
@ -3,11 +3,13 @@ mod types;
|
|||
use crate::client::{Client, DynEventFormatter};
|
||||
use crate::clientmem::{ClientMem, ClientMemOffset};
|
||||
use crate::format::Format;
|
||||
use crate::gles2::gl::GlTexture;
|
||||
use crate::ifs::wl_surface::{WlSurface, WlSurfaceId};
|
||||
use crate::object::{Interface, Object, ObjectId};
|
||||
use crate::pixman;
|
||||
use crate::rect::Rect;
|
||||
use crate::utils::buffd::MsgParser;
|
||||
use crate::utils::clonecell::CloneCell;
|
||||
use crate::utils::copyhashmap::CopyHashMap;
|
||||
use std::rc::Rc;
|
||||
pub use types::*;
|
||||
|
|
@ -23,10 +25,14 @@ pub struct WlBuffer {
|
|||
pub client: Rc<Client>,
|
||||
_offset: usize,
|
||||
pub rect: Rect,
|
||||
_stride: i32,
|
||||
_format: &'static Format,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
pub image: Rc<pixman::Image<ClientMemOffset>>,
|
||||
mem: ClientMemOffset,
|
||||
pub texture: CloneCell<Option<Rc<GlTexture>>>,
|
||||
pub(super) surfaces: CopyHashMap<WlSurfaceId, Rc<WlSurface>>,
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
|
||||
impl WlBuffer {
|
||||
|
|
@ -52,8 +58,8 @@ impl WlBuffer {
|
|||
return Err(WlBufferError::StrideTooSmall);
|
||||
}
|
||||
let image = pixman::Image::new(
|
||||
mem,
|
||||
format.pixman,
|
||||
mem.clone(),
|
||||
format.pixman.unwrap(),
|
||||
width as u32,
|
||||
height as u32,
|
||||
stride as u32,
|
||||
|
|
@ -63,13 +69,27 @@ impl WlBuffer {
|
|||
client: client.clone(),
|
||||
_offset: offset,
|
||||
rect: Rect::new_sized(0, 0, width, height).unwrap(),
|
||||
_stride: stride,
|
||||
_format: format,
|
||||
stride,
|
||||
format,
|
||||
image: Rc::new(image),
|
||||
mem,
|
||||
width,
|
||||
height,
|
||||
texture: CloneCell::new(None),
|
||||
surfaces: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_texture(&self) -> Result<(), WlBufferError> {
|
||||
self.texture.set(None);
|
||||
let ctx = self.client.state.egl.get().unwrap();
|
||||
let tex = self.mem.access(|mem| {
|
||||
GlTexture::import_texture(&ctx, mem, self.format, self.width, self.height, self.stride)
|
||||
})??;
|
||||
self.texture.set(Some(tex));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> {
|
||||
let _req: Destroy = self.client.parse(self, parser)?;
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use crate::client::{ClientError, EventFormatter, RequestParser};
|
||||
use crate::gles2::GlesError;
|
||||
use crate::ifs::wl_buffer::{WlBuffer, RELEASE};
|
||||
use crate::object::Object;
|
||||
use crate::pixman::PixmanError;
|
||||
use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError};
|
||||
use crate::ClientMemError;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::rc::Rc;
|
||||
use thiserror::Error;
|
||||
|
|
@ -17,8 +19,14 @@ pub enum WlBufferError {
|
|||
DestroyError(#[from] DestroyError),
|
||||
#[error("Pixman returned an error")]
|
||||
PixmanError(#[source] Box<PixmanError>),
|
||||
#[error("Could not access the client memory")]
|
||||
ClientMemError(#[source] Box<ClientMemError>),
|
||||
#[error("GLES could not import the client image")]
|
||||
GlesError(#[source] Box<GlesError>),
|
||||
}
|
||||
efrom!(WlBufferError, PixmanError, PixmanError);
|
||||
efrom!(WlBufferError, ClientMemError, ClientMemError);
|
||||
efrom!(WlBufferError, GlesError, GlesError);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DestroyError {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use crate::client::{Client, ClientId, DynEventFormatter};
|
|||
use crate::fixed::Fixed;
|
||||
use crate::globals::{Global, GlobalName};
|
||||
use crate::ifs::wl_seat::wl_keyboard::{WlKeyboard, WlKeyboardId};
|
||||
use crate::ifs::wl_seat::wl_pointer::{WlPointer, WlPointerId};
|
||||
use crate::ifs::wl_seat::wl_pointer::{WlPointer, WlPointerId, POINTER_FRAME_SINCE_VERSION};
|
||||
use crate::ifs::wl_seat::wl_touch::WlTouch;
|
||||
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::{XdgToplevel, XdgToplevelId};
|
||||
use crate::ifs::wl_surface::WlSurface;
|
||||
|
|
@ -139,12 +139,12 @@ impl WlSeatGlobal {
|
|||
KeyState::Released => wl_pointer::RELEASED,
|
||||
KeyState::Pressed => wl_pointer::PRESSED,
|
||||
};
|
||||
self.surface_pointer_event(surface, |p| p.button(0, 0, button, state));
|
||||
self.surface_pointer_event(0, surface, |p| p.button(0, 0, button, state));
|
||||
}
|
||||
|
||||
pub fn focus_surface(&self, surface: &Rc<WlSurface>) {
|
||||
let pressed_keys: Vec<_> = self.pressed_keys.borrow().iter().copied().collect();
|
||||
self.surface_kb_event(&surface, |k| {
|
||||
self.surface_kb_event(0, &surface, |k| {
|
||||
k.enter(0, surface.id, pressed_keys.clone())
|
||||
});
|
||||
let ModifierState {
|
||||
|
|
@ -153,15 +153,13 @@ impl WlSeatGlobal {
|
|||
mods_locked,
|
||||
group,
|
||||
} = self.kb_state.borrow().mods();
|
||||
self.surface_kb_event(surface, |k| {
|
||||
self.surface_kb_event(0, surface, |k| {
|
||||
k.modifiers(0, mods_depressed, mods_latched, mods_locked, group)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn unfocus_surface(&self, surface: &Rc<WlSurface>) {
|
||||
self.surface_kb_event(surface, |k| {
|
||||
k.leave(0, surface.id)
|
||||
})
|
||||
self.surface_kb_event(0, surface, |k| k.leave(0, surface.id))
|
||||
}
|
||||
|
||||
fn focus_toplevel(&self, toplevel: &Rc<XdgToplevel>) {
|
||||
|
|
@ -191,23 +189,25 @@ impl WlSeatGlobal {
|
|||
self.set_new_position(x, y);
|
||||
}
|
||||
|
||||
fn for_each_seat<C>(&self, client: ClientId, mut f: C)
|
||||
fn for_each_seat<C>(&self, ver: u32, client: ClientId, mut f: C)
|
||||
where
|
||||
C: FnMut(&Rc<WlSeatObj>),
|
||||
{
|
||||
let bindings = self.bindings.borrow();
|
||||
if let Some(hm) = bindings.get(&client) {
|
||||
for seat in hm.values() {
|
||||
f(seat);
|
||||
if seat.version >= ver {
|
||||
f(seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_pointer<C>(&self, client: ClientId, mut f: C)
|
||||
fn for_each_pointer<C>(&self, ver: u32, client: ClientId, mut f: C)
|
||||
where
|
||||
C: FnMut(&Rc<WlPointer>),
|
||||
{
|
||||
self.for_each_seat(client, |seat| {
|
||||
self.for_each_seat(ver, client, |seat| {
|
||||
let pointers = seat.pointers.lock();
|
||||
for pointer in pointers.values() {
|
||||
f(pointer);
|
||||
|
|
@ -215,11 +215,11 @@ impl WlSeatGlobal {
|
|||
})
|
||||
}
|
||||
|
||||
fn for_each_kb<C>(&self, client: ClientId, mut f: C)
|
||||
fn for_each_kb<C>(&self, ver: u32, client: ClientId, mut f: C)
|
||||
where
|
||||
C: FnMut(&Rc<WlKeyboard>),
|
||||
{
|
||||
self.for_each_seat(client, |seat| {
|
||||
self.for_each_seat(ver, client, |seat| {
|
||||
let keyboards = seat.keyboards.lock();
|
||||
for keyboard in keyboards.values() {
|
||||
f(keyboard);
|
||||
|
|
@ -227,23 +227,23 @@ impl WlSeatGlobal {
|
|||
})
|
||||
}
|
||||
|
||||
fn surface_pointer_event<F>(&self, surface: &WlSurface, mut f: F)
|
||||
fn surface_pointer_event<F>(&self, ver: u32, surface: &WlSurface, mut f: F)
|
||||
where
|
||||
F: FnMut(&Rc<WlPointer>) -> DynEventFormatter,
|
||||
{
|
||||
let client = &surface.client;
|
||||
self.for_each_pointer(client.id, |p| {
|
||||
self.for_each_pointer(ver, client.id, |p| {
|
||||
client.event(f(p));
|
||||
});
|
||||
client.flush();
|
||||
}
|
||||
|
||||
fn surface_kb_event<F>(&self, surface: &WlSurface, mut f: F)
|
||||
fn surface_kb_event<F>(&self, ver: u32, surface: &WlSurface, mut f: F)
|
||||
where
|
||||
F: FnMut(&Rc<WlKeyboard>) -> DynEventFormatter,
|
||||
{
|
||||
let client = &surface.client;
|
||||
self.for_each_kb(client.id, |p| {
|
||||
self.for_each_kb(ver, client.id, |p| {
|
||||
client.event(f(p));
|
||||
});
|
||||
client.flush();
|
||||
|
|
@ -321,7 +321,7 @@ impl WlSeatGlobal {
|
|||
}
|
||||
|
||||
pub fn leave_surface(&self, n: &WlSurface) {
|
||||
self.surface_pointer_event(n, |p| p.leave(0, n.id));
|
||||
self.surface_pointer_event(0, n, |p| p.leave(0, n.id));
|
||||
}
|
||||
|
||||
pub fn enter_toplevel(&self, n: &Rc<XdgToplevel>) {
|
||||
|
|
@ -329,12 +329,12 @@ impl WlSeatGlobal {
|
|||
}
|
||||
|
||||
pub fn enter_surface(&self, n: &WlSurface, x: Fixed, y: Fixed) {
|
||||
self.surface_pointer_event(n, |p| p.enter(0, n.id, x, y));
|
||||
self.surface_pointer_event(0, n, |p| p.enter(0, n.id, x, y));
|
||||
}
|
||||
|
||||
pub fn motion_surface(&self, n: &WlSurface, x: Fixed, y: Fixed) {
|
||||
self.surface_pointer_event(n, |p| p.motion(0, x, y));
|
||||
self.surface_pointer_event(n, |p| p.frame());
|
||||
self.surface_pointer_event(0, n, |p| p.motion(0, x, y));
|
||||
self.surface_pointer_event(POINTER_FRAME_SINCE_VERSION, n, |p| p.frame());
|
||||
}
|
||||
|
||||
fn motion_event(&self, dx: Fixed, dy: Fixed) {
|
||||
|
|
@ -370,8 +370,8 @@ impl WlSeatGlobal {
|
|||
ScrollAxis::Horizontal => wl_pointer::HORIZONTAL_SCROLL,
|
||||
ScrollAxis::Vertical => wl_pointer::VERTICAL_SCROLL,
|
||||
};
|
||||
self.surface_pointer_event(surface, |p| p.axis(0, axis, Fixed::from_int(delta)));
|
||||
self.surface_pointer_event(surface, |p| p.frame());
|
||||
self.surface_pointer_event(0, surface, |p| p.axis(0, axis, Fixed::from_int(delta)));
|
||||
self.surface_pointer_event(POINTER_FRAME_SINCE_VERSION, surface, |p| p.frame());
|
||||
}
|
||||
|
||||
fn scroll_event(&self, delta: i32, axis: ScrollAxis) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ const CONTINUOUS: u32 = 2;
|
|||
#[allow(dead_code)]
|
||||
const WHEEL_TILT: u32 = 3;
|
||||
|
||||
pub const POINTER_FRAME_SINCE_VERSION: u32 = 5;
|
||||
|
||||
id!(WlPointerId);
|
||||
|
||||
pub struct WlPointer {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
mod types;
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::format::FORMATS;
|
||||
use crate::globals::{Global, GlobalName};
|
||||
use crate::ifs::wl_shm_pool::WlShmPool;
|
||||
use crate::object::{Interface, Object, ObjectId};
|
||||
|
|
@ -41,7 +42,7 @@ impl WlShmGlobal {
|
|||
client: client.clone(),
|
||||
});
|
||||
client.add_client_obj(&obj)?;
|
||||
for &format in client.state.formats.values() {
|
||||
for format in FORMATS {
|
||||
client.event(Box::new(FormatE {
|
||||
obj: obj.clone(),
|
||||
format,
|
||||
|
|
|
|||
|
|
@ -65,7 +65,8 @@ pub(super) struct FormatE {
|
|||
}
|
||||
impl EventFormatter for FormatE {
|
||||
fn format(self: Box<Self>, fmt: &mut MsgFormatter<'_>) {
|
||||
fmt.header(self.obj.id, FORMAT).uint(self.format.id);
|
||||
fmt.header(self.obj.id, FORMAT)
|
||||
.uint(self.format.wl_id.unwrap_or(self.format.drm));
|
||||
}
|
||||
fn obj(&self) -> &dyn Object {
|
||||
&*self.obj
|
||||
|
|
@ -76,7 +77,8 @@ impl Debug for FormatE {
|
|||
write!(
|
||||
f,
|
||||
"format(format: \"{}\" (0x{:x}))",
|
||||
self.format.name, self.format.id
|
||||
self.format.name,
|
||||
self.format.wl_id.unwrap_or(self.format.drm),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ mod types;
|
|||
|
||||
use crate::client::Client;
|
||||
use crate::clientmem::ClientMem;
|
||||
use crate::format::{formats, map_wayland_format_id};
|
||||
use crate::ifs::wl_buffer::WlBuffer;
|
||||
use crate::object::{Interface, Object, ObjectId};
|
||||
use crate::utils::buffd::MsgParser;
|
||||
|
|
@ -40,7 +41,8 @@ impl WlShmPool {
|
|||
|
||||
fn create_buffer(&self, parser: MsgParser<'_, '_>) -> Result<(), CreateBufferError> {
|
||||
let req: CreateBuffer = self.client.parse(self, parser)?;
|
||||
let format = match self.client.state.formats.get(&req.format) {
|
||||
let drm_format = map_wayland_format_id(req.format);
|
||||
let format = match formats().get(&drm_format) {
|
||||
Some(f) => *f,
|
||||
_ => return Err(CreateBufferError::InvalidFormat(req.format)),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@ mod types;
|
|||
pub mod wl_subsurface;
|
||||
pub mod xdg_surface;
|
||||
|
||||
use crate::backend::{KeyState, ScrollAxis};
|
||||
use crate::client::{Client, RequestParser};
|
||||
use crate::fixed::Fixed;
|
||||
use crate::gles2::gl::GlTexture;
|
||||
use crate::ifs::wl_buffer::WlBuffer;
|
||||
use crate::ifs::wl_callback::WlCallback;
|
||||
use crate::ifs::wl_seat::WlSeatGlobal;
|
||||
use crate::ifs::wl_surface::wl_subsurface::WlSubsurface;
|
||||
use crate::object::{Interface, Object, ObjectId};
|
||||
use crate::pixman::Region;
|
||||
|
|
@ -20,9 +24,6 @@ use std::mem;
|
|||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
pub use types::*;
|
||||
use crate::backend::{KeyState, ScrollAxis};
|
||||
use crate::fixed::Fixed;
|
||||
use crate::ifs::wl_seat::WlSeatGlobal;
|
||||
|
||||
const DESTROY: u32 = 0;
|
||||
const ATTACH: u32 = 1;
|
||||
|
|
@ -342,6 +343,7 @@ impl WlSurface {
|
|||
buffer.surfaces.remove(&self.id);
|
||||
}
|
||||
if let Some((dx, dy, buffer)) = buffer_change {
|
||||
let _ = buffer.update_texture();
|
||||
new_size = Some(buffer.rect);
|
||||
self.buffer.set(Some(buffer));
|
||||
self.buf_x.fetch_add(dx);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
mod types;
|
||||
|
||||
use crate::backend::{KeyState, ScrollAxis};
|
||||
use crate::fixed::Fixed;
|
||||
use crate::ifs::wl_seat::WlSeatGlobal;
|
||||
use crate::ifs::wl_surface::{
|
||||
CommitAction, CommitContext, StackElement, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceId,
|
||||
};
|
||||
|
|
@ -13,9 +16,6 @@ use std::cell::{Cell, RefCell};
|
|||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
pub use types::*;
|
||||
use crate::backend::{KeyState, ScrollAxis};
|
||||
use crate::fixed::Fixed;
|
||||
use crate::ifs::wl_seat::WlSeatGlobal;
|
||||
|
||||
const DESTROY: u32 = 0;
|
||||
const SET_POSITION: u32 = 1;
|
||||
|
|
|
|||
10
src/main.rs
10
src/main.rs
|
|
@ -2,7 +2,8 @@
|
|||
generic_associated_types,
|
||||
type_alias_impl_trait,
|
||||
never_type,
|
||||
c_variadic
|
||||
c_variadic,
|
||||
thread_local
|
||||
)]
|
||||
#![allow(
|
||||
clippy::len_zero,
|
||||
|
|
@ -18,6 +19,7 @@ use crate::backends::xorg::{XorgBackend, XorgBackendError};
|
|||
use crate::client::Clients;
|
||||
use crate::clientmem::ClientMemError;
|
||||
use crate::event_loop::EventLoopError;
|
||||
use crate::gles2::egl;
|
||||
use crate::globals::{AddGlobal, Globals};
|
||||
use crate::ifs::wl_compositor::WlCompositorGlobal;
|
||||
use crate::ifs::wl_data_device_manager::WlDataDeviceManagerGlobal;
|
||||
|
|
@ -48,9 +50,11 @@ mod backend;
|
|||
mod backends;
|
||||
mod client;
|
||||
mod clientmem;
|
||||
mod drm;
|
||||
mod event_loop;
|
||||
mod fixed;
|
||||
mod format;
|
||||
mod gles2;
|
||||
mod globals;
|
||||
mod ifs;
|
||||
mod object;
|
||||
|
|
@ -96,6 +100,8 @@ enum MainError {
|
|||
}
|
||||
|
||||
fn main_() -> Result<(), MainError> {
|
||||
egl::init();
|
||||
|
||||
clientmem::init()?;
|
||||
let el = EventLoop::new()?;
|
||||
sighand::install(&el)?;
|
||||
|
|
@ -111,11 +117,11 @@ fn main_() -> Result<(), MainError> {
|
|||
let state = Rc::new(State {
|
||||
eng: engine.clone(),
|
||||
el: el.clone(),
|
||||
egl: Default::default(),
|
||||
wheel,
|
||||
clients: Clients::new(),
|
||||
next_name: NumCell::new(1),
|
||||
globals,
|
||||
formats: format::formats(),
|
||||
output_ids: Default::default(),
|
||||
root: Rc::new(DisplayNode::new(node_ids.next())),
|
||||
node_ids,
|
||||
|
|
|
|||
335
src/render/gles.rs
Normal file
335
src/render/gles.rs
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
use crate::egl::EglContext;
|
||||
use crate::gles2::gl::{with_scissor, GlFrameBuffer, GlProgram, GlShader};
|
||||
use crate::gles2::sys::{
|
||||
glActiveTexture, glBindTexture, glClear, glClearColor, glDisableVertexAttribArray,
|
||||
glDrawArrays, glEnableVertexAttribArray, glTexParameteri, glUniform1f, glUniform1i,
|
||||
glVertexAttribPointer, GLint, GL_COLOR_BUFFER_BIT, GL_FALSE, GL_FLOAT, GL_FRAGMENT_SHADER,
|
||||
GL_LINEAR, GL_TEXTURE0, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_TRIANGLE_STRIP,
|
||||
GL_VERTEX_SHADER,
|
||||
};
|
||||
use crate::gles2::GlesError;
|
||||
use crate::ifs::wl_buffer::WlBuffer;
|
||||
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
|
||||
use crate::ifs::wl_surface::WlSurface;
|
||||
use crate::pixman::Image;
|
||||
use crate::render::{Border, Renderer};
|
||||
use crate::servermem::ServerMem;
|
||||
use crate::tree::{
|
||||
ContainerFocus, ContainerNode, ContainerSplit, CONTAINER_BORDER, CONTAINER_TITLE_HEIGHT,
|
||||
};
|
||||
use crate::tree::{FloatNode, OutputNode, WorkspaceNode};
|
||||
use renderdoc::{RenderDoc, V100};
|
||||
use std::borrow::BorrowMut;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use uapi::ustr;
|
||||
|
||||
pub const RENDERDOC: bool = false;
|
||||
|
||||
pub struct GlesRenderer {
|
||||
ctx: Rc<EglContext>,
|
||||
|
||||
renderdoc: Option<RefCell<RenderDoc<V100>>>,
|
||||
|
||||
tex_prog: GlProgram,
|
||||
tex_prog_pos: GLint,
|
||||
tex_prog_texcoord: GLint,
|
||||
tex_prog_tex: GLint,
|
||||
}
|
||||
|
||||
impl GlesRenderer {
|
||||
pub unsafe fn new(ctx: &Rc<EglContext>) -> Result<Self, GlesError> {
|
||||
let vert = GlShader::compile(ctx, GL_VERTEX_SHADER, include_str!("shaders/tex.vert.glsl"))?;
|
||||
let frag = GlShader::compile(
|
||||
ctx,
|
||||
GL_FRAGMENT_SHADER,
|
||||
include_str!("shaders/tex.frag.glsl"),
|
||||
)?;
|
||||
let prog = GlProgram::link(&vert, &frag)?;
|
||||
Ok(Self {
|
||||
ctx: ctx.clone(),
|
||||
tex_prog_pos: prog.get_attrib_location(ustr!("pos")),
|
||||
tex_prog_texcoord: prog.get_attrib_location(ustr!("texcoord")),
|
||||
tex_prog_tex: prog.get_uniform_location(ustr!("tex")),
|
||||
tex_prog: prog,
|
||||
renderdoc: if RENDERDOC {
|
||||
Some(RefCell::new(RenderDoc::new().unwrap()))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render_fb<'a>(&'a self, fb: &'a GlFrameBuffer) -> GlesImageRenderer<'a> {
|
||||
if let Some(rd) = &self.renderdoc {
|
||||
rd.borrow_mut()
|
||||
.start_frame_capture(ptr::null(), ptr::null());
|
||||
}
|
||||
GlesImageRenderer {
|
||||
renderer: self,
|
||||
image: fb,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlesImageRenderer<'a> {
|
||||
renderer: &'a GlesRenderer,
|
||||
image: &'a GlFrameBuffer,
|
||||
}
|
||||
|
||||
impl Drop for GlesImageRenderer<'_> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(rd) = &self.renderer.renderdoc {
|
||||
rd.borrow_mut().end_frame_capture(ptr::null(), ptr::null());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const NON_COLOR: (u8, u8, u8) = (100, 100, 100);
|
||||
const CHILD_COLOR: (u8, u8, u8) = (200, 200, 200);
|
||||
const YES_COLOR: (u8, u8, u8) = (0, 0, 255);
|
||||
|
||||
fn focus_color(focus: ContainerFocus) -> (u8, u8, u8) {
|
||||
match focus {
|
||||
ContainerFocus::None => NON_COLOR,
|
||||
ContainerFocus::Child => CHILD_COLOR,
|
||||
ContainerFocus::Yes => YES_COLOR,
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for GlesImageRenderer<'_> {
|
||||
fn render_output(&mut self, output: &OutputNode) {
|
||||
unsafe {
|
||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
if let Some(ws) = output.workspace.get() {
|
||||
self.render_workspace(&ws);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_workspace(&mut self, workspace: &WorkspaceNode) {
|
||||
if let Some(node) = workspace.container.get() {
|
||||
self.render_container(&node, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32) {
|
||||
let cwidth = container.width.get();
|
||||
let cheight = container.height.get();
|
||||
let num_children = container.num_children();
|
||||
if let Some(child) = container.mono_child.get() {
|
||||
let space_per_child = cwidth / num_children as i32;
|
||||
let mut rem = cwidth % num_children as i32;
|
||||
let mut pos = x;
|
||||
for child in container.children.iter() {
|
||||
let (r, g, b) = focus_color(child.focus.get());
|
||||
let mut width = space_per_child;
|
||||
if rem > 0 {
|
||||
rem -= 1;
|
||||
width += 1;
|
||||
}
|
||||
// let _ = self.image.fill_rect(
|
||||
// r,
|
||||
// g,
|
||||
// b,
|
||||
// 255,
|
||||
// pos,
|
||||
// y,
|
||||
// pos + width as i32,
|
||||
// y + CONTAINER_TITLE_HEIGHT as i32,
|
||||
// );
|
||||
pos += width as i32;
|
||||
}
|
||||
with_scissor(&container.mono_body.get(), || {
|
||||
let content = container.mono_content.get();
|
||||
child.node.render(self, x + content.x1(), y + content.y1());
|
||||
});
|
||||
} else {
|
||||
let split = container.split.get();
|
||||
for (i, child) in container.children.iter().enumerate() {
|
||||
let body = child.body.get();
|
||||
if body.x1() >= cwidth || body.y1() >= cheight {
|
||||
break;
|
||||
}
|
||||
let (r, g, b) = focus_color(child.focus.get());
|
||||
// let _ = self.image.fill_rect(
|
||||
// r,
|
||||
// g,
|
||||
// b,
|
||||
// 255,
|
||||
// x + body.x1(),
|
||||
// y + body.y1() - CONTAINER_TITLE_HEIGHT,
|
||||
// x + body.x2(),
|
||||
// y + body.y1(),
|
||||
// );
|
||||
{
|
||||
let mut x1 = x + body.x1();
|
||||
let mut x2 = x + body.x2();
|
||||
let mut y2 = y + body.y2();
|
||||
let mut border = Border::empty();
|
||||
if i < num_children {
|
||||
if split == ContainerSplit::Horizontal {
|
||||
border |= Border::RIGHT;
|
||||
x2 += CONTAINER_BORDER;
|
||||
} else if split == ContainerSplit::Vertical {
|
||||
border |= Border::BOTTOM;
|
||||
y2 += CONTAINER_BORDER;
|
||||
}
|
||||
}
|
||||
if i > 0 && split == ContainerSplit::Horizontal {
|
||||
border |= Border::LEFT;
|
||||
x1 -= CONTAINER_BORDER;
|
||||
}
|
||||
// let _ = self.image.fill_inner_border(
|
||||
// r,
|
||||
// g,
|
||||
// b,
|
||||
// 255,
|
||||
// x1,
|
||||
// y + body.y1() - CONTAINER_TITLE_HEIGHT,
|
||||
// x2,
|
||||
// y2,
|
||||
// CONTAINER_BORDER as i32,
|
||||
// border,
|
||||
// );
|
||||
}
|
||||
with_scissor(&body, || {
|
||||
let content = child.content.get();
|
||||
child.node.render(self, x + content.x1(), y + content.y1());
|
||||
// self.image.fill_inner_border(
|
||||
// 0,
|
||||
// 0,
|
||||
// 255,
|
||||
// 255,
|
||||
// x + body.x1(),
|
||||
// y + body.y1(),
|
||||
// x + body.x1() + body.width(),
|
||||
// y + body.y1() + body.height(),
|
||||
// 2,
|
||||
// Border::all(),
|
||||
// );
|
||||
// self.image.fill_inner_border(
|
||||
// 255,
|
||||
// 0,
|
||||
// 0,
|
||||
// 255,
|
||||
// x + content.x1(),
|
||||
// y + content.y1(),
|
||||
// x + content.x1() + content.width(),
|
||||
// y + content.y1() + content.height(),
|
||||
// 2,
|
||||
// Border::all(),
|
||||
// );
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_toplevel(&mut self, tl: &XdgToplevel, mut x: i32, mut y: i32) {
|
||||
let surface = &tl.xdg.surface;
|
||||
if let Some(geo) = tl.xdg.geometry() {
|
||||
let (xt, yt) = geo.translate(x, y);
|
||||
x = xt;
|
||||
y = yt;
|
||||
}
|
||||
self.render_surface(surface, x, y);
|
||||
}
|
||||
|
||||
fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32) {
|
||||
let children = surface.children.borrow();
|
||||
let buffer = match surface.buffer.get() {
|
||||
Some(b) => b,
|
||||
_ => {
|
||||
log::warn!("surface has no buffer attached");
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Some(children) = children.deref() {
|
||||
macro_rules! render {
|
||||
($children:expr) => {
|
||||
for child in $children.rev_iter() {
|
||||
if child.pending.get() {
|
||||
continue;
|
||||
}
|
||||
let pos = child.sub_surface.position.get();
|
||||
self.render_surface(&child.sub_surface.surface, x + pos.x1(), y + pos.y1());
|
||||
}
|
||||
};
|
||||
}
|
||||
render!(&children.above);
|
||||
self.render_buffer(&buffer, x, y);
|
||||
render!(&children.below);
|
||||
} else {
|
||||
self.render_buffer(&buffer, x, y);
|
||||
}
|
||||
let mut fr = surface.frame_requests.borrow_mut();
|
||||
for cb in fr.drain(..) {
|
||||
surface.client.dispatch_frame_requests.push(cb);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_buffer(&mut self, buffer: &WlBuffer, x: i32, y: i32) {
|
||||
let texture = match buffer.texture.get() {
|
||||
Some(t) => t,
|
||||
_ => return,
|
||||
};
|
||||
unsafe {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture.tex);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
self.renderer.tex_prog.use_();
|
||||
|
||||
glUniform1i(self.renderer.tex_prog_tex, 0);
|
||||
|
||||
let texcoord: [f32; 8] = [1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0];
|
||||
|
||||
let f_width = self.image.width as f32;
|
||||
let f_height = self.image.height as f32;
|
||||
|
||||
let x1 = 2.0 * (x as f32 / f_width) - 1.0;
|
||||
let y1 = 2.0 * (y as f32 / f_height) - 1.0;
|
||||
let x2 = 2.0 * ((x + texture.width) as f32 / f_width) - 1.0;
|
||||
let y2 = 2.0 * ((y + texture.height) as f32 / f_height) - 1.0;
|
||||
|
||||
let pos: [f32; 8] = [x2, y1, x1, y1, x2, y2, x1, y2];
|
||||
|
||||
glVertexAttribPointer(
|
||||
self.renderer.tex_prog_texcoord as _,
|
||||
2,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
0,
|
||||
texcoord.as_ptr() as _,
|
||||
);
|
||||
glVertexAttribPointer(
|
||||
self.renderer.tex_prog_pos as _,
|
||||
2,
|
||||
GL_FLOAT,
|
||||
GL_FALSE,
|
||||
0,
|
||||
pos.as_ptr() as _,
|
||||
);
|
||||
|
||||
glEnableVertexAttribArray(self.renderer.tex_prog_texcoord as _);
|
||||
glEnableVertexAttribArray(self.renderer.tex_prog_pos as _);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glDisableVertexAttribArray(self.renderer.tex_prog_texcoord as _);
|
||||
glDisableVertexAttribArray(self.renderer.tex_prog_pos as _);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32) {
|
||||
if let Some(child) = floating.child.get() {
|
||||
child.render(self, x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ use crate::ifs::wl_surface::WlSurface;
|
|||
use crate::tree::ContainerNode;
|
||||
use crate::tree::{FloatNode, OutputNode, WorkspaceNode};
|
||||
|
||||
pub mod gles;
|
||||
pub mod pixman;
|
||||
|
||||
bitflags::bitflags! {
|
||||
|
|
|
|||
|
|
@ -75,9 +75,7 @@ impl Renderer for PixmanRenderer<'_> {
|
|||
}
|
||||
self.image.with_clip(container.mono_body.get(), || {
|
||||
let content = container.mono_content.get();
|
||||
child
|
||||
.node
|
||||
.render(self, x + content.x1(), y + content.y1());
|
||||
child.node.render(self, x + content.x1(), y + content.y1());
|
||||
});
|
||||
} else {
|
||||
let split = container.split.get();
|
||||
|
|
@ -131,8 +129,30 @@ impl Renderer for PixmanRenderer<'_> {
|
|||
self.image.with_clip(body, || {
|
||||
let content = child.content.get();
|
||||
child.node.render(self, x + content.x1(), y + content.y1());
|
||||
self.image.fill_inner_border(0, 0, 255, 255, x + body.x1(), y + body.y1(), x + body.x1() + body.width(), y + body.y1() + body.height(), 2, Border::all());
|
||||
self.image.fill_inner_border(255, 0, 0, 255, x + content.x1(), y + content.y1(), x + content.x1() + content.width(), y + content.y1() + content.height(), 2, Border::all());
|
||||
self.image.fill_inner_border(
|
||||
0,
|
||||
0,
|
||||
255,
|
||||
255,
|
||||
x + body.x1(),
|
||||
y + body.y1(),
|
||||
x + body.x1() + body.width(),
|
||||
y + body.y1() + body.height(),
|
||||
2,
|
||||
Border::all(),
|
||||
);
|
||||
self.image.fill_inner_border(
|
||||
255,
|
||||
0,
|
||||
0,
|
||||
255,
|
||||
x + content.x1(),
|
||||
y + content.y1(),
|
||||
x + content.x1() + content.width(),
|
||||
y + content.y1() + content.height(),
|
||||
2,
|
||||
Border::all(),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -154,8 +174,8 @@ impl Renderer for PixmanRenderer<'_> {
|
|||
Some(b) => b,
|
||||
_ => {
|
||||
log::warn!("surface has no buffer attached");
|
||||
return
|
||||
},
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Some(children) = children.deref() {
|
||||
macro_rules! render {
|
||||
|
|
|
|||
7
src/render/shaders/tex.frag.glsl
Normal file
7
src/render/shaders/tex.frag.glsl
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
precision mediump float;
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(texture2D(tex, v_texcoord).rgb, 1.0);
|
||||
}
|
||||
8
src/render/shaders/tex.vert.glsl
Normal file
8
src/render/shaders/tex.vert.glsl
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
attribute vec2 pos;
|
||||
attribute vec2 texcoord;
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
v_texcoord = texcoord;
|
||||
}
|
||||
|
|
@ -1,14 +1,15 @@
|
|||
use crate::async_engine::{AsyncEngine, SpawnedFuture};
|
||||
use crate::backend::{BackendEvent, OutputId, OutputIds, SeatId, SeatIds};
|
||||
use crate::client::{Client, Clients};
|
||||
use crate::egl::EglContext;
|
||||
use crate::event_loop::EventLoop;
|
||||
use crate::format::Format;
|
||||
use crate::globals::{AddGlobal, Globals};
|
||||
use crate::ifs::wl_output::WlOutputGlobal;
|
||||
use crate::ifs::wl_seat::WlSeatGlobal;
|
||||
use crate::ifs::wl_surface::NoneSurfaceExt;
|
||||
use crate::tree::{DisplayNode, NodeIds};
|
||||
use crate::utils::asyncevent::AsyncEvent;
|
||||
use crate::utils::clonecell::CloneCell;
|
||||
use crate::utils::copyhashmap::CopyHashMap;
|
||||
use crate::utils::linkedlist::LinkedList;
|
||||
use crate::utils::numcell::NumCell;
|
||||
|
|
@ -21,11 +22,11 @@ use std::rc::Rc;
|
|||
pub struct State {
|
||||
pub eng: Rc<AsyncEngine>,
|
||||
pub el: Rc<EventLoop>,
|
||||
pub egl: CloneCell<Option<Rc<EglContext>>>,
|
||||
pub wheel: Rc<Wheel>,
|
||||
pub clients: Clients,
|
||||
pub next_name: NumCell<u32>,
|
||||
pub globals: Globals,
|
||||
pub formats: AHashMap<u32, &'static Format>,
|
||||
pub output_ids: OutputIds,
|
||||
pub seat_ids: SeatIds,
|
||||
pub node_ids: NodeIds,
|
||||
|
|
|
|||
|
|
@ -113,7 +113,11 @@ impl ContainerNode {
|
|||
}
|
||||
|
||||
pub fn add_child_after(self: &Rc<Self>, prev: &dyn Node, new: Rc<dyn Node>) {
|
||||
let node = self.child_nodes.borrow().get(&prev.id()).map(|n| n.to_ref());
|
||||
let node = self
|
||||
.child_nodes
|
||||
.borrow()
|
||||
.get(&prev.id())
|
||||
.map(|n| n.to_ref());
|
||||
if let Some(node) = node {
|
||||
self.add_child_after_(&node, new);
|
||||
return;
|
||||
|
|
@ -185,7 +189,9 @@ impl ContainerNode {
|
|||
body_size = body_size.min(remaining_content_size);
|
||||
remaining_content_size -= body_size;
|
||||
let (x1, y1, width, height) = match split {
|
||||
ContainerSplit::Horizontal => (pos, CONTAINER_TITLE_HEIGHT, body_size, other_content_size),
|
||||
ContainerSplit::Horizontal => {
|
||||
(pos, CONTAINER_TITLE_HEIGHT, body_size, other_content_size)
|
||||
}
|
||||
_ => (0, pos, other_content_size, body_size),
|
||||
};
|
||||
let body = Rect::new_sized(x1, y1, width, height).unwrap();
|
||||
|
|
@ -209,7 +215,13 @@ impl ContainerNode {
|
|||
let (x1, y1, width, height, size) = match split {
|
||||
ContainerSplit::Horizontal => {
|
||||
let width = body.width() + add;
|
||||
(pos, CONTAINER_TITLE_HEIGHT, width, other_content_size, width)
|
||||
(
|
||||
pos,
|
||||
CONTAINER_TITLE_HEIGHT,
|
||||
width,
|
||||
other_content_size,
|
||||
width,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let height = body.height() + add;
|
||||
|
|
|
|||
|
|
@ -238,7 +238,8 @@ impl Node for OutputNode {
|
|||
}
|
||||
|
||||
fn change_size(self: Rc<Self>, width: i32, height: i32) {
|
||||
self.position.set(Rect::new_sized(0, 0, width, height).unwrap());
|
||||
self.position
|
||||
.set(Rect::new_sized(0, 0, width, height).unwrap());
|
||||
if let Some(c) = self.workspace.get() {
|
||||
c.change_size(width, height);
|
||||
}
|
||||
|
|
@ -294,6 +295,7 @@ impl Node for FloatNode {
|
|||
|
||||
fn child_size_changed(&self, _child: &dyn Node, width: i32, height: i32) {
|
||||
let pos = self.position.get();
|
||||
self.position.set(Rect::new_sized(pos.x1(), pos.x2(), width, height).unwrap());
|
||||
self.position
|
||||
.set(Rect::new_sized(pos.x1(), pos.x2(), width, height).unwrap());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
22
src/utils/bitflags.rs
Normal file
22
src/utils/bitflags.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
pub trait BitflagsExt {
|
||||
fn contains(self, other: Self) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! num {
|
||||
($ty:ident) => {
|
||||
impl BitflagsExt for $ty {
|
||||
fn contains(self, other: Self) -> bool {
|
||||
self & other != 0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
num!(u8);
|
||||
num!(u16);
|
||||
num!(u32);
|
||||
num!(u64);
|
||||
num!(i8);
|
||||
num!(i16);
|
||||
num!(i32);
|
||||
num!(i64);
|
||||
21
src/utils/debug_fn.rs
Normal file
21
src/utils/debug_fn.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
pub fn debug_fn<F>(f: F) -> impl Debug
|
||||
where
|
||||
F: Fn(&mut Formatter<'_>) -> std::fmt::Result,
|
||||
{
|
||||
DebugFn { f }
|
||||
}
|
||||
|
||||
struct DebugFn<F> {
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<F> Debug for DebugFn<F>
|
||||
where
|
||||
F: Fn(&mut Formatter<'_>) -> std::fmt::Result,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
(self.f)(f)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
pub mod asyncevent;
|
||||
pub mod bitflags;
|
||||
pub mod buffd;
|
||||
pub mod clonecell;
|
||||
pub mod copyhashmap;
|
||||
pub mod debug_fn;
|
||||
pub mod errorfmt;
|
||||
pub mod linkedlist;
|
||||
pub mod numcell;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue