1
0
Fork 0
forked from wry/wry

autocommit 2022-01-28 19:46:23 CET

This commit is contained in:
Julian Orth 2022-01-28 19:46:23 +01:00
parent a5573b8a3a
commit b11a36729c
45 changed files with 1646 additions and 2171 deletions

View file

@ -4,31 +4,21 @@ use crate::backend::{
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};
use crate::tree::Node;
use crate::render::{Framebuffer, RenderContext, RenderError};
use crate::servermem::ServerMemError;
use crate::utils::clonecell::CloneCell;
use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::ptr_ext::PtrExt;
use crate::wheel::{WheelDispatcher, WheelId};
use crate::{gles2, pixman, EventLoopError, NumCell, State, WheelError};
use gles2::egl;
use crate::{EventLoopError, NumCell, State, WheelError};
use isnt::std_1::primitive::IsntConstPtrExt;
use rand::Rng;
use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::error::Error;
use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
use thiserror::Error;
@ -45,16 +35,12 @@ pub enum XorgBackendError {
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),
CreateEgl(#[source] RenderError),
#[error("Could not create a framebuffer from a dma-buf")]
CreateFramebuffer(#[source] RenderError),
#[error("Could not select input events")]
CannotSelectInputEvents(#[source] XcbError),
#[error("Could not select present events")]
@ -69,10 +55,6 @@ pub enum XorgBackendError {
WheelError(#[source] Box<WheelError>),
#[error("Could not allocate and map image memory")]
ServerMemError(#[source] Box<ServerMemError>),
#[error("Pixman returned an error")]
PixmanError(#[source] Box<PixmanError>),
#[error("dupfd failed")]
DupfdFailed(#[source] std::io::Error),
#[error("Could not create a window")]
CreateWindow(#[source] XcbError),
#[error("Could not set WM_CLASS")]
@ -86,7 +68,6 @@ pub enum XorgBackendError {
}
efrom!(XorgBackendError, EventLoopError, EventLoopError);
efrom!(XorgBackendError, ServerMemError, ServerMemError);
efrom!(XorgBackendError, PixmanError, PixmanError);
efrom!(XorgBackendError, WheelError, WheelError);
struct XcbCon {
@ -221,8 +202,7 @@ 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,
ctx: Rc<RenderContext>,
gbm: GbmDevice,
r: Cell<f32>,
g: Cell<f32>,
@ -251,24 +231,11 @@ impl XorgBackend {
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 ctx = match RenderContext::from_drm_device(&drm) {
Ok(r) => Rc::new(r),
Err(e) => return Err(XorgBackendError::CreateEgl(e)),
};
let fd = con.xcb.xcb_get_file_descriptor(con.c);
@ -283,7 +250,6 @@ impl XorgBackend {
seats: Default::default(),
mouse_seats: Default::default(),
ctx: ctx.clone(),
renderer,
gbm,
r: Cell::new(0.0),
g: Cell::new(0.0),
@ -311,7 +277,7 @@ impl XorgBackend {
slf.query_devices(ffi::XCB_INPUT_DEVICE_ALL_MASTER as _)?;
slf.handle_events()?;
state.egl.set(Some(ctx.clone()));
state.render_ctx.set(Some(ctx.clone()));
Ok(slf)
}
@ -337,12 +303,9 @@ impl XorgBackend {
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 fb = match self.ctx.dmabuf_fb(dma) {
Ok(f) => f,
Err(e) => return Err(XorgBackendError::CreateFramebuffer(e)),
};
let pixmap = unsafe {
let pixmap = self.con.xcb.xcb_generate_id(self.con.c);
@ -416,7 +379,6 @@ impl XorgBackend {
serial: Default::default(),
next_msc: Cell::new(0),
next_image: Default::default(),
image: RefCell::new(None),
cb: CloneCell::new(None),
images,
});
@ -695,17 +657,7 @@ impl XorgBackend {
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();
fb.render(&*node);
}
unsafe {
@ -890,7 +842,6 @@ impl XorgBackend {
_ => return Ok(()),
};
output.removed.set(true);
*output.image.borrow_mut() = None;
output.changed();
Ok(())
}
@ -911,87 +862,12 @@ impl XorgBackend {
unsafe {
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());
}
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()?;
}
Ok(())
}
fn render(&self) -> Result<(), XorgBackendError> {
return Ok(());
let outputs = self.outputs.lock();
for output in outputs.values() {
let image = output.image.borrow();
let image = match image.deref() {
Some(i) => i,
None => continue,
};
let _ = image.fill(0, 0, 0, 255);
let node = match self.state.root.outputs.get(&output.id) {
Some(n) => n,
_ => continue,
};
let pos = node.position.get();
let mut renderer = PixmanRenderer::new(image);
renderer.render_output(&node);
for floating in self.state.root.floaters.iter() {
let fpos = floating.position.get();
if floating.visible.get() && fpos.intersects(&pos) {
let (x, y) = pos.translate(fpos.x1(), fpos.x2());
floating.render(&mut renderer, x, y);
}
}
unsafe {
let cookie =
self.con
.xcb
.xcb_clear_area_checked(self.con.c, 0, output.window, 0, 0, 0, 0);
self.con.check_cookie(cookie)?;
}
}
Ok(())
}
@ -1009,8 +885,6 @@ impl EventLoopDispatcher for XorgBackend {
impl WheelDispatcher for XorgBackend {
fn dispatch(self: Rc<Self>) -> Result<(), Box<dyn Error + Send + Sync>> {
self.render()?;
self.handle_events()?;
Ok(())
}
}
@ -1033,13 +907,12 @@ struct XorgOutput {
next_msc: Cell<u64>,
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>>,
fb: CloneCell<Rc<Framebuffer>>,
idle: Cell<bool>,
render_on_idle: Cell<bool>,
last_serial: Cell<u32>,

View file

@ -1,4 +1,3 @@
use crate::pixman::PixmanMemory;
use std::cell::{Cell, UnsafeCell};
use std::mem::MaybeUninit;
use std::ptr;
@ -165,14 +164,3 @@ pub fn init() -> Result<(), ClientMemError> {
}
}
}
unsafe impl PixmanMemory for ClientMemOffset {
type E = ClientMemError;
fn access<T, F>(&self, f: F) -> Result<T, Self::E>
where
F: FnOnce(&[Cell<u8>]) -> T,
{
ClientMemOffset::access(self, f)
}
}

View file

@ -11,8 +11,6 @@ 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")]

View file

@ -1,5 +1,5 @@
use crate::gles2::sys::{GLint, GL_BGRA_EXT, GL_UNSIGNED_BYTE};
use crate::pixman;
use crate::render::sys::{GLint, GL_BGRA_EXT, GL_UNSIGNED_BYTE};
use ahash::AHashMap;
use once_cell::sync::Lazy;

View file

@ -1,487 +0,0 @@
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,
}
}

View file

@ -1,363 +0,0 @@
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(())
});
}
}

View file

@ -1,58 +0,0 @@
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),
}

View file

@ -3,11 +3,10 @@ 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::render::Texture;
use crate::utils::buffd::MsgParser;
use crate::utils::clonecell::CloneCell;
use crate::utils::copyhashmap::CopyHashMap;
@ -27,9 +26,8 @@ pub struct WlBuffer {
pub rect: Rect,
stride: i32,
format: &'static Format,
pub image: Rc<pixman::Image<ClientMemOffset>>,
mem: ClientMemOffset,
pub texture: CloneCell<Option<Rc<GlTexture>>>,
pub texture: CloneCell<Option<Rc<Texture>>>,
pub(super) surfaces: CopyHashMap<WlSurfaceId, Rc<WlSurface>>,
width: i32,
height: i32,
@ -57,13 +55,6 @@ impl WlBuffer {
if (stride as u64) < min_row_size {
return Err(WlBufferError::StrideTooSmall);
}
let image = pixman::Image::new(
mem.clone(),
format.pixman.unwrap(),
width as u32,
height as u32,
stride as u32,
)?;
Ok(Self {
id,
client: client.clone(),
@ -71,7 +62,6 @@ impl WlBuffer {
rect: Rect::new_sized(0, 0, width, height).unwrap(),
stride,
format,
image: Rc::new(image),
mem,
width,
height,
@ -82,9 +72,9 @@ impl WlBuffer {
pub fn update_texture(&self) -> Result<(), WlBufferError> {
self.texture.set(None);
let ctx = self.client.state.egl.get().unwrap();
let ctx = self.client.state.render_ctx.get().unwrap();
let tex = self.mem.access(|mem| {
GlTexture::import_texture(&ctx, mem, self.format, self.width, self.height, self.stride)
ctx.shmem_texture(mem, self.format, self.width, self.height, self.stride)
})??;
self.texture.set(Some(tex));
Ok(())

View file

@ -1,8 +1,7 @@
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::render::RenderError;
use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError};
use crate::ClientMemError;
use std::fmt::{Debug, Formatter};
@ -17,16 +16,13 @@ pub enum WlBufferError {
StrideTooSmall,
#[error("Could not handle a `destroy` request")]
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>),
GlesError(#[source] Box<RenderError>),
}
efrom!(WlBufferError, PixmanError, PixmanError);
efrom!(WlBufferError, ClientMemError, ClientMemError);
efrom!(WlBufferError, GlesError, GlesError);
efrom!(WlBufferError, GlesError, RenderError);
#[derive(Debug, Error)]
pub enum DestroyError {

View file

@ -5,7 +5,6 @@ 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;

View file

@ -367,7 +367,7 @@ impl Node for XdgToplevel {
}
}
fn render(&self, renderer: &mut dyn Renderer, x: i32, y: i32) {
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_toplevel(self, x, y)
}

View file

@ -19,7 +19,6 @@ 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;
@ -27,6 +26,7 @@ use crate::ifs::wl_shm::WlShmGlobal;
use crate::ifs::wl_subcompositor::WlSubcompositorGlobal;
use crate::ifs::wl_surface::NoneSurfaceExt;
use crate::ifs::xdg_wm_base::XdgWmBaseGlobal;
use crate::render::RenderError;
use crate::sighand::SighandError;
use crate::state::State;
use crate::tree::{DisplayNode, NodeIds};
@ -54,7 +54,6 @@ mod drm;
mod event_loop;
mod fixed;
mod format;
mod gles2;
mod globals;
mod ifs;
mod object;
@ -97,10 +96,12 @@ enum MainError {
AsyncError(#[from] AsyncError),
#[error("The xorg backend caused an error")]
XorgBackendError(#[from] XorgBackendError),
#[error("The render backend caused an error")]
RenderError(#[from] RenderError),
}
fn main_() -> Result<(), MainError> {
egl::init();
render::init()?;
clientmem::init()?;
let el = EventLoop::new()?;
@ -117,7 +118,7 @@ fn main_() -> Result<(), MainError> {
let state = Rc::new(State {
eng: engine.clone(),
el: el.clone(),
egl: Default::default(),
render_ctx: Default::default(),
wheel,
clients: Clients::new(),
next_name: NumCell::new(1),

View file

@ -2,13 +2,8 @@ mod consts;
include!(concat!(env!("OUT_DIR"), "/pixman_tys.rs"));
use crate::rect::Rect;
use crate::render::Border;
use crate::ClientMemError;
pub use consts::*;
use std::cell::Cell;
use std::ptr;
use thiserror::Error;
use uapi::c;
#[link(name = "pixman-1")]
@ -26,37 +21,6 @@ extern "C" {
fn pixman_region32_copy(dst: *mut Region, src: *const Region);
fn pixman_region32_union(dst: *mut Region, a: *const Region, b: *const Region);
fn pixman_region32_subtract(dst: *mut Region, a: *const Region, b: *const Region);
fn pixman_image_set_clip_region32(image: *mut PixmanImage, region: *const Region);
fn pixman_image_create_bits_no_clear(
format: PixmanFormat,
width: c::c_int,
height: c::c_int,
bits: *mut u32,
stride: c::c_int,
) -> *mut PixmanImage;
fn pixman_image_unref(image: *mut PixmanImage) -> c::c_int;
// fn pixman_image_ref(image: *mut PixmanImage) -> *mut PixmanImage;
fn pixman_image_fill_boxes(
op: PixmanOp,
dest: *mut PixmanImage,
color: *const Color,
nboxes: c::c_int,
boxes: *const Box32,
) -> c::c_int;
fn pixman_image_composite32(
op: PixmanOp,
src: *mut PixmanImage,
mask: *mut PixmanImage,
dest: *mut PixmanImage,
src_x: i32,
src_y: i32,
mask_x: i32,
mask_y: i32,
dest_x: i32,
dest_y: i32,
width: i32,
height: i32,
);
}
#[repr(C)]
@ -144,244 +108,3 @@ impl Drop for Region {
}
}
}
pub unsafe trait PixmanMemory: Clone {
type E;
fn access<T, F>(&self, f: F) -> Result<T, Self::E>
where
F: FnOnce(&[Cell<u8>]) -> T;
}
#[derive(Debug, Error)]
pub enum PixmanError {
#[error("The image size values cannot be represented in c_int")]
SizeOverflow,
#[error("The pixman memory does not contain enough memory to hold the image")]
InsufficientMemory,
#[error("The stride does not contain enough bytes to contain a row")]
RowOverflow,
#[error("Pixman images must be aligned at a 4 byte boundary")]
UnalignedMemory,
#[error("Internal pixman error")]
Internal,
#[error(transparent)]
ClientMemError(Box<ClientMemError>),
}
efrom!(PixmanError, ClientMemError, ClientMemError);
impl From<!> for PixmanError {
fn from(_: !) -> Self {
unreachable!()
}
}
struct PixmanImage;
pub struct Image<T> {
data: *mut PixmanImage,
width: u32,
height: u32,
memory: T,
clip: Cell<Option<Region>>,
}
impl<T: PixmanMemory> Image<T>
where
PixmanError: From<<T as PixmanMemory>::E>,
{
pub fn new(
memory: T,
format: Format,
width: u32,
height: u32,
stride: u32,
) -> Result<Self, PixmanError> {
let data = memory.access(|m| {
if format_bpp(format.raw()) as u64 * width as u64 > stride as u64 * 8 {
return Err(PixmanError::RowOverflow);
}
if (m.len() as u64) < height as u64 * stride as u64 {
return Err(PixmanError::InsufficientMemory);
}
let (width, height, stride) =
match (width.try_into(), height.try_into(), stride.try_into()) {
(Ok(w), Ok(h), Ok(s)) => (w, h, s),
_ => return Err(PixmanError::SizeOverflow),
};
if m.as_ptr() as usize % 4 != 0 {
return Err(PixmanError::UnalignedMemory);
}
let data = unsafe {
pixman_image_create_bits_no_clear(
format.raw() as _,
width,
height,
m.as_ptr() as _,
stride,
)
};
if data.is_null() {
return Err(PixmanError::Internal);
}
Ok(data)
})??;
Ok(Self {
data,
width,
height,
memory,
clip: Cell::new(None),
})
}
pub fn with_clip<F: FnOnce()>(&self, clip: Rect, f: F) {
let region = Region::rect(clip.x1(), clip.y1(), clip.width(), clip.height());
unsafe {
pixman_image_set_clip_region32(self.data, &region);
}
let region = self.clip.replace(Some(region));
f();
unsafe {
let region = region
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null());
pixman_image_set_clip_region32(self.data, region);
}
self.clip.set(region);
}
pub fn fill(&self, r: u8, g: u8, b: u8, a: u8) -> Result<(), PixmanError> {
self.fill_rect(r, g, b, a, 0, 0, self.width as _, self.height as _)
}
#[allow(clippy::too_many_arguments)]
pub fn fill_rect(
&self,
r: u8,
g: u8,
b: u8,
a: u8,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
) -> Result<(), PixmanError> {
self.memory.access(|_| {
let bx = Box32 {
x1: x1.max(0),
y1: y1.max(0),
x2: x2.min(self.width as i32),
y2: y2.min(self.height as i32),
};
let color = Color {
red: (r as u16) << 8,
green: (g as u16) << 8,
blue: (b as u16) << 8,
alpha: (a as u16) << 8,
};
unsafe {
pixman_image_fill_boxes(OP_SRC.raw() as PixmanOp, self.data, &color, 1, &bx);
}
})?;
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn fill_inner_border(
&self,
r: u8,
g: u8,
b: u8,
a: u8,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
width: i32,
borders: Border,
) -> Result<(), PixmanError> {
self.memory.access(|_| {
let mut bx = [
Box32 { x1, y1, x2, y2: y1 },
Box32 { x1: x2, y1, x2, y2 },
Box32 { x1, y1, x2: x1, y2 },
Box32 { x1, y1: y2, x2, y2 },
];
if borders.contains(Border::TOP) {
bx[0].y2 += width;
}
if borders.contains(Border::RIGHT) {
bx[1].x1 -= width;
}
if borders.contains(Border::LEFT) {
bx[2].x2 += width;
}
if borders.contains(Border::BOTTOM) {
bx[3].y1 -= width;
}
for bx in &mut bx {
bx.x1 = bx.x1.max(0).min(self.width as i32);
bx.x2 = bx.x2.max(0).min(self.width as i32);
bx.y1 = bx.y1.max(0).min(self.height as i32);
bx.y2 = bx.y2.max(0).min(self.height as i32);
}
let color = Color {
red: (r as u16) << 8,
green: (g as u16) << 8,
blue: (b as u16) << 8,
alpha: (a as u16) << 8,
};
unsafe {
pixman_image_fill_boxes(
OP_SRC.raw() as PixmanOp,
self.data,
&color,
bx.len() as _,
bx.as_ptr(),
);
}
})?;
Ok(())
}
pub fn add_image<U>(&self, over: &Image<U>, x: i32, y: i32) -> Result<(), PixmanError>
where
U: PixmanMemory,
PixmanError: From<<U as PixmanMemory>::E>,
{
self.memory.access(|_| {
over.memory.access(|_| unsafe {
pixman_image_composite32(
OP_OVER.raw(),
over.data,
ptr::null_mut(),
self.data,
0,
0,
0,
0,
x,
y,
over.width as _,
over.height as _,
);
})
})??;
Ok(())
}
#[allow(dead_code)]
pub fn memory(&self) -> &T {
&self.memory
}
}
impl<T> Drop for Image<T> {
fn drop(&mut self) {
unsafe {
pixman_image_unref(self.data);
}
}
}

72
src/render/egl/context.rs Normal file
View file

@ -0,0 +1,72 @@
use crate::render::egl::display::EglDisplay;
use crate::render::egl::sys::{
eglDestroyContext, eglMakeCurrent, EGLContext, EGLSurface, EGL_FALSE, EGL_TRUE,
};
use crate::render::ext::GlExt;
use crate::render::gl::sys::{GLint, GLuint};
use crate::render::RenderError;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct EglContext {
pub dpy: Rc<EglDisplay>,
pub ext: GlExt,
pub ctx: EGLContext,
pub tex_prog: GLuint,
pub tex_tex: GLint,
pub tex_texcoord: GLint,
pub tex_pos: GLint,
}
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, RenderError>>(
&self,
f: F,
) -> Result<T, RenderError> {
unsafe {
if CURRENT == self.ctx {
return f();
}
self.with_current_slow(f)
}
}
#[cold]
unsafe fn with_current_slow<T, F: FnOnce() -> Result<T, RenderError>>(
&self,
f: F,
) -> Result<T, RenderError> {
if eglMakeCurrent(
self.dpy.dpy,
EGLSurface::none(),
EGLSurface::none(),
self.ctx,
) == EGL_FALSE
{
return Err(RenderError::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
}
}

99
src/render/egl/device.rs Normal file
View file

@ -0,0 +1,99 @@
use crate::format::{formats, Format};
use crate::render::egl::display::EglDisplay;
use crate::render::egl::sys::{
eglInitialize, EGLDeviceEXT, EGLDisplay, EGLint, EGL_PLATFORM_DEVICE_EXT, EGL_TRUE,
};
use crate::render::egl::PROCS;
use crate::render::ext::{get_display_ext, DeviceExt, DisplayExt};
use crate::render::RenderError;
use ahash::AHashMap;
use std::ffi::CStr;
use std::ptr;
use std::rc::Rc;
#[derive(Debug, Copy, Clone)]
pub struct EglDevice {
pub exts: DeviceExt,
pub dev: EGLDeviceEXT,
}
impl EglDevice {
pub fn query_string(&self, name: EGLint) -> Result<&'static CStr, RenderError> {
unsafe {
let res = PROCS.eglQueryDeviceStringEXT(self.dev, name);
if res.is_null() {
return Err(RenderError::DeviceQueryString);
}
Ok(CStr::from_ptr(res))
}
}
pub fn create_display(&self) -> Result<Rc<EglDisplay>, RenderError> {
unsafe {
let dpy = PROCS.eglGetPlatformDisplayEXT(
EGL_PLATFORM_DEVICE_EXT as _,
self.dev.0,
ptr::null(),
);
if dpy.is_none() {
return Err(RenderError::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(RenderError::Initialize);
}
dpy.exts = get_display_ext(dpy.dpy);
if !dpy.exts.intersects(DisplayExt::KHR_IMAGE_BASE) {
return Err(RenderError::ImageBase);
}
if !dpy
.exts
.intersects(DisplayExt::EXT_IMAGE_DMA_BUF_IMPORT_MODIFIERS)
{
return Err(RenderError::DmaBufImport);
}
if !dpy
.exts
.intersects(DisplayExt::KHR_NO_CONFIG_CONTEXT | DisplayExt::MESA_CONFIGLESS_CONTEXT)
{
return Err(RenderError::ConfiglessContext);
}
if !dpy.exts.intersects(DisplayExt::KHR_SURFACELESS_CONTEXT) {
return Err(RenderError::SurfacelessContext);
}
dpy.formats = query_formats(dpy.dpy)?;
Ok(Rc::new(dpy))
}
}
}
unsafe fn query_formats(dpy: EGLDisplay) -> Result<AHashMap<u32, &'static Format>, RenderError> {
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(RenderError::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(RenderError::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)
}

147
src/render/egl/display.rs Normal file
View file

@ -0,0 +1,147 @@
use crate::drm::dma::DmaBuf;
use crate::drm::INVALID_MODIFIER;
use crate::format::Format;
use crate::render::egl::context::EglContext;
use crate::render::egl::device::EglDevice;
use crate::render::egl::image::EglImage;
use crate::render::egl::sys::{
eglCreateContext, eglTerminate, EGLClientBuffer, EGLConfig, EGLContext, EGLDisplay, EGLint,
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_HEIGHT, EGL_IMAGE_PRESERVED_KHR, EGL_LINUX_DMA_BUF_EXT,
EGL_LINUX_DRM_FOURCC_EXT, EGL_NONE, EGL_TRUE, EGL_WIDTH,
};
use crate::render::egl::PROCS;
use crate::render::ext::{get_gl_ext, DisplayExt, GlExt};
use crate::render::RenderError;
use ahash::AHashMap;
use std::rc::Rc;
#[derive(Debug, Clone)]
pub struct EglDisplay {
pub exts: DisplayExt,
pub formats: AHashMap<u32, &'static Format>,
pub dev: EglDevice,
pub dpy: EGLDisplay,
}
impl EglDisplay {
pub fn create_context(self: &Rc<Self>) -> Result<Rc<EglContext>, RenderError> {
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(RenderError::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>, RenderError> {
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(RenderError::CreateImage);
}
Ok(Rc::new(EglImage {
dpy: self.clone(),
img,
width: buf.width,
height: buf.height,
}))
}
}
impl Drop for EglDisplay {
fn drop(&mut self) {
unsafe {
if eglTerminate(self.dpy) != EGL_TRUE {
log::warn!("`eglTerminate` failed");
}
}
}
}

21
src/render/egl/image.rs Normal file
View file

@ -0,0 +1,21 @@
use crate::render::egl::display::EglDisplay;
use crate::render::egl::sys::{EGLImageKHR, EGL_FALSE};
use crate::render::egl::PROCS;
use std::rc::Rc;
pub struct EglImage {
pub dpy: Rc<EglDisplay>,
pub 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");
}
}
}
}

171
src/render/egl/mod.rs Normal file
View file

@ -0,0 +1,171 @@
use crate::drm::drm::Drm;
use crate::render::egl::device::EglDevice;
use crate::render::egl::sys::{
eglBindAPI, EGLAttrib, EGLLabelKHR, EGLenum, EGLint, EGL_DEBUG_MSG_CRITICAL_KHR,
EGL_DEBUG_MSG_ERROR_KHR, EGL_DEBUG_MSG_INFO_KHR, EGL_DEBUG_MSG_WARN_KHR,
EGL_DRM_DEVICE_FILE_EXT, EGL_NONE, EGL_OPENGL_ES_API, EGL_TRUE,
};
use crate::render::ext::{get_client_ext, get_device_ext, ClientExt, DeviceExt};
use crate::render::proc::ExtProc;
use crate::render::RenderError;
use bstr::ByteSlice;
use log::Level;
use once_cell::sync::Lazy;
use std::ffi::CStr;
use std::ptr;
use sys::{
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_NOT_INITIALIZED, EGL_SUCCESS,
};
use uapi::c;
pub mod context;
pub mod device;
pub mod display;
pub mod image;
pub mod sys;
pub(super) static PROCS: Lazy<ExtProc> = Lazy::new(|| ExtProc::load());
pub(super) static EXTS: Lazy<ClientExt> = Lazy::new(|| get_client_ext());
pub fn init() -> Result<(), RenderError> {
if !EXTS.contains(ClientExt::EXT_PLATFORM_BASE) {
return Err(RenderError::ExtPlatformBase);
}
if !EXTS.device_query() {
return Err(RenderError::DeviceQuery);
}
if !EXTS.device_enumeration() {
return Err(RenderError::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(RenderError::BindFailed);
}
Ok(())
}
pub fn find_drm_device(drm: &Drm) -> Result<Option<EglDevice>, RenderError> {
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>, RenderError> {
if !EXTS.device_enumeration() {
return Err(RenderError::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(RenderError::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(RenderError::QueryDevices);
}
devices.set_len(num_devices as usize);
Ok(devices
.into_iter()
.map(|d| EglDevice {
exts: get_device_ext(d),
dev: d,
})
.collect())
}
}
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,
}
}

102
src/render/egl/sys.rs Normal file
View file

@ -0,0 +1,102 @@
use uapi::c;
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;
#[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;
}

View file

@ -1,7 +1,6 @@
use crate::gles2::egl::PROCS;
use crate::gles2::sys::{
eglQueryString, glGetString, EGLDeviceEXT, EGLDisplay, EGL_EXTENSIONS, GL_EXTENSIONS,
};
use crate::render::egl::sys::{eglQueryString, EGLDeviceEXT, EGLDisplay, EGL_EXTENSIONS};
use crate::render::egl::PROCS;
use crate::render::gl::sys::{glGetString, GL_EXTENSIONS};
use ahash::AHashSet;
use bstr::ByteSlice;
use std::ffi::CStr;

View file

@ -0,0 +1,55 @@
use crate::rect::Rect;
use crate::render::egl::context::EglContext;
use crate::render::gl::render_buffer::GlRenderBuffer;
use crate::render::gl::sys::{glDeleteFramebuffers, GLuint};
use crate::render::gl::texture::GlTexture;
use crate::render::sys::{glDisable, glEnable, glScissor, GL_SCISSOR_TEST};
use crate::utils::ptr_ext::PtrExt;
use std::ptr;
use std::rc::Rc;
pub struct GlFrameBuffer {
pub _rb: Option<Rc<GlRenderBuffer>>,
pub _tex: Option<Rc<GlTexture>>,
pub ctx: Rc<EglContext>,
pub width: i32,
pub height: i32,
pub fbo: GLuint,
}
impl Drop for GlFrameBuffer {
fn drop(&mut self) {
let _ = self.ctx.with_current(|| {
unsafe {
glDeleteFramebuffers(1, &self.fbo);
}
Ok(())
});
}
}
pub unsafe fn with_scissor<T, F: FnOnce() -> T>(scissor: &Rect, f: F) -> T {
#[thread_local]
static mut SCISSOR: *const Rect = ptr::null();
let prev = SCISSOR;
if prev.is_null() {
glEnable(GL_SCISSOR_TEST);
}
glScissor(
scissor.x1(),
scissor.y1(),
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.y1(), prev.width(), prev.height());
}
SCISSOR = prev;
res
}

6
src/render/gl/mod.rs Normal file
View file

@ -0,0 +1,6 @@
pub mod frame_buffer;
pub mod program;
pub mod render_buffer;
pub mod shader;
pub mod sys;
pub mod texture;

66
src/render/gl/program.rs Normal file
View file

@ -0,0 +1,66 @@
use crate::render::egl::context::EglContext;
use crate::render::gl::shader::GlShader;
use crate::render::gl::sys::{
glAttachShader, glCreateProgram, glDeleteProgram, glDetachShader, glGetAttribLocation,
glGetProgramiv, glGetUniformLocation, glLinkProgram, GLint, GLuint, GL_FALSE, GL_LINK_STATUS,
};
use crate::render::gl::sys::{GL_FRAGMENT_SHADER, GL_VERTEX_SHADER};
use crate::render::RenderError;
use std::rc::Rc;
use uapi::Ustr;
pub struct GlProgram {
pub _ctx: Rc<EglContext>,
pub prog: GLuint,
}
impl GlProgram {
pub unsafe fn from_shaders(
ctx: &Rc<EglContext>,
vert: &str,
frag: &str,
) -> Result<Self, RenderError> {
let vert = GlShader::compile(ctx, GL_VERTEX_SHADER, vert)?;
let frag = GlShader::compile(ctx, GL_FRAGMENT_SHADER, frag)?;
Self::link(&vert, &frag)
}
pub unsafe fn link(vert: &GlShader, frag: &GlShader) -> Result<Self, RenderError> {
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(RenderError::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 _)
}
}
impl Drop for GlProgram {
fn drop(&mut self) {
unsafe {
let _ = self._ctx.with_current(|| {
glDeleteProgram(self.prog);
Ok(())
});
}
}
}

View file

@ -0,0 +1,72 @@
use crate::render::egl::context::EglContext;
use crate::render::egl::image::EglImage;
use crate::render::egl::PROCS;
use crate::render::gl::frame_buffer::GlFrameBuffer;
use crate::render::gl::sys::{
glBindFramebuffer, glBindRenderbuffer, glCheckFramebufferStatus, glDeleteRenderbuffers,
glFramebufferRenderbuffer, glGenFramebuffers, glGenRenderbuffers, GLeglImageOES, GLuint,
GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER, GL_FRAMEBUFFER_COMPLETE, GL_RENDERBUFFER,
};
use crate::render::RenderError;
use std::rc::Rc;
pub struct GlRenderBuffer {
pub img: Rc<EglImage>,
pub ctx: Rc<EglContext>,
rbo: GLuint,
}
impl GlRenderBuffer {
pub unsafe fn from_image(
img: &Rc<EglImage>,
ctx: &Rc<EglContext>,
) -> Result<Rc<GlRenderBuffer>, RenderError> {
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 unsafe fn create_framebuffer(self: &Rc<Self>) -> Result<GlFrameBuffer, RenderError> {
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(RenderError::CreateFramebuffer);
}
Ok(fb)
}
}
impl Drop for GlRenderBuffer {
fn drop(&mut self) {
let _ = self.ctx.with_current(|| {
unsafe {
glDeleteRenderbuffers(1, &self.rbo);
}
Ok(())
});
}
}

45
src/render/gl/shader.rs Normal file
View file

@ -0,0 +1,45 @@
use crate::render::egl::context::EglContext;
use crate::render::gl::sys::{
glCompileShader, glCreateShader, glDeleteShader, glGetShaderiv, glShaderSource, GLenum, GLuint,
GL_COMPILE_STATUS, GL_FALSE,
};
use crate::render::RenderError;
use std::rc::Rc;
pub struct GlShader {
pub ctx: Rc<EglContext>,
pub shader: GLuint,
}
impl GlShader {
pub unsafe fn compile(
ctx: &Rc<EglContext>,
ty: GLenum,
src: &str,
) -> Result<Self, RenderError> {
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(RenderError::ShaderCompileFailed);
}
Ok(res)
}
}
impl Drop for GlShader {
fn drop(&mut self) {
let _ = self.ctx.with_current(|| unsafe {
glDeleteShader(self.shader);
Ok(())
});
}
}

View file

@ -1,147 +1,43 @@
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;
pub type GLchar = c::c_char;
pub type GLenum = c::c_uint;
pub type GLfloat = f32;
pub type GLint = c::c_int;
pub type GLsizei = c::c_int;
pub type GLubyte = u8;
pub type GLuint = c::c_uint;
egl_transparent!(GLeglImageOES);
pub const GL_EXTENSIONS: GLenum = 0x1F03;
pub const GL_RENDERBUFFER: GLenum = 0x8D41;
pub const GL_FRAMEBUFFER: GLenum = 0x8D40;
pub const GL_BGRA_EXT: GLint = 0x80E1;
pub const GL_CLAMP_TO_EDGE: GLint = 0x812F;
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_COMPILE_STATUS: GLenum = 0x8B81;
pub const GL_EXTENSIONS: GLenum = 0x1F03;
pub const GL_FALSE: GLboolean = 0;
pub const GL_FLOAT: GLenum = 0x1406;
pub const GL_FRAGMENT_SHADER: GLenum = 0x8B30;
pub const GL_FRAMEBUFFER_COMPLETE: GLenum = 0x8CD5;
pub const GL_FRAMEBUFFER: GLenum = 0x8D40;
pub const GL_LINEAR: GLint = 0x2601;
pub const GL_LINK_STATUS: GLenum = 0x8B82;
pub const GL_RENDERBUFFER: GLenum = 0x8D41;
pub const GL_SCISSOR_TEST: GLenum = 0x0C11;
pub const GL_TEXTURE0: GLenum = 0x84C0;
pub const GL_TEXTURE_2D: GLenum = 0x0DE1;
pub const GL_TEXTURE_MAG_FILTER: GLenum = 0x2800;
pub const GL_TEXTURE_MIN_FILTER: GLenum = 0x2801;
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;
pub const GL_TRIANGLES: GLenum = 0x0004;
pub const GL_UNPACK_ROW_LENGTH_EXT: GLenum = 0x0CF2;
pub const GL_UNSIGNED_BYTE: GLint = 0x1401;
pub const GL_VERTEX_SHADER: GLenum = 0x8B31;
#[link(name = "GLESv2")]
extern "C" {
@ -217,6 +113,7 @@ extern "C" {
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 glUniform4f(location: GLint, v0: GLfloat, v1: GLfloat, v2: GLfloat, v3: GLfloat);
pub fn glVertexAttribPointer(
index: GLuint,
size: GLint,
@ -232,25 +129,3 @@ extern "C" {
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;
}

138
src/render/gl/texture.rs Normal file
View file

@ -0,0 +1,138 @@
use crate::format::Format;
use crate::render::egl::context::EglContext;
use crate::render::gl::frame_buffer::GlFrameBuffer;
use crate::render::gl::sys::{
glBindFramebuffer, glBindTexture, glCheckFramebufferStatus, glDeleteTextures,
glFramebufferTexture2D, glGenFramebuffers, glGenTextures, glPixelStorei, glTexImage2D,
glTexParameteri, GLint, GLuint, GL_CLAMP_TO_EDGE, GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER,
GL_FRAMEBUFFER_COMPLETE, GL_LINEAR, 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::render::RenderError;
use std::cell::Cell;
use std::ptr;
use std::rc::Rc;
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>, RenderError> {
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>, RenderError> {
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(RenderError::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<GlTexture, RenderError> {
if (stride * height) as usize > data.len() {
return Err(RenderError::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(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(())
});
}
}
}

View file

@ -1,335 +0,0 @@
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)
}
}
}

View file

@ -1,29 +1,92 @@
use crate::ifs::wl_buffer::WlBuffer;
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
use crate::ifs::wl_surface::WlSurface;
use crate::tree::ContainerNode;
use crate::tree::{FloatNode, OutputNode, WorkspaceNode};
macro_rules! egl_transparent {
($name:ident) => {
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct $name(pub *mut u8);
pub mod gles;
pub mod pixman;
impl $name {
#[allow(dead_code)]
pub const fn none() -> Self {
Self(std::ptr::null_mut())
}
bitflags::bitflags! {
pub struct Border: u32 {
const NONE = 0b0000;
const LEFT = 0b0001;
const TOP = 0b0010;
const RIGHT = 0b0100;
const BOTTOM = 0b1000;
const ALL = 0b1111;
}
#[allow(dead_code)]
pub fn is_none(self) -> bool {
self.0.is_null()
}
}
};
}
pub trait Renderer {
fn render_output(&mut self, output: &OutputNode);
fn render_workspace(&mut self, workspace: &WorkspaceNode);
fn render_container(&mut self, container: &ContainerNode, x: i32, y: i32);
fn render_toplevel(&mut self, toplevel: &XdgToplevel, x: i32, y: i32);
fn render_surface(&mut self, surface: &WlSurface, x: i32, y: i32);
fn render_buffer(&mut self, buffer: &WlBuffer, x: i32, y: i32);
fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32);
use crate::drm::drm::DrmError;
pub use renderer::*;
use thiserror::Error;
mod egl;
mod ext;
mod gl;
mod proc;
mod renderer;
pub mod sys {
pub use super::egl::sys::*;
pub use super::gl::sys::*;
}
pub fn init() -> Result<(), RenderError> {
egl::init()
}
#[derive(Debug, Error)]
pub enum RenderError {
#[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),
#[error("Could not find the requested DRM device")]
UnknownDrmDevice,
#[error("The GLES driver does not support the XRGB8888 format")]
XRGB888,
}

View file

@ -1,223 +0,0 @@
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 std::ops::Deref;
use std::rc::Rc;
pub struct PixmanRenderer<'a> {
image: &'a Image<Rc<ServerMem>>,
}
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<'a> PixmanRenderer<'a> {
pub fn new(image: &'a Image<Rc<ServerMem>>) -> Self {
Self { image }
}
}
impl Renderer for PixmanRenderer<'_> {
fn render_output(&mut self, output: &OutputNode) {
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;
}
self.image.with_clip(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,
);
}
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(),
);
});
}
}
}
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) {
if let Err(e) = self.image.add_image(&buffer.image, x, y) {
let client = &buffer.client;
log::error!("Could not access client {} memory: {:#}", client.id, e);
if let Ok(d) = client.display() {
client.fatal_event(
d.implementation_error(format!("Could not access memory: {:#}", e)),
);
} else {
client.state.clients.kill(client.id);
}
}
}
fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32) {
if let Some(child) = floating.child.get() {
child.render(self, x, y)
}
}
}

View file

@ -0,0 +1,104 @@
use crate::drm::dma::DmaBuf;
use crate::drm::drm::Drm;
use crate::format::{Format, XRGB8888};
use crate::render::egl::context::EglContext;
use crate::render::egl::find_drm_device;
use crate::render::gl::program::GlProgram;
use crate::render::gl::render_buffer::GlRenderBuffer;
use crate::render::gl::sys::GLint;
use crate::render::gl::texture::GlTexture;
use crate::render::renderer::framebuffer::Framebuffer;
use crate::render::renderer::RENDERDOC;
use crate::render::{RenderError, Texture};
use renderdoc::{RenderDoc, V100};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use uapi::ustr;
pub struct RenderContext {
pub(super) ctx: Rc<EglContext>,
pub(super) renderdoc: Option<RefCell<RenderDoc<V100>>>,
pub(super) tex_prog: GlProgram,
pub(super) tex_prog_pos: GLint,
pub(super) tex_prog_texcoord: GLint,
pub(super) tex_prog_tex: GLint,
pub(super) fill_prog: GlProgram,
pub(super) fill_prog_pos: GLint,
pub(super) fill_prog_color: GLint,
}
impl RenderContext {
pub fn from_drm_device(drm: &Drm) -> Result<Self, RenderError> {
let egl_dev = match find_drm_device(&drm)? {
Some(d) => d,
None => return Err(RenderError::UnknownDrmDevice),
};
let dpy = egl_dev.create_display()?;
if !dpy.formats.contains_key(&XRGB8888.drm) {
return Err(RenderError::XRGB888);
}
let ctx = dpy.create_context()?;
ctx.with_current(|| unsafe { Self::new(&ctx) })
}
unsafe fn new(ctx: &Rc<EglContext>) -> Result<Self, RenderError> {
let tex_prog = GlProgram::from_shaders(
ctx,
include_str!("../shaders/tex.vert.glsl"),
include_str!("../shaders/tex.frag.glsl"),
)?;
let fill_prog = GlProgram::from_shaders(
ctx,
include_str!("../shaders/fill.vert.glsl"),
include_str!("../shaders/fill.frag.glsl"),
)?;
Ok(Self {
ctx: ctx.clone(),
tex_prog_pos: tex_prog.get_attrib_location(ustr!("pos")),
tex_prog_texcoord: tex_prog.get_attrib_location(ustr!("texcoord")),
tex_prog_tex: tex_prog.get_uniform_location(ustr!("tex")),
tex_prog,
fill_prog_pos: fill_prog.get_attrib_location(ustr!("pos")),
fill_prog_color: fill_prog.get_uniform_location(ustr!("color")),
fill_prog,
renderdoc: if RENDERDOC {
Some(RefCell::new(RenderDoc::new().unwrap()))
} else {
None
},
})
}
pub fn dmabuf_fb(self: &Rc<Self>, buf: &DmaBuf) -> Result<Rc<Framebuffer>, RenderError> {
self.ctx.with_current(|| unsafe {
let img = self.ctx.dpy.import_dmabuf(buf)?;
let rb = GlRenderBuffer::from_image(&img, &self.ctx)?;
let fb = rb.create_framebuffer()?;
Ok(Rc::new(Framebuffer {
ctx: self.clone(),
gl: fb,
}))
})
}
pub fn shmem_texture(
self: &Rc<Self>,
data: &[Cell<u8>],
format: &'static Format,
width: i32,
height: i32,
stride: i32,
) -> Result<Rc<Texture>, RenderError> {
let gl = GlTexture::import_texture(&self.ctx, data, format, width, height, stride)?;
Ok(Rc::new(Texture {
ctx: self.clone(),
gl,
}))
}
}

View file

@ -0,0 +1,40 @@
use crate::render::gl::frame_buffer::GlFrameBuffer;
use crate::render::gl::sys::{
glBindFramebuffer, glClear, glClearColor, glViewport, GL_COLOR_BUFFER_BIT, GL_FRAMEBUFFER,
};
use crate::render::renderer::context::RenderContext;
use crate::render::renderer::renderer::Renderer;
use crate::tree::Node;
use std::ptr;
use std::rc::Rc;
pub struct Framebuffer {
pub(super) ctx: Rc<RenderContext>,
pub(super) gl: GlFrameBuffer,
}
impl Framebuffer {
pub fn render(&self, node: &dyn Node) {
let _ = self.ctx.ctx.with_current(|| {
if let Some(rd) = &self.ctx.renderdoc {
rd.borrow_mut()
.start_frame_capture(ptr::null(), ptr::null());
}
unsafe {
glBindFramebuffer(GL_FRAMEBUFFER, self.gl.fbo);
glViewport(0, 0, self.gl.width, self.gl.height);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
}
let mut renderer = Renderer {
renderer: &self.ctx,
image: &self.gl,
};
node.render(&mut renderer, 0, 0);
if let Some(rd) = &self.ctx.renderdoc {
rd.borrow_mut().end_frame_capture(ptr::null(), ptr::null());
}
Ok(())
});
}
}

View file

@ -0,0 +1,11 @@
pub use context::*;
pub use framebuffer::*;
pub use renderer::*;
pub use texture::*;
mod context;
mod framebuffer;
mod renderer;
mod texture;
pub const RENDERDOC: bool = true;

View file

@ -0,0 +1,280 @@
use crate::ifs::wl_buffer::WlBuffer;
use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel;
use crate::ifs::wl_surface::WlSurface;
use crate::rect::Rect;
use crate::render::gl::frame_buffer::{with_scissor, GlFrameBuffer};
use crate::render::gl::sys::{
glActiveTexture, glBindTexture, glDisableVertexAttribArray, glDrawArrays,
glEnableVertexAttribArray, glTexParameteri, glUniform1i, glUniform4f, glUseProgram,
glVertexAttribPointer, GL_FALSE, GL_FLOAT, GL_LINEAR, GL_TEXTURE0, GL_TEXTURE_2D,
GL_TEXTURE_MIN_FILTER, GL_TRIANGLES, GL_TRIANGLE_STRIP,
};
use crate::render::renderer::context::RenderContext;
use crate::tree::{
ContainerFocus, ContainerNode, ContainerSplit, FloatNode, OutputNode, WorkspaceNode,
CONTAINER_BORDER, CONTAINER_TITLE_HEIGHT,
};
use std::ops::Deref;
use std::rc::Rc;
use std::slice;
const NON_COLOR: (f32, f32, f32) = (0.2, 0.2, 0.2);
const CHILD_COLOR: (f32, f32, f32) = (0.8, 0.8, 0.8);
const YES_COLOR: (f32, f32, f32) = (0.0, 0.0, 1.0);
fn focus_color(focus: ContainerFocus) -> (f32, f32, f32) {
match focus {
ContainerFocus::None => NON_COLOR,
ContainerFocus::Child => CHILD_COLOR,
ContainerFocus::Yes => YES_COLOR,
}
}
pub struct Renderer<'a> {
pub(super) renderer: &'a RenderContext,
pub(super) image: &'a GlFrameBuffer,
}
impl Renderer<'_> {
pub fn render_output(&mut self, output: &OutputNode, x: i32, y: i32) {
if let Some(ws) = output.workspace.get() {
self.render_workspace(&ws, x, y);
}
}
pub fn render_workspace(&mut self, workspace: &WorkspaceNode, x: i32, y: i32) {
if let Some(node) = workspace.container.get() {
self.render_container(&node, x, y)
}
}
fn x_to_f(&self, x: i32) -> f32 {
2.0 * (x as f32 / self.image.width as f32) - 1.0
}
fn y_to_f(&self, y: i32) -> f32 {
2.0 * (y as f32 / self.image.height as f32) - 1.0
}
fn fill_boxes(&self, boxes: &[Rect], r: f32, g: f32, b: f32, a: f32) {
let mut pos = Vec::with_capacity(boxes.len() * 12);
for bx in boxes {
let x1 = self.x_to_f(bx.x1());
let y1 = self.y_to_f(bx.y1());
let x2 = self.x_to_f(bx.x2());
let y2 = self.y_to_f(bx.y2());
pos.extend_from_slice(&[
// triangle 1
x2, y1, // top right
x1, y1, // top left
x1, y2, // bottom left
// triangle 2
x2, y1, // top right
x1, y2, // bottom left
x2, y2, // bottom right
]);
}
unsafe {
glUseProgram(self.renderer.fill_prog.prog);
glUniform4f(self.renderer.fill_prog_color, r, g, b, a);
glVertexAttribPointer(
self.renderer.fill_prog_pos as _,
2,
GL_FLOAT,
GL_FALSE,
0,
pos.as_ptr() as _,
);
glEnableVertexAttribArray(self.renderer.fill_prog_pos as _);
glDrawArrays(GL_TRIANGLES, 0, (boxes.len() * 6) as _);
glDisableVertexAttribArray(self.renderer.fill_prog_pos as _);
}
}
pub 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();
let title_rect =
Rect::new_sized(x, y, container.width.get(), CONTAINER_TITLE_HEIGHT).unwrap();
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;
let (r, g, b) = focus_color(ContainerFocus::None);
self.fill_boxes(slice::from_ref(&title_rect), r, g, b, 1.0);
for child in container.children.iter() {
let focus = child.focus.get();
let (r, g, b) = focus_color(focus);
let mut width = space_per_child;
if rem > 0 {
rem -= 1;
width += 1;
}
if focus != ContainerFocus::None {
let rect = Rect::new_sized(pos, y, width, CONTAINER_TITLE_HEIGHT).unwrap();
self.fill_boxes(slice::from_ref(&rect), r, g, b, 1.0);
}
pos += width as i32;
}
unsafe {
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();
let mut rects = Vec::with_capacity(num_children);
rects.push(title_rect);
for (i, child) in container.children.iter().enumerate() {
let body = child.body.get();
if i + 1 < num_children {
let rect = if split == ContainerSplit::Horizontal {
Rect::new_sized(
x + body.x2(),
y + body.y1() - CONTAINER_TITLE_HEIGHT,
CONTAINER_BORDER,
container.height.get(),
)
.unwrap()
} else {
Rect::new_sized(
x + body.x1(),
y + body.y2(),
container.width.get(),
CONTAINER_BORDER,
)
.unwrap()
};
rects.push(rect);
}
}
let (r, g, b) = focus_color(ContainerFocus::None);
self.fill_boxes(&rects, r, g, b, 1.0);
for child in container.children.iter() {
let body = child.body.get();
if body.x1() >= cwidth || body.y1() >= cheight {
break;
}
unsafe {
with_scissor(&body, || {
let content = child.content.get();
child.node.render(self, x + content.x1(), y + content.y1());
});
}
}
}
}
pub 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);
}
pub 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);
}
}
pub fn render_buffer(&mut self, buffer: &WlBuffer, x: i32, y: i32) {
let texture = match buffer.texture.get() {
Some(t) => t,
_ => return,
};
assert!(Rc::ptr_eq(&self.renderer.ctx, &texture.ctx.ctx));
unsafe {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture.gl.tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glUseProgram(self.renderer.tex_prog.prog);
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.gl.width) as f32 / f_width) - 1.0;
let y2 = 2.0 * ((y + texture.gl.height) as f32 / f_height) - 1.0;
let pos: [f32; 8] = [
x2, y1, // top right
x1, y1, // top left
x2, y2, // bottom right
x1, y2, // bottom left
];
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);
}
}
pub fn render_floating(&mut self, floating: &FloatNode, x: i32, y: i32) {
if let Some(child) = floating.child.get() {
child.render(self, x, y)
}
}
}

