Fixup anvil for winit rendering
- Since a lot of functionality is now in smithay's rendering module we can get rid of shm_load, shaders and the glium dependency. - glium_drawer becomes drawing and only features some code to get from surfaces to textures for any(!) renderer. (Should probably moved into smithay at some point as well.) - buffer_utils is only necessary to query the buffer size anymore. - disable egl-buffer support temporarily
This commit is contained in:
parent
3a2e4ddf61
commit
fb30c830d7
|
@ -8,18 +8,17 @@ edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.2.1"
|
bitflags = "1.2.1"
|
||||||
glium = { version = "0.29.0", default-features = false }
|
|
||||||
input = { version = "0.5.0", features = ["udev"], optional = true }
|
input = { version = "0.5.0", features = ["udev"], optional = true }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
slog = { version = "2.1.1" }
|
slog = { version = "2.1.1" }
|
||||||
slog-term = "2.3"
|
slog-term = "2.8"
|
||||||
slog-async = "2.2"
|
slog-async = "2.2"
|
||||||
xkbcommon = "0.4.0"
|
xkbcommon = "0.4.0"
|
||||||
|
|
||||||
[dependencies.smithay]
|
[dependencies.smithay]
|
||||||
path = ".."
|
path = ".."
|
||||||
default-features = false
|
default-features = false
|
||||||
features = [ "renderer_glium", "backend_egl", "wayland_frontend" ]
|
features = [ "renderer_gl", "backend_egl", "wayland_frontend" ]
|
||||||
|
|
||||||
[dependencies.x11rb]
|
[dependencies.x11rb]
|
||||||
optional = true
|
optional = true
|
||||||
|
@ -34,7 +33,7 @@ gl_generator = "0.14"
|
||||||
default = [ "winit", "egl", "udev", "logind", "xwayland" ]
|
default = [ "winit", "egl", "udev", "logind", "xwayland" ]
|
||||||
egl = [ "smithay/use_system_lib" ]
|
egl = [ "smithay/use_system_lib" ]
|
||||||
winit = [ "smithay/backend_winit" ]
|
winit = [ "smithay/backend_winit" ]
|
||||||
udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm_atomic", "smithay/backend_drm_legacy", "smithay/backend_drm_gbm", "smithay/backend_drm_eglstream", "smithay/backend_drm_egl", "smithay/backend_session", "input" ]
|
udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm", "smithay/backend_gbm", "smithay/backend_egl", "smithay/backend_session", "input" ]
|
||||||
logind = [ "smithay/backend_session_logind" ]
|
logind = [ "smithay/backend_session_logind" ]
|
||||||
elogind = ["logind", "smithay/backend_session_elogind" ]
|
elogind = ["logind", "smithay/backend_session_elogind" ]
|
||||||
xwayland = [ "smithay/xwayland", "x11rb" ]
|
xwayland = [ "smithay/xwayland", "x11rb" ]
|
||||||
|
|
|
@ -1,11 +1,3 @@
|
||||||
use glium::texture::Texture2d;
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
use glium::{
|
|
||||||
texture::{MipmapsOption, UncompressedFloatFormat},
|
|
||||||
GlObject,
|
|
||||||
};
|
|
||||||
use slog::Logger;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
@ -14,25 +6,22 @@ use smithay::backend::egl::{
|
||||||
display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLImages, Format,
|
display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLImages, Format,
|
||||||
};
|
};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::graphics::gl::GLGraphicsBackend,
|
|
||||||
reexports::wayland_server::protocol::wl_buffer::WlBuffer,
|
reexports::wayland_server::protocol::wl_buffer::WlBuffer,
|
||||||
wayland::shm::{with_buffer_contents as shm_buffer_contents, BufferAccessError},
|
wayland::shm::{with_buffer_contents as shm_buffer_contents, BufferAccessError},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::glium_drawer::GliumDrawer;
|
|
||||||
|
|
||||||
/// Utilities for working with `WlBuffer`s.
|
/// Utilities for working with `WlBuffer`s.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BufferUtils {
|
pub struct BufferUtils {
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
|
||||||
log: Logger,
|
log: ::slog::Logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferUtils {
|
impl BufferUtils {
|
||||||
/// Creates a new `BufferUtils`.
|
/// Creates a new `BufferUtils`.
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
pub fn new(egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>, log: Logger) -> Self {
|
pub fn new(egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>, log: ::slog::Logger) -> Self {
|
||||||
Self {
|
Self {
|
||||||
egl_buffer_reader,
|
egl_buffer_reader,
|
||||||
log,
|
log,
|
||||||
|
@ -41,7 +30,7 @@ impl BufferUtils {
|
||||||
|
|
||||||
/// Creates a new `BufferUtils`.
|
/// Creates a new `BufferUtils`.
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
pub fn new(log: Logger) -> Self {
|
pub fn new(log: ::slog::Logger) -> Self {
|
||||||
Self { log }
|
Self { log }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,166 +58,4 @@ impl BufferUtils {
|
||||||
err
|
err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
pub fn load_buffer(&self, buffer: WlBuffer) -> Result<BufferTextures, WlBuffer> {
|
|
||||||
// try to retrieve the egl contents of this buffer
|
|
||||||
let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() {
|
|
||||||
display.egl_buffer_contents(&buffer)
|
|
||||||
} else {
|
|
||||||
return Err(buffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
match images {
|
|
||||||
Ok(images) => {
|
|
||||||
// we have an EGL buffer
|
|
||||||
Ok(BufferTextures {
|
|
||||||
buffer,
|
|
||||||
textures: HashMap::new(),
|
|
||||||
fragment: crate::shaders::BUFFER_RGBA,
|
|
||||||
y_inverted: images.y_inverted,
|
|
||||||
dimensions: (images.width, images.height),
|
|
||||||
images: Some(images), // I guess we need to keep this alive ?
|
|
||||||
logger: self.log.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Err(EGLBufferAccessError::NotManaged(_)) => {
|
|
||||||
// this is not an EGL buffer, try SHM
|
|
||||||
self.load_shm_buffer(buffer)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(self.log, "EGL error"; "err" => format!("{:?}", err));
|
|
||||||
Err(buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "egl"))]
|
|
||||||
pub fn load_buffer(&self, buffer: WlBuffer) -> Result<BufferTextures, WlBuffer> {
|
|
||||||
self.load_shm_buffer(buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_shm_buffer(&self, buffer: WlBuffer) -> Result<BufferTextures, WlBuffer> {
|
|
||||||
let (width, height, format) =
|
|
||||||
match shm_buffer_contents(&buffer, |_, data| (data.width, data.height, data.format)) {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(err) => {
|
|
||||||
warn!(self.log, "Unable to load buffer contents"; "err" => format!("{:?}", err));
|
|
||||||
return Err(buffer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let shader = match crate::shm_load::load_format(format) {
|
|
||||||
Ok(x) => x.1,
|
|
||||||
Err(format) => {
|
|
||||||
warn!(self.log, "Unable to load buffer format: {:?}", format);
|
|
||||||
return Err(buffer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(BufferTextures {
|
|
||||||
buffer,
|
|
||||||
textures: HashMap::new(),
|
|
||||||
fragment: shader,
|
|
||||||
y_inverted: false,
|
|
||||||
dimensions: (width as u32, height as u32),
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
images: None,
|
|
||||||
logger: self.log.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BufferTextures {
|
|
||||||
buffer: WlBuffer,
|
|
||||||
pub textures: HashMap<usize, Texture2d>,
|
|
||||||
pub fragment: usize,
|
|
||||||
pub y_inverted: bool,
|
|
||||||
pub dimensions: (u32, u32),
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
images: Option<EGLImages>,
|
|
||||||
logger: slog::Logger,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BufferTextures {
|
|
||||||
#[cfg(feature = "egl")]
|
|
||||||
pub fn load_texture<'a, F: GLGraphicsBackend + 'static>(
|
|
||||||
&'a mut self,
|
|
||||||
drawer: &GliumDrawer<F>,
|
|
||||||
) -> Result<&'a Texture2d, ()> {
|
|
||||||
if self.textures.contains_key(&drawer.id) {
|
|
||||||
return Ok(&self.textures[&drawer.id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(images) = self.images.as_ref() {
|
|
||||||
//EGL buffer
|
|
||||||
let format = match images.format {
|
|
||||||
Format::RGB => UncompressedFloatFormat::U8U8U8,
|
|
||||||
Format::RGBA => UncompressedFloatFormat::U8U8U8U8,
|
|
||||||
_ => {
|
|
||||||
warn!(self.logger, "Unsupported EGL buffer format"; "format" => format!("{:?}", images.format));
|
|
||||||
return Err(());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let opengl_texture = Texture2d::empty_with_format(
|
|
||||||
&drawer.display,
|
|
||||||
format,
|
|
||||||
MipmapsOption::NoMipmap,
|
|
||||||
images.width,
|
|
||||||
images.height,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
images
|
|
||||||
.bind_to_texture(0, opengl_texture.get_id(), &*drawer.display.borrow())
|
|
||||||
.expect("Failed to bind to texture");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.textures.insert(drawer.id, opengl_texture);
|
|
||||||
Ok(&self.textures[&drawer.id])
|
|
||||||
} else {
|
|
||||||
self.load_shm_texture(drawer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "egl"))]
|
|
||||||
pub fn load_texture<'a, F: GLGraphicsBackend + 'static>(
|
|
||||||
&'a mut self,
|
|
||||||
drawer: &GliumDrawer<F>,
|
|
||||||
) -> Result<&'a Texture2d, ()> {
|
|
||||||
if self.textures.contains_key(&drawer.id) {
|
|
||||||
return Ok(&self.textures[&drawer.id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.load_shm_texture(drawer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_shm_texture<'a, F: GLGraphicsBackend + 'static>(
|
|
||||||
&'a mut self,
|
|
||||||
drawer: &GliumDrawer<F>,
|
|
||||||
) -> Result<&'a Texture2d, ()> {
|
|
||||||
match shm_buffer_contents(&self.buffer, |slice, data| {
|
|
||||||
crate::shm_load::load_shm_buffer(data, slice)
|
|
||||||
.map(|(image, _kind)| Texture2d::new(&drawer.display, image).unwrap())
|
|
||||||
}) {
|
|
||||||
Ok(Ok(texture)) => {
|
|
||||||
self.textures.insert(drawer.id, texture);
|
|
||||||
Ok(&self.textures[&drawer.id])
|
|
||||||
}
|
|
||||||
Ok(Err(format)) => {
|
|
||||||
warn!(self.logger, "Unsupported SHM buffer format"; "format" => format!("{:?}", format));
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!(self.logger, "Unable to load buffer contents"; "err" => format!("{:?}", err));
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for BufferTextures {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.buffer.release()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
use slog::Logger;
|
||||||
|
use smithay::{
|
||||||
|
backend::renderer::{Renderer, Frame, Transform, Texture},
|
||||||
|
reexports::{wayland_server::protocol::wl_surface},
|
||||||
|
utils::Rectangle,
|
||||||
|
wayland::{
|
||||||
|
compositor::{roles::Role, SubsurfaceRole, TraversalAction},
|
||||||
|
data_device::DnDIconRole,
|
||||||
|
seat::CursorImageRole,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
|
||||||
|
|
||||||
|
pub fn draw_cursor<R, E, T, F>(
|
||||||
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
|
surface: &wl_surface::WlSurface,
|
||||||
|
(x, y): (i32, i32),
|
||||||
|
token: MyCompositorToken,
|
||||||
|
log: &Logger,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
R: Renderer<Error=E, Texture=T, Frame=F>,
|
||||||
|
F: Frame<Error=E, Texture=T>,
|
||||||
|
E: std::error::Error,
|
||||||
|
T: Texture + 'static,
|
||||||
|
{
|
||||||
|
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(_) => {
|
||||||
|
warn!(
|
||||||
|
log,
|
||||||
|
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
||||||
|
);
|
||||||
|
(0, 0)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
draw_surface_tree(renderer, frame, surface, (x - dx, y - dy), token, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_surface_tree<R, E, T, F>(
|
||||||
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
|
root: &wl_surface::WlSurface,
|
||||||
|
location: (i32, i32),
|
||||||
|
compositor_token: MyCompositorToken,
|
||||||
|
log: &Logger,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
R: Renderer<Error=E, Texture=T, Frame=F>,
|
||||||
|
F: Frame<Error=E, Texture=T>,
|
||||||
|
E: std::error::Error,
|
||||||
|
T: Texture + 'static,
|
||||||
|
{
|
||||||
|
compositor_token.with_surface_tree_upward(
|
||||||
|
root,
|
||||||
|
location,
|
||||||
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
|
// Pull a new buffer if available
|
||||||
|
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
||||||
|
let mut data = data.borrow_mut();
|
||||||
|
if data.texture.is_none() {
|
||||||
|
if let Some(buffer) = data.current_state.buffer.take() {
|
||||||
|
match renderer.import_shm(&buffer) {
|
||||||
|
Ok(m) => data.texture = Some(Box::new(m) as Box<dyn std::any::Any + 'static>),
|
||||||
|
// there was an error reading the buffer, release it, we
|
||||||
|
// already logged the error
|
||||||
|
Err(err) => {
|
||||||
|
warn!(log, "Error loading buffer: {}", err);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
buffer.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now, should we be drawn ?
|
||||||
|
if data.texture.is_some() {
|
||||||
|
// if yes, also process the children
|
||||||
|
if Role::<SubsurfaceRole>::has(role) {
|
||||||
|
x += data.current_state.sub_location.0;
|
||||||
|
y += data.current_state.sub_location.1;
|
||||||
|
}
|
||||||
|
TraversalAction::DoChildren((x, y))
|
||||||
|
} else {
|
||||||
|
// we are not displayed, so our children are neither
|
||||||
|
TraversalAction::SkipChildren
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we are not displayed, so our children are neither
|
||||||
|
TraversalAction::SkipChildren
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|_surface, attributes, role, &(mut x, mut y)| {
|
||||||
|
if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
||||||
|
let mut data = data.borrow_mut();
|
||||||
|
let (sub_x, sub_y) = data.current_state.sub_location;
|
||||||
|
if let Some(buffer_texture) = data.texture.as_ref().and_then(|x| x.downcast_ref::<T>()) {
|
||||||
|
// we need to re-extract the subsurface offset, as the previous closure
|
||||||
|
// only passes it to our children
|
||||||
|
if Role::<SubsurfaceRole>::has(role) {
|
||||||
|
x += sub_x;
|
||||||
|
y += sub_y;
|
||||||
|
}
|
||||||
|
frame.render_texture_at(&*buffer_texture, (x, y), Transform::Normal /* TODO */, 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|_, _, _, _| true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_windows<R, E, T, F>(
|
||||||
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
|
window_map: &MyWindowMap,
|
||||||
|
output_rect: Option<Rectangle>,
|
||||||
|
compositor_token: MyCompositorToken,
|
||||||
|
log: &::slog::Logger,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
R: Renderer<Error=E, Texture=T, Frame=F>,
|
||||||
|
F: Frame<Error=E, Texture=T>,
|
||||||
|
E: std::error::Error,
|
||||||
|
T: Texture + 'static,
|
||||||
|
{
|
||||||
|
// redraw the frame, in a simple but inneficient way
|
||||||
|
{
|
||||||
|
window_map.with_windows_from_bottom_to_top(
|
||||||
|
|toplevel_surface, mut initial_place, bounding_box| {
|
||||||
|
// skip windows that do not overlap with a given output
|
||||||
|
if let Some(output) = output_rect {
|
||||||
|
if !output.overlaps(bounding_box) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
initial_place.0 -= output.x;
|
||||||
|
}
|
||||||
|
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
||||||
|
// this surface is a root of a subsurface tree that needs to be drawn
|
||||||
|
draw_surface_tree(
|
||||||
|
renderer,
|
||||||
|
frame,
|
||||||
|
&wl_surface,
|
||||||
|
initial_place,
|
||||||
|
compositor_token,
|
||||||
|
log,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw_dnd_icon<R, E, T, F>(
|
||||||
|
renderer: &mut R,
|
||||||
|
frame: &mut F,
|
||||||
|
surface: &wl_surface::WlSurface,
|
||||||
|
(x, y): (i32, i32),
|
||||||
|
token: MyCompositorToken,
|
||||||
|
log: &::slog::Logger,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
R: Renderer<Error=E, Texture=T, Frame=F>,
|
||||||
|
F: Frame<Error=E, Texture=T>,
|
||||||
|
E: std::error::Error,
|
||||||
|
T: Texture + 'static,
|
||||||
|
{
|
||||||
|
if !token.has_role::<DnDIconRole>(surface) {
|
||||||
|
warn!(
|
||||||
|
log,
|
||||||
|
"Trying to display as a dnd icon a surface that does not have the DndIcon role."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
draw_surface_tree(renderer, frame, surface, (x, y), token, log);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn schedule_initial_render<F: GLGraphicsBackend + 'static, Data: 'static>(
|
||||||
|
renderer: Rc<GliumDrawer<F>>,
|
||||||
|
evt_handle: &LoopHandle<Data>,
|
||||||
|
) {
|
||||||
|
let mut frame = renderer.draw();
|
||||||
|
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
||||||
|
if let Err(err) = frame.set_finish() {
|
||||||
|
match err {
|
||||||
|
SwapBuffersError::AlreadySwapped => {}
|
||||||
|
SwapBuffersError::TemporaryFailure(err) => {
|
||||||
|
// TODO dont reschedule after 3(?) retries
|
||||||
|
warn!(renderer.log, "Failed to submit page_flip: {}", err);
|
||||||
|
let handle = evt_handle.clone();
|
||||||
|
evt_handle.insert_idle(move |_| schedule_initial_render(renderer, &handle));
|
||||||
|
}
|
||||||
|
SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
|
@ -1,398 +0,0 @@
|
||||||
use std::{
|
|
||||||
cell::{Ref, RefCell},
|
|
||||||
rc::Rc,
|
|
||||||
sync::atomic::{AtomicBool, AtomicUsize, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
use glium::{self, index::PrimitiveType, texture::Texture2d, Surface};
|
|
||||||
use slog::Logger;
|
|
||||||
|
|
||||||
use smithay::{
|
|
||||||
backend::graphics::{
|
|
||||||
gl::GLGraphicsBackend,
|
|
||||||
glium::{Frame, GliumGraphicsBackend},
|
|
||||||
CursorBackend, SwapBuffersError,
|
|
||||||
},
|
|
||||||
reexports::{calloop::LoopHandle, wayland_server::protocol::wl_surface},
|
|
||||||
utils::Rectangle,
|
|
||||||
wayland::{
|
|
||||||
compositor::{roles::Role, SubsurfaceRole, TraversalAction},
|
|
||||||
data_device::DnDIconRole,
|
|
||||||
seat::CursorImageRole,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::buffer_utils::BufferUtils;
|
|
||||||
use crate::shaders;
|
|
||||||
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
|
|
||||||
|
|
||||||
pub static BACKEND_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
struct Vertex {
|
|
||||||
position: [f32; 2],
|
|
||||||
tex_coords: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
mod implement_vertex {
|
|
||||||
#![allow(clippy::unneeded_field_pattern)]
|
|
||||||
// Module to scope the clippy lint disabling
|
|
||||||
use super::Vertex;
|
|
||||||
implement_vertex!(Vertex, position, tex_coords);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct GliumDrawer<F: GLGraphicsBackend + 'static> {
|
|
||||||
pub id: usize,
|
|
||||||
pub display: GliumGraphicsBackend<F>,
|
|
||||||
vertex_buffer: glium::VertexBuffer<Vertex>,
|
|
||||||
index_buffer: glium::IndexBuffer<u16>,
|
|
||||||
programs: [glium::Program; shaders::FRAGMENT_COUNT],
|
|
||||||
buffer_loader: BufferUtils,
|
|
||||||
pub hardware_cursor: AtomicBool,
|
|
||||||
log: Logger,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
|
||||||
pub fn borrow(&self) -> Ref<'_, F> {
|
|
||||||
self.display.borrow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Into<GliumGraphicsBackend<T>> + GLGraphicsBackend + 'static> GliumDrawer<T> {
|
|
||||||
pub fn init(backend: T, buffer_loader: BufferUtils, log: Logger) -> GliumDrawer<T> {
|
|
||||||
let display = backend.into();
|
|
||||||
|
|
||||||
// building the vertex buffer, which contains all the vertices that we will draw
|
|
||||||
let vertex_buffer = glium::VertexBuffer::new(
|
|
||||||
&display,
|
|
||||||
&[
|
|
||||||
Vertex {
|
|
||||||
position: [0.0, 0.0],
|
|
||||||
tex_coords: [0.0, 0.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [0.0, 1.0],
|
|
||||||
tex_coords: [0.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [1.0, 1.0],
|
|
||||||
tex_coords: [1.0, 1.0],
|
|
||||||
},
|
|
||||||
Vertex {
|
|
||||||
position: [1.0, 0.0],
|
|
||||||
tex_coords: [1.0, 0.0],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// building the index buffer
|
|
||||||
let index_buffer =
|
|
||||||
glium::IndexBuffer::new(&display, PrimitiveType::TriangleStrip, &[1 as u16, 2, 0, 3]).unwrap();
|
|
||||||
|
|
||||||
let programs = opengl_programs!(&display);
|
|
||||||
|
|
||||||
GliumDrawer {
|
|
||||||
id: BACKEND_COUNTER.fetch_add(1, Ordering::AcqRel),
|
|
||||||
display,
|
|
||||||
vertex_buffer,
|
|
||||||
index_buffer,
|
|
||||||
programs,
|
|
||||||
buffer_loader,
|
|
||||||
hardware_cursor: AtomicBool::new(false),
|
|
||||||
log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: GLGraphicsBackend + CursorBackend + 'static> GliumDrawer<F> {
|
|
||||||
pub fn draw_hardware_cursor(
|
|
||||||
&self,
|
|
||||||
cursor: &<F as CursorBackend>::CursorFormat,
|
|
||||||
hotspot: (u32, u32),
|
|
||||||
position: (i32, i32),
|
|
||||||
) {
|
|
||||||
let (x, y) = position;
|
|
||||||
let _ = self.display.borrow().set_cursor_position(x as u32, y as u32);
|
|
||||||
if !self.hardware_cursor.swap(true, Ordering::SeqCst)
|
|
||||||
&& self
|
|
||||||
.display
|
|
||||||
.borrow()
|
|
||||||
.set_cursor_representation(cursor, hotspot)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
warn!(self.log, "Failed to upload hardware cursor",);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_software_cursor(
|
|
||||||
&self,
|
|
||||||
frame: &mut Frame,
|
|
||||||
surface: &wl_surface::WlSurface,
|
|
||||||
(x, y): (i32, i32),
|
|
||||||
token: MyCompositorToken,
|
|
||||||
) {
|
|
||||||
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
|
|
||||||
Ok(h) => h,
|
|
||||||
Err(_) => {
|
|
||||||
warn!(
|
|
||||||
self.log,
|
|
||||||
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
|
||||||
);
|
|
||||||
(0, 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
|
||||||
self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions);
|
|
||||||
self.clear_cursor()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear_cursor(&self) {
|
|
||||||
if self.hardware_cursor.swap(false, Ordering::SeqCst)
|
|
||||||
&& self.display.borrow().clear_cursor_representation().is_err()
|
|
||||||
{
|
|
||||||
warn!(self.log, "Failed to clear cursor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// I would love to do this (check on !CursorBackend), but this is essentially specialization...
|
|
||||||
// And since this is just an example compositor, it seems we require now,
|
|
||||||
// that for the use of software cursors we need the hardware cursor trait (to do automatic cleanup..)
|
|
||||||
/*
|
|
||||||
impl<F: GLGraphicsBackend + !CursorBackend + 'static> GliumDrawer<F> {
|
|
||||||
pub fn draw_software_cursor(
|
|
||||||
&self,
|
|
||||||
frame: &mut Frame,
|
|
||||||
surface: &wl_surface::WlSurface,
|
|
||||||
(x, y): (i32, i32),
|
|
||||||
token: MyCompositorToken,
|
|
||||||
) {
|
|
||||||
let (dx, dy) = match token.with_role_data::<CursorImageRole, _, _>(surface, |data| data.hotspot) {
|
|
||||||
Ok(h) => h,
|
|
||||||
Err(_) => {
|
|
||||||
warn!(
|
|
||||||
self.log,
|
|
||||||
"Trying to display as a cursor a surface that does not have the CursorImage role."
|
|
||||||
);
|
|
||||||
(0, 0)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
|
||||||
self.draw_surface_tree(frame, surface, (x - dx, y - dy), token, screen_dimensions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
|
||||||
pub fn render_texture(&self, target: &mut Frame, spec: RenderTextureSpec<'_>) {
|
|
||||||
let xscale = 2.0 * (spec.surface_dimensions.0 as f32) / (spec.screen_size.0 as f32);
|
|
||||||
let mut yscale = -2.0 * (spec.surface_dimensions.1 as f32) / (spec.screen_size.1 as f32);
|
|
||||||
|
|
||||||
let x = 2.0 * (spec.surface_location.0 as f32) / (spec.screen_size.0 as f32) - 1.0;
|
|
||||||
let mut y = 1.0 - 2.0 * (spec.surface_location.1 as f32) / (spec.screen_size.1 as f32);
|
|
||||||
|
|
||||||
if spec.y_inverted {
|
|
||||||
yscale = -yscale;
|
|
||||||
y -= spec.surface_dimensions.1 as f32;
|
|
||||||
}
|
|
||||||
|
|
||||||
let uniforms = uniform! {
|
|
||||||
matrix: [
|
|
||||||
[xscale, 0.0 , 0.0, 0.0],
|
|
||||||
[ 0.0 , yscale , 0.0, 0.0],
|
|
||||||
[ 0.0 , 0.0 , 1.0, 0.0],
|
|
||||||
[ x , y , 0.0, 1.0]
|
|
||||||
],
|
|
||||||
tex: spec.texture,
|
|
||||||
};
|
|
||||||
|
|
||||||
target
|
|
||||||
.draw(
|
|
||||||
&self.vertex_buffer,
|
|
||||||
&self.index_buffer,
|
|
||||||
&self.programs[spec.texture_kind],
|
|
||||||
&uniforms,
|
|
||||||
&glium::DrawParameters {
|
|
||||||
blend: spec.blending,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn draw(&self) -> Frame {
|
|
||||||
self.display.draw()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RenderTextureSpec<'a> {
|
|
||||||
texture: &'a Texture2d,
|
|
||||||
texture_kind: usize,
|
|
||||||
y_inverted: bool,
|
|
||||||
surface_dimensions: (u32, u32),
|
|
||||||
surface_location: (i32, i32),
|
|
||||||
screen_size: (u32, u32),
|
|
||||||
blending: glium::Blend,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
|
|
||||||
fn draw_surface_tree(
|
|
||||||
&self,
|
|
||||||
frame: &mut Frame,
|
|
||||||
root: &wl_surface::WlSurface,
|
|
||||||
location: (i32, i32),
|
|
||||||
compositor_token: MyCompositorToken,
|
|
||||||
screen_dimensions: (u32, u32),
|
|
||||||
) {
|
|
||||||
compositor_token.with_surface_tree_upward(
|
|
||||||
root,
|
|
||||||
location,
|
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|
||||||
// Pull a new buffer if available
|
|
||||||
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
|
||||||
let mut data = data.borrow_mut();
|
|
||||||
if data.texture.is_none() {
|
|
||||||
if let Some(buffer) = data.current_state.buffer.take() {
|
|
||||||
match self.buffer_loader.load_buffer(buffer) {
|
|
||||||
Ok(m) => data.texture = Some(m),
|
|
||||||
// there was an error reading the buffer, release it, we
|
|
||||||
// already logged the error
|
|
||||||
Err(buffer) => buffer.release(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Now, should we be drawn ?
|
|
||||||
if data.texture.is_some() {
|
|
||||||
// if yes, also process the children
|
|
||||||
if Role::<SubsurfaceRole>::has(role) {
|
|
||||||
x += data.current_state.sub_location.0;
|
|
||||||
y += data.current_state.sub_location.1;
|
|
||||||
}
|
|
||||||
TraversalAction::DoChildren((x, y))
|
|
||||||
} else {
|
|
||||||
// we are not displayed, so our children are neither
|
|
||||||
TraversalAction::SkipChildren
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we are not displayed, so our children are neither
|
|
||||||
TraversalAction::SkipChildren
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|_surface, attributes, role, &(mut x, mut y)| {
|
|
||||||
if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
|
|
||||||
let mut data = data.borrow_mut();
|
|
||||||
let (sub_x, sub_y) = data.current_state.sub_location;
|
|
||||||
if let Some(buffer_textures) = data.texture.as_mut() {
|
|
||||||
let texture_kind = buffer_textures.fragment;
|
|
||||||
let y_inverted = buffer_textures.y_inverted;
|
|
||||||
let surface_dimensions = buffer_textures.dimensions;
|
|
||||||
if let Ok(ref texture) = buffer_textures.load_texture(&self) {
|
|
||||||
// we need to re-extract the subsurface offset, as the previous closure
|
|
||||||
// only passes it to our children
|
|
||||||
if Role::<SubsurfaceRole>::has(role) {
|
|
||||||
x += sub_x;
|
|
||||||
y += sub_y;
|
|
||||||
}
|
|
||||||
self.render_texture(
|
|
||||||
frame,
|
|
||||||
RenderTextureSpec {
|
|
||||||
texture: &texture,
|
|
||||||
texture_kind,
|
|
||||||
y_inverted,
|
|
||||||
surface_dimensions,
|
|
||||||
surface_location: (x, y),
|
|
||||||
screen_size: screen_dimensions,
|
|
||||||
blending: ::glium::Blend {
|
|
||||||
color: ::glium::BlendingFunction::Addition {
|
|
||||||
source: ::glium::LinearBlendingFactor::One,
|
|
||||||
destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
|
||||||
},
|
|
||||||
alpha: ::glium::BlendingFunction::Addition {
|
|
||||||
source: ::glium::LinearBlendingFactor::One,
|
|
||||||
destination: ::glium::LinearBlendingFactor::OneMinusSourceAlpha,
|
|
||||||
},
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|_, _, _, _| true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_windows(
|
|
||||||
&self,
|
|
||||||
frame: &mut Frame,
|
|
||||||
window_map: &MyWindowMap,
|
|
||||||
output_rect: Option<Rectangle>,
|
|
||||||
compositor_token: MyCompositorToken,
|
|
||||||
) {
|
|
||||||
// redraw the frame, in a simple but inneficient way
|
|
||||||
{
|
|
||||||
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
|
||||||
window_map.with_windows_from_bottom_to_top(
|
|
||||||
|toplevel_surface, mut initial_place, bounding_box| {
|
|
||||||
// skip windows that do not overlap with a given output
|
|
||||||
if let Some(output) = output_rect {
|
|
||||||
if !output.overlaps(bounding_box) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
initial_place.0 -= output.x;
|
|
||||||
}
|
|
||||||
if let Some(wl_surface) = toplevel_surface.get_surface() {
|
|
||||||
// this surface is a root of a subsurface tree that needs to be drawn
|
|
||||||
self.draw_surface_tree(
|
|
||||||
frame,
|
|
||||||
&wl_surface,
|
|
||||||
initial_place,
|
|
||||||
compositor_token,
|
|
||||||
screen_dimensions,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_dnd_icon(
|
|
||||||
&self,
|
|
||||||
frame: &mut Frame,
|
|
||||||
surface: &wl_surface::WlSurface,
|
|
||||||
(x, y): (i32, i32),
|
|
||||||
token: MyCompositorToken,
|
|
||||||
) {
|
|
||||||
if !token.has_role::<DnDIconRole>(surface) {
|
|
||||||
warn!(
|
|
||||||
self.log,
|
|
||||||
"Trying to display as a dnd icon a surface that does not have the DndIcon role."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let screen_dimensions = self.borrow().get_framebuffer_dimensions();
|
|
||||||
self.draw_surface_tree(frame, surface, (x, y), token, screen_dimensions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn schedule_initial_render<F: GLGraphicsBackend + 'static, Data: 'static>(
|
|
||||||
renderer: Rc<GliumDrawer<F>>,
|
|
||||||
evt_handle: &LoopHandle<Data>,
|
|
||||||
) {
|
|
||||||
let mut frame = renderer.draw();
|
|
||||||
frame.clear_color(0.8, 0.8, 0.9, 1.0);
|
|
||||||
if let Err(err) = frame.set_finish() {
|
|
||||||
match err {
|
|
||||||
SwapBuffersError::AlreadySwapped => {}
|
|
||||||
SwapBuffersError::TemporaryFailure(err) => {
|
|
||||||
// TODO dont reschedule after 3(?) retries
|
|
||||||
warn!(renderer.log, "Failed to submit page_flip: {}", err);
|
|
||||||
let handle = evt_handle.clone();
|
|
||||||
evt_handle.insert_idle(move |_| schedule_initial_render(renderer, &handle));
|
|
||||||
}
|
|
||||||
SwapBuffersError::ContextLost(err) => panic!("Rendering loop lost: {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,5 @@
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate glium;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate slog;
|
extern crate slog;
|
||||||
#[macro_use(define_roles)]
|
#[macro_use(define_roles)]
|
||||||
|
@ -12,13 +10,10 @@ use std::{cell::RefCell, rc::Rc};
|
||||||
use slog::Drain;
|
use slog::Drain;
|
||||||
use smithay::reexports::{calloop::EventLoop, wayland_server::Display};
|
use smithay::reexports::{calloop::EventLoop, wayland_server::Display};
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod shaders;
|
|
||||||
mod buffer_utils;
|
mod buffer_utils;
|
||||||
mod glium_drawer;
|
mod drawing;
|
||||||
mod input_handler;
|
mod input_handler;
|
||||||
mod shell;
|
mod shell;
|
||||||
mod shm_load;
|
|
||||||
mod state;
|
mod state;
|
||||||
#[cfg(feature = "udev")]
|
#[cfg(feature = "udev")]
|
||||||
mod udev;
|
mod udev;
|
||||||
|
@ -41,6 +36,7 @@ fn main() {
|
||||||
// A logger facility, here we use the terminal here
|
// A logger facility, here we use the terminal here
|
||||||
let log = slog::Logger::root(
|
let log = slog::Logger::root(
|
||||||
slog_async::Async::default(slog_term::term_full().fuse()).fuse(),
|
slog_async::Async::default(slog_term::term_full().fuse()).fuse(),
|
||||||
|
//std::sync::Mutex::new(slog_term::term_full().fuse()).fuse(),
|
||||||
o!(),
|
o!(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
/*
|
|
||||||
* This file is the single point of definition of the opengl shaders
|
|
||||||
* and their indexes.
|
|
||||||
*
|
|
||||||
* The opengl_programs!() macro must call make_program!() in the correct
|
|
||||||
* order matching the indices stored in the BUFFER_* constants, if it
|
|
||||||
* does not, things will be drawn on screen with wrong colors.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// create a set of shaders for various loading types
|
|
||||||
macro_rules! make_program(
|
|
||||||
($display: expr, $fragment_shader:expr) => {
|
|
||||||
program!($display,
|
|
||||||
100 => {
|
|
||||||
vertex: crate::shaders::VERTEX_SHADER,
|
|
||||||
fragment: $fragment_shader,
|
|
||||||
},
|
|
||||||
).unwrap()
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
#[macro_escape]
|
|
||||||
macro_rules! opengl_programs(
|
|
||||||
($display: expr) => {
|
|
||||||
[
|
|
||||||
make_program!($display, crate::shaders::FRAGMENT_SHADER_RGBA),
|
|
||||||
make_program!($display, crate::shaders::FRAGMENT_SHADER_ABGR),
|
|
||||||
make_program!($display, crate::shaders::FRAGMENT_SHADER_XBGR),
|
|
||||||
make_program!($display, crate::shaders::FRAGMENT_SHADER_BGRA),
|
|
||||||
make_program!($display, crate::shaders::FRAGMENT_SHADER_BGRX),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* OpenGL Shaders
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub const VERTEX_SHADER: &str = r#"
|
|
||||||
#version 100
|
|
||||||
uniform lowp mat4 matrix;
|
|
||||||
attribute lowp vec2 position;
|
|
||||||
attribute lowp vec2 tex_coords;
|
|
||||||
varying lowp vec2 v_tex_coords;
|
|
||||||
void main() {
|
|
||||||
gl_Position = matrix * vec4(position, 0.0, 1.0);
|
|
||||||
v_tex_coords = tex_coords;
|
|
||||||
}"#;
|
|
||||||
|
|
||||||
pub const FRAGMENT_COUNT: usize = 5;
|
|
||||||
|
|
||||||
pub const BUFFER_RGBA: usize = 0;
|
|
||||||
pub const FRAGMENT_SHADER_RGBA: &str = r#"
|
|
||||||
#version 100
|
|
||||||
uniform lowp sampler2D tex;
|
|
||||||
varying lowp vec2 v_tex_coords;
|
|
||||||
void main() {
|
|
||||||
lowp vec4 color = texture2D(tex, v_tex_coords);
|
|
||||||
gl_FragColor.r = color.x;
|
|
||||||
gl_FragColor.g = color.y;
|
|
||||||
gl_FragColor.b = color.z;
|
|
||||||
gl_FragColor.a = color.w;
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
pub const BUFFER_ABGR: usize = 1;
|
|
||||||
pub const FRAGMENT_SHADER_ABGR: &str = r#"
|
|
||||||
#version 100
|
|
||||||
uniform lowp sampler2D tex;
|
|
||||||
varying lowp vec2 v_tex_coords;
|
|
||||||
void main() {
|
|
||||||
lowp vec4 color = texture2D(tex, v_tex_coords);
|
|
||||||
gl_FragColor.r = color.w;
|
|
||||||
gl_FragColor.g = color.z;
|
|
||||||
gl_FragColor.b = color.y;
|
|
||||||
gl_FragColor.a = color.x;
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
pub const BUFFER_XBGR: usize = 2;
|
|
||||||
pub const FRAGMENT_SHADER_XBGR: &str = r#"
|
|
||||||
#version 100
|
|
||||||
uniform lowp sampler2D tex;
|
|
||||||
varying lowp vec2 v_tex_coords;
|
|
||||||
void main() {
|
|
||||||
lowp vec4 color = texture2D(tex, v_tex_coords);
|
|
||||||
gl_FragColor.r = color.w;
|
|
||||||
gl_FragColor.g = color.z;
|
|
||||||
gl_FragColor.b = color.y;
|
|
||||||
gl_FragColor.a = 1.0;
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
pub const BUFFER_BGRA: usize = 3;
|
|
||||||
pub const FRAGMENT_SHADER_BGRA: &str = r#"
|
|
||||||
#version 100
|
|
||||||
uniform lowp sampler2D tex;
|
|
||||||
varying lowp vec2 v_tex_coords;
|
|
||||||
void main() {
|
|
||||||
lowp vec4 color = texture2D(tex, v_tex_coords);
|
|
||||||
gl_FragColor.r = color.z;
|
|
||||||
gl_FragColor.g = color.y;
|
|
||||||
gl_FragColor.b = color.x;
|
|
||||||
gl_FragColor.a = color.w;
|
|
||||||
}
|
|
||||||
"#;
|
|
||||||
|
|
||||||
pub const BUFFER_BGRX: usize = 4;
|
|
||||||
pub const FRAGMENT_SHADER_BGRX: &str = r#"
|
|
||||||
#version 100
|
|
||||||
uniform lowp sampler2D tex;
|
|
||||||
varying lowp vec2 v_tex_coords;
|
|
||||||
void main() {
|
|
||||||
lowp vec4 color = texture2D(tex, v_tex_coords);
|
|
||||||
gl_FragColor.r = color.z;
|
|
||||||
gl_FragColor.g = color.y;
|
|
||||||
gl_FragColor.b = color.x;
|
|
||||||
gl_FragColor.a = 1.0;
|
|
||||||
}
|
|
||||||
"#;
|
|
|
@ -672,7 +672,7 @@ pub struct CommitedState {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SurfaceData {
|
pub struct SurfaceData {
|
||||||
pub texture: Option<crate::buffer_utils::BufferTextures>,
|
pub texture: Option<Box<dyn std::any::Any + 'static>>,
|
||||||
pub geometry: Option<Rectangle>,
|
pub geometry: Option<Rectangle>,
|
||||||
pub resize_state: ResizeState,
|
pub resize_state: ResizeState,
|
||||||
/// Minimum width and height, as requested by the surface.
|
/// Minimum width and height, as requested by the surface.
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use smithay::{reexports::wayland_server::protocol::wl_shm::Format, wayland::shm::BufferData};
|
|
||||||
|
|
||||||
use glium::texture::{ClientFormat, RawImage2d};
|
|
||||||
|
|
||||||
pub fn load_shm_buffer(data: BufferData, pool: &[u8]) -> Result<(RawImage2d<'_, u8>, usize), Format> {
|
|
||||||
let offset = data.offset as usize;
|
|
||||||
let width = data.width as usize;
|
|
||||||
let height = data.height as usize;
|
|
||||||
let stride = data.stride as usize;
|
|
||||||
|
|
||||||
// number of bytes per pixel
|
|
||||||
// TODO: compute from data.format
|
|
||||||
let pixelsize = 4;
|
|
||||||
|
|
||||||
// ensure consistency, the SHM handler of smithay should ensure this
|
|
||||||
assert!(offset + (height - 1) * stride + width * pixelsize <= pool.len());
|
|
||||||
|
|
||||||
let slice: Cow<'_, [u8]> = if stride == width * pixelsize {
|
|
||||||
// the buffer is cleanly continuous, use as-is
|
|
||||||
Cow::Borrowed(&pool[offset..(offset + height * width * pixelsize)])
|
|
||||||
} else {
|
|
||||||
// the buffer is discontinuous or lines overlap
|
|
||||||
// we need to make a copy as unfortunately Glium does not
|
|
||||||
// expose the OpenGL APIs we would need to load this buffer :/
|
|
||||||
let mut data = Vec::with_capacity(height * width * pixelsize);
|
|
||||||
for i in 0..height {
|
|
||||||
data.extend(&pool[(offset + i * stride)..(offset + i * stride + width * pixelsize)]);
|
|
||||||
}
|
|
||||||
Cow::Owned(data)
|
|
||||||
};
|
|
||||||
|
|
||||||
// sharders format need to be reversed to account for endianness
|
|
||||||
let (client_format, fragment) = load_format(data.format)?;
|
|
||||||
Ok((
|
|
||||||
RawImage2d {
|
|
||||||
data: slice,
|
|
||||||
width: width as u32,
|
|
||||||
height: height as u32,
|
|
||||||
format: client_format,
|
|
||||||
},
|
|
||||||
fragment,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn load_format(format: Format) -> Result<(ClientFormat, usize), Format> {
|
|
||||||
Ok(match format {
|
|
||||||
Format::Argb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRA),
|
|
||||||
Format::Xrgb8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_BGRX),
|
|
||||||
Format::Rgba8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_ABGR),
|
|
||||||
Format::Rgbx8888 => (ClientFormat::U8U8U8U8, crate::shaders::BUFFER_XBGR),
|
|
||||||
_ => return Err(format),
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@ use slog::Logger;
|
||||||
use smithay::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend};
|
use smithay::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
drm::{
|
drm_old::{
|
||||||
atomic::{AtomicDrmDevice, AtomicDrmSurface},
|
atomic::{AtomicDrmDevice, AtomicDrmSurface},
|
||||||
common::fallback::{FallbackDevice, FallbackSurface},
|
common::fallback::{FallbackDevice, FallbackSurface},
|
||||||
device_bind,
|
device_bind,
|
||||||
|
@ -679,9 +679,9 @@ impl DrmRenderer {
|
||||||
match err {
|
match err {
|
||||||
SwapBuffersError::AlreadySwapped => false,
|
SwapBuffersError::AlreadySwapped => false,
|
||||||
SwapBuffersError::TemporaryFailure(err) => {
|
SwapBuffersError::TemporaryFailure(err) => {
|
||||||
match err.downcast_ref::<smithay::backend::drm::common::Error>() {
|
match err.downcast_ref::<smithay::backend::drm_old::common::Error>() {
|
||||||
Some(&smithay::backend::drm::common::Error::DeviceInactive) => false,
|
Some(&smithay::backend::drm_old::common::Error::DeviceInactive) => false,
|
||||||
Some(&smithay::backend::drm::common::Error::Access {
|
Some(&smithay::backend::drm_old::common::Error::Access {
|
||||||
ref source, ..
|
ref source, ..
|
||||||
}) if matches!(source.get_ref(), drm::SystemError::PermissionDenied) => false,
|
}) if matches!(source.get_ref(), drm::SystemError::PermissionDenied) => false,
|
||||||
_ => true,
|
_ => true,
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
|
use std::{cell::RefCell, rc::Rc, sync::atomic::Ordering, time::Duration};
|
||||||
|
|
||||||
#[cfg(feature = "egl")]
|
//#[cfg(feature = "egl")]
|
||||||
use smithay::backend::egl::EGLGraphicsBackend;
|
//use smithay::backend::egl::EGLGraphicsBackend;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{graphics::gl::GLGraphicsBackend, input::InputBackend, winit},
|
backend::{renderer::Frame, input::InputBackend, winit, SwapBuffersError},
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::EventLoop,
|
calloop::EventLoop,
|
||||||
wayland_server::{protocol::wl_output, Display},
|
wayland_server::{protocol::wl_output, Display},
|
||||||
|
@ -17,19 +17,20 @@ use smithay::{
|
||||||
|
|
||||||
use slog::Logger;
|
use slog::Logger;
|
||||||
|
|
||||||
use crate::buffer_utils::BufferUtils;
|
|
||||||
use crate::glium_drawer::GliumDrawer;
|
|
||||||
use crate::state::AnvilState;
|
use crate::state::AnvilState;
|
||||||
|
use crate::buffer_utils::BufferUtils;
|
||||||
|
use crate::drawing::*;
|
||||||
|
|
||||||
pub fn run_winit(
|
pub fn run_winit(
|
||||||
display: Rc<RefCell<Display>>,
|
display: Rc<RefCell<Display>>,
|
||||||
event_loop: &mut EventLoop<AnvilState>,
|
event_loop: &mut EventLoop<AnvilState>,
|
||||||
log: Logger,
|
log: Logger,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
let (renderer, mut input) = winit::init(log.clone()).map_err(|err| {
|
let (mut renderer, mut input) = winit::init(log.clone()).map_err(|err| {
|
||||||
slog::crit!(log, "Failed to initialize Winit backend: {}", err);
|
slog::crit!(log, "Failed to initialize Winit backend: {}", err);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
/*
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let egl_buffer_reader = Rc::new(RefCell::new(
|
let egl_buffer_reader = Rc::new(RefCell::new(
|
||||||
if let Ok(egl_buffer_reader) = renderer.bind_wl_display(&display.borrow()) {
|
if let Ok(egl_buffer_reader) = renderer.bind_wl_display(&display.borrow()) {
|
||||||
|
@ -43,10 +44,10 @@ pub fn run_winit(
|
||||||
#[cfg(feature = "egl")]
|
#[cfg(feature = "egl")]
|
||||||
let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone());
|
let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone());
|
||||||
#[cfg(not(feature = "egl"))]
|
#[cfg(not(feature = "egl"))]
|
||||||
|
*/
|
||||||
let buffer_utils = BufferUtils::new(log.clone());
|
let buffer_utils = BufferUtils::new(log.clone());
|
||||||
|
|
||||||
let (w, h) = renderer.get_framebuffer_dimensions();
|
let (w, h): (u32, u32) = renderer.window_size().physical_size.into();
|
||||||
let drawer = GliumDrawer::init(renderer, buffer_utils.clone(), log.clone());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the globals
|
* Initialize the globals
|
||||||
|
@ -94,9 +95,13 @@ pub fn run_winit(
|
||||||
info!(log, "Initialization completed, starting the main loop.");
|
info!(log, "Initialization completed, starting the main loop.");
|
||||||
|
|
||||||
while state.running.load(Ordering::SeqCst) {
|
while state.running.load(Ordering::SeqCst) {
|
||||||
input
|
if input
|
||||||
.dispatch_new_events(|event, _| state.process_input_event(event))
|
.dispatch_new_events(|event, _| state.process_input_event(event))
|
||||||
.unwrap();
|
.is_err()
|
||||||
|
{
|
||||||
|
state.running.store(false, Ordering::SeqCst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Send frame events so that client start drawing their next frame
|
// Send frame events so that client start drawing their next frame
|
||||||
state
|
state
|
||||||
|
@ -107,12 +112,11 @@ pub fn run_winit(
|
||||||
|
|
||||||
// drawing logic
|
// drawing logic
|
||||||
{
|
{
|
||||||
use glium::Surface;
|
let mut frame = renderer.begin().expect("Failed to render frame");
|
||||||
let mut frame = drawer.draw();
|
frame.clear([0.8, 0.8, 0.9, 1.0]);
|
||||||
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
|
||||||
|
|
||||||
// draw the windows
|
// draw the windows
|
||||||
drawer.draw_windows(&mut frame, &*state.window_map.borrow(), None, state.ctoken);
|
draw_windows(&mut renderer, &mut frame, &*state.window_map.borrow(), None, state.ctoken, &log);
|
||||||
|
|
||||||
let (x, y) = *state.pointer_location.borrow();
|
let (x, y) = *state.pointer_location.borrow();
|
||||||
// draw the dnd icon if any
|
// draw the dnd icon if any
|
||||||
|
@ -120,7 +124,7 @@ pub fn run_winit(
|
||||||
let guard = state.dnd_icon.lock().unwrap();
|
let guard = state.dnd_icon.lock().unwrap();
|
||||||
if let Some(ref surface) = *guard {
|
if let Some(ref surface) = *guard {
|
||||||
if surface.as_ref().is_alive() {
|
if surface.as_ref().is_alive() {
|
||||||
drawer.draw_dnd_icon(&mut frame, surface, (x as i32, y as i32), state.ctoken);
|
draw_dnd_icon(&mut renderer, &mut frame, surface, (x as i32, y as i32), state.ctoken, &log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,19 +139,23 @@ pub fn run_winit(
|
||||||
if reset {
|
if reset {
|
||||||
*guard = CursorImageStatus::Default;
|
*guard = CursorImageStatus::Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw as relevant
|
// draw as relevant
|
||||||
if let CursorImageStatus::Image(ref surface) = *guard {
|
if let CursorImageStatus::Image(ref surface) = *guard {
|
||||||
drawer.draw_software_cursor(&mut frame, surface, (x as i32, y as i32), state.ctoken);
|
renderer.window().set_cursor_visible(false);
|
||||||
|
draw_cursor(&mut renderer, &mut frame, surface, (x as i32, y as i32), state.ctoken, &log);
|
||||||
} else {
|
} else {
|
||||||
drawer.draw_hardware_cursor(&CursorIcon::Default, (0, 0), (x as i32, y as i32));
|
renderer.window().set_cursor_visible(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = frame.finish() {
|
if let Err(SwapBuffersError::ContextLost(err)) = frame.finish() {
|
||||||
error!(log, "Error during rendering: {:?}", err);
|
error!(log, "Critical Rendering Error: {}", err);
|
||||||
|
state.running.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if event_loop
|
if event_loop
|
||||||
.dispatch(Some(Duration::from_millis(16)), &mut state)
|
.dispatch(Some(Duration::from_millis(16)), &mut state)
|
||||||
.is_err()
|
.is_err()
|
||||||
|
|
Loading…
Reference in New Issue