View file

@ -0,0 +1,8 @@
use crate::render::gl::texture::GlTexture;
use crate::render::renderer::context::RenderContext;
use std::rc::Rc;
pub struct Texture {
pub(super) ctx: Rc<RenderContext>,
pub(super) gl: GlTexture,
}

View file

@ -0,0 +1,6 @@
precision mediump float;
uniform vec4 color;
void main() {
gl_FragColor = color;
}

View file

@ -0,0 +1,5 @@
attribute vec2 pos;
void main() {
gl_Position = vec4(pos, 0.0, 1.0);
}

View file

View file

@ -1,7 +1,5 @@
use crate::pixman::PixmanMemory;
use std::cell::Cell;
use std::ptr;
use std::rc::Rc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;
use thiserror::Error;
@ -80,14 +78,3 @@ impl Drop for ServerMem {
}
}
}
unsafe impl PixmanMemory for Rc<ServerMem> {
type E = !;
fn access<T, F>(&self, f: F) -> Result<T, Self::E>
where
F: FnOnce(&[Cell<u8>]) -> T,
{
Ok(ServerMem::access(self, f))
}
}

View file

@ -1,12 +1,12 @@
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::globals::{AddGlobal, Globals};
use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_seat::WlSeatGlobal;
use crate::ifs::wl_surface::NoneSurfaceExt;
use crate::render::RenderContext;
use crate::tree::{DisplayNode, NodeIds};
use crate::utils::asyncevent::AsyncEvent;
use crate::utils::clonecell::CloneCell;
@ -22,7 +22,7 @@ use std::rc::Rc;
pub struct State {
pub eng: Rc<AsyncEngine>,
pub el: Rc<EventLoop>,
pub egl: CloneCell<Option<Rc<EglContext>>>,
pub render_ctx: CloneCell<Option<Rc<RenderContext>>>,
pub wheel: Rc<Wheel>,
pub clients: Clients,
pub next_name: NumCell<u32>,

View file

@ -1,5 +1,5 @@
use crate::rect::Rect;
use crate::render::{Border, Renderer};
use crate::render::Renderer;
use crate::tree::{FoundNode, Node, NodeId, WorkspaceNode};
use crate::utils::clonecell::CloneCell;
use crate::utils::linkedlist::{LinkedList, LinkedNode, NodeRef};
@ -39,7 +39,6 @@ pub struct ContainerNode {
pub height: Cell<i32>,
pub content_width: Cell<i32>,
pub content_height: Cell<i32>,
pub borders: Cell<Border>,
num_children: NumCell<usize>,
pub children: LinkedList<ContainerChild>,
child_nodes: RefCell<AHashMap<NodeId, LinkedNode<ContainerChild>>>,
@ -59,11 +58,13 @@ impl ContainerChild {
let body = self.body.get();
let width = content.width();
let height = content.height();
let x1 = body.x1() + (body.width() - width) / 2;
let y1 = body.y1() + (body.height() - height) / 2;
// let x1 = body.x1() + (body.width() - width) / 2;
// let y1 = body.y1() + (body.height() - height) / 2;
let x1 = body.x1();
let y1 = body.y1();
content = Rect::new_sized(x1, y1, width, height).unwrap();
log::debug!("body: {:?}", body);
log::debug!("content: {:?}", content);
// log::debug!("body: {:?}", body);
// log::debug!("content: {:?}", content);
self.content.set(content);
}
}
@ -93,7 +94,6 @@ impl ContainerNode {
height: Cell::new(0),
content_width: Cell::new(0),
content_height: Cell::new(0),
borders: Cell::new(Border::NONE),
num_children: NumCell::new(1),
children,
child_nodes: RefCell::new(child_nodes),
@ -145,20 +145,8 @@ impl ContainerNode {
}),
);
}
match self.split.get() {
ContainerSplit::Horizontal => {
let new_content_size = self.content_width.get().saturating_sub(CONTAINER_BORDER);
self.content_width.set(new_content_size);
}
ContainerSplit::Vertical => {
let new_content_size = self
.content_height
.get()
.saturating_sub(CONTAINER_BORDER + CONTAINER_TITLE_HEIGHT);
self.content_height.set(new_content_size);
}
}
let num_children = self.num_children.fetch_add(1) + 1;
self.update_content_size();
let new_child_factor = 1.0 / num_children as f64;
let mut sum_factors = 0.0;
for child in self.children.iter() {
@ -198,7 +186,7 @@ impl ContainerNode {
child.body.set(body);
pos += body_size + CONTAINER_BORDER;
if split == ContainerSplit::Vertical {
pos += body_size + CONTAINER_BORDER + CONTAINER_TITLE_HEIGHT;
pos += CONTAINER_TITLE_HEIGHT;
}
}
if remaining_content_size > 0 {
@ -242,6 +230,29 @@ impl ContainerNode {
child.position_content();
}
}
fn update_content_size(&self) {
let nc = self.num_children.get();
match self.split.get() {
ContainerSplit::Horizontal => {
let new_content_size = self
.width
.get()
.saturating_sub((nc - 1) as i32 * CONTAINER_BORDER);
self.content_width.set(new_content_size);
self.content_height
.set(self.height.get().saturating_sub(CONTAINER_TITLE_HEIGHT));
}
ContainerSplit::Vertical => {
let new_content_size = self.height.get().saturating_sub(
CONTAINER_TITLE_HEIGHT
+ (nc - 1) as i32 * (CONTAINER_BORDER + CONTAINER_TITLE_HEIGHT),
);
self.content_height.set(new_content_size);
self.content_width.set(self.width.get());
}
}
}
}
impl Node for ContainerNode {
@ -295,6 +306,7 @@ impl Node for ContainerNode {
self.parent.get().remove_child(self);
return;
}
self.update_content_size();
let rem = 1.0 - node.factor.get();
let mut sum = 0.0;
if rem <= 0.0 {
@ -323,7 +335,7 @@ impl Node for ContainerNode {
}
}
fn render(&self, renderer: &mut dyn Renderer, x: i32, y: i32) {
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_container(self, x, y);
}
@ -338,22 +350,7 @@ impl Node for ContainerNode {
fn change_size(self: Rc<Self>, width: i32, height: i32) {
self.width.set(width);
self.height.set(height);
let num_children = self.num_children.get();
match self.split.get() {
ContainerSplit::Horizontal => {
self.content_width
.set(width.saturating_sub((num_children - 1) as i32 * CONTAINER_BORDER));
self.content_height
.set(height.saturating_sub(CONTAINER_TITLE_HEIGHT));
}
ContainerSplit::Vertical => {
self.content_width.set(width);
self.content_height.set(height.saturating_sub(
(num_children - 1) as i32 * CONTAINER_BORDER
+ num_children as i32 * CONTAINER_TITLE_HEIGHT,
));
}
}
self.update_content_size();
self.apply_factors(1.0);
}
}

View file

@ -107,7 +107,7 @@ pub trait Node {
let _ = y;
}
fn render(&self, renderer: &mut dyn Renderer, x: i32, y: i32) {
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
let _ = renderer;
let _ = x;
let _ = y;
@ -229,8 +229,8 @@ impl Node for OutputNode {
}
}
fn render(&self, renderer: &mut dyn Renderer, _x: i32, _y: i32) {
renderer.render_output(self);
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_output(self, x, y);
}
fn remove_child(&self, _child: &dyn Node) {
@ -277,7 +277,7 @@ impl Node for FloatNode {
self.workspace_link.set(None);
}
fn render(&self, renderer: &mut dyn Renderer, x: i32, y: i32) {
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_floating(self, x, y)
}

View file

@ -47,8 +47,8 @@ impl Node for WorkspaceNode {
}
}
fn render(&self, renderer: &mut dyn Renderer, _x: i32, _y: i32) {
renderer.render_workspace(self);
fn render(&self, renderer: &mut Renderer, x: i32, y: i32) {
renderer.render_workspace(self, x, y);
}
fn get_workspace(self: Rc<Self>) -> Option<Rc<WorkspaceNode>> {