cosmic-comp/src/backend/x11.rs

425 lines
14 KiB
Rust
Raw Normal View History

2021-12-15 23:23:49 +01:00
// SPDX-License-Identifier: GPL-3.0-only
use crate::{
2022-02-04 21:04:17 +01:00
backend::render,
config::OutputConfig,
input::Devices,
state::{BackendData, Common, Data},
utils::prelude::*,
2021-12-15 23:23:49 +01:00
};
use anyhow::{Context, Result};
use smithay::{
backend::{
allocator::dmabuf::Dmabuf,
egl::{EGLContext, EGLDisplay},
2021-12-22 20:14:09 +01:00
input::{Event, InputEvent},
2022-09-28 12:01:29 +02:00
renderer::{
damage::DamageTrackedRenderer, gles2::Gles2Renderer, Bind, ImportDma, ImportEgl,
},
2021-12-22 20:14:09 +01:00
x11::{Window, WindowBuilder, X11Backend, X11Event, X11Handle, X11Input, X11Surface},
2021-12-15 23:23:49 +01:00
},
2022-01-11 17:22:23 +01:00
desktop::layer_map_for_output,
output::{Mode, Output, PhysicalProperties, Scale, Subpixel},
2021-12-15 23:23:49 +01:00
reexports::{
calloop::{ping, EventLoop, LoopHandle},
2022-03-16 20:05:24 +01:00
gbm::{Device as GbmDevice, FdWrapper},
wayland_server::DisplayHandle,
2021-12-15 23:23:49 +01:00
},
utils::Transform,
2021-12-15 23:23:49 +01:00
};
use std::{
cell::RefCell,
sync::{Arc, Mutex},
};
2022-01-11 19:18:41 +01:00
#[cfg(feature = "debug")]
2022-02-04 21:04:17 +01:00
use crate::state::Fps;
2022-01-11 19:18:41 +01:00
2021-12-15 23:23:49 +01:00
pub struct X11State {
2022-03-16 20:05:24 +01:00
allocator: Arc<Mutex<GbmDevice<FdWrapper>>>,
2021-12-15 23:23:49 +01:00
_egl: EGLDisplay,
pub renderer: Gles2Renderer,
2021-12-15 23:23:49 +01:00
surfaces: Vec<Surface>,
2021-12-17 17:53:01 +01:00
handle: X11Handle,
2021-12-15 23:23:49 +01:00
}
impl X11State {
pub fn add_window(&mut self, handle: LoopHandle<'_, Data>) -> Result<Output> {
2021-12-15 23:23:49 +01:00
let window = WindowBuilder::new()
.title("COSMIC")
.build(&self.handle)
.with_context(|| "Failed to create window")?;
2021-12-17 17:53:01 +01:00
let fourcc = window.format();
2021-12-15 23:23:49 +01:00
let surface = self
.handle
.create_surface(
&window,
self.allocator.clone(),
Bind::<Dmabuf>::supported_formats(&self.renderer)
2021-12-15 23:23:49 +01:00
.unwrap()
.iter()
.filter(|format| format.code == fourcc)
.map(|format| format.modifier),
)
.with_context(|| "Failed to create surface")?;
let name = format!("X11-{}", self.surfaces.len());
let size = window.size();
let props = PhysicalProperties {
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "COSMIC".to_string(),
model: name.clone(),
};
let mode = Mode {
size: (size.w as i32, size.h as i32).into(),
refresh: 60_000,
};
let output = Output::new(name, props, None);
output.add_mode(mode);
2021-12-15 23:23:49 +01:00
output.set_preferred(mode);
output.change_current_state(
Some(mode),
Some(Transform::Normal),
Some(Scale::Integer(1)),
Some((0, 0).into()),
);
output.user_data().insert_if_missing(|| {
RefCell::new(OutputConfig {
mode: ((size.w as i32, size.h as i32), None),
..Default::default()
})
});
2021-12-15 23:23:49 +01:00
let output_ref = output.clone();
let (ping, source) =
ping::make_ping().with_context(|| "Failed to create output event loop source")?;
let _token = handle
.insert_source(source, move |_, _, data| {
let x11_state = data.state.backend.x11();
2021-12-15 23:23:49 +01:00
if let Some(surface) = x11_state
.surfaces
.iter_mut()
.find(|s| s.output == output_ref)
{
2022-07-04 16:00:29 +02:00
if let Err(err) =
surface.render_output(&mut x11_state.renderer, &mut data.state.common)
2022-01-18 19:42:56 +01:00
{
2021-12-15 23:23:49 +01:00
slog_scope::error!("Error rendering: {}", err);
}
2022-02-04 21:04:17 +01:00
surface.dirty = false;
surface.pending = true;
2021-12-15 23:23:49 +01:00
}
})
.with_context(|| "Failed to add output to event loop")?;
self.surfaces.push(Surface {
window,
surface,
2022-09-28 12:01:29 +02:00
damage_tracker: DamageTrackedRenderer::from_output(&output),
2021-12-15 23:23:49 +01:00
output: output.clone(),
render: ping.clone(),
2022-02-04 21:04:17 +01:00
dirty: false,
pending: true,
2022-01-11 19:18:41 +01:00
#[cfg(feature = "debug")]
fps: Fps::default(),
2021-12-15 23:23:49 +01:00
});
// schedule first render
ping.ping();
Ok(output)
}
pub fn schedule_render(&mut self, output: &Output) {
if let Some(surface) = self.surfaces.iter_mut().find(|s| s.output == *output) {
2022-02-04 21:04:17 +01:00
surface.dirty = true;
if !surface.pending {
surface.render.ping();
}
}
}
pub fn apply_config_for_output(
&mut self,
output: &Output,
test_only: bool,
) -> Result<(), anyhow::Error> {
// TODO: if we ever have multiple winit outputs, don't ignore config.enabled
// reset size
let size = self
.surfaces
.iter()
.find(|s| s.output == *output)
.unwrap()
.window
.size();
let mut config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
if config.mode.0 != (size.w as i32, size.h as i32) {
if !test_only {
config.mode = ((size.w as i32, size.h as i32), None);
}
Err(anyhow::anyhow!("Cannot set window size"))
} else {
Ok(())
}
}
2021-12-15 23:23:49 +01:00
}
pub struct Surface {
window: Window,
2022-09-28 12:01:29 +02:00
damage_tracker: DamageTrackedRenderer,
2021-12-15 23:23:49 +01:00
surface: X11Surface,
output: Output,
render: ping::Ping,
2022-02-04 21:04:17 +01:00
dirty: bool,
pending: bool,
2022-01-11 19:18:41 +01:00
#[cfg(feature = "debug")]
fps: Fps,
2021-12-15 23:23:49 +01:00
}
impl Surface {
2022-01-11 17:22:23 +01:00
pub fn render_output(
2021-12-15 23:23:49 +01:00
&mut self,
renderer: &mut Gles2Renderer,
2022-01-11 17:22:23 +01:00
state: &mut Common,
2021-12-15 23:23:49 +01:00
) -> Result<()> {
let (buffer, age) = self
.surface
.buffer()
.with_context(|| "Failed to allocate buffer")?;
renderer
.bind(buffer)
.with_context(|| "Failed to bind buffer")?;
2022-02-04 21:04:17 +01:00
match render::render_output(
None,
2021-12-15 23:23:49 +01:00
renderer,
2022-09-28 12:01:29 +02:00
&mut self.damage_tracker,
age as usize,
2022-02-04 21:04:17 +01:00
state,
2021-12-15 23:23:49 +01:00
&self.output,
2022-02-04 21:04:17 +01:00
true,
#[cfg(feature = "debug")]
2022-08-03 16:34:04 +02:00
Some(&mut self.fps),
2021-12-15 23:23:49 +01:00
) {
Ok(_) => {
2022-09-28 12:01:29 +02:00
state.send_frames(&self.output);
2021-12-15 23:23:49 +01:00
self.surface
.submit()
.with_context(|| "Failed to submit buffer for display")?;
}
Err(err) => {
self.surface.reset_buffers();
anyhow::bail!("Rendering failed: {}", err);
}
};
Ok(())
}
}
2022-07-04 16:00:29 +02:00
pub fn init_backend(
dh: &DisplayHandle,
event_loop: &mut EventLoop<Data>,
state: &mut State,
) -> Result<()> {
2021-12-15 23:23:49 +01:00
let backend = X11Backend::new(None).with_context(|| "Failed to initilize X11 backend")?;
let handle = backend.handle();
// Obtain the DRM node the X server uses for direct rendering.
2022-03-16 20:05:24 +01:00
let (_drm_node, fd) = handle
2021-12-15 23:23:49 +01:00
.drm_node()
.with_context(|| "Could not get DRM node used by X server")?;
// Create the gbm device for buffer allocation.
2022-03-16 20:05:24 +01:00
let device =
unsafe { GbmDevice::new_from_fd(fd) }.with_context(|| "Failed to create GBM device")?;
2021-12-15 23:23:49 +01:00
// Initialize EGL using the GBM device.
2022-09-28 15:18:04 +02:00
let egl =
unsafe { EGLDisplay::new(&device, None).with_context(|| "Failed to create EGL display")? };
2021-12-15 23:23:49 +01:00
// Create the OpenGL context
let context = EGLContext::new(&egl, None).with_context(|| "Failed to create EGL context")?;
// Create a renderer
let mut renderer = unsafe { Gles2Renderer::new(context, None) }
.with_context(|| "Failed to initialize renderer")?;
2021-12-15 23:23:49 +01:00
init_egl_client_side(dh, state, &mut renderer)?;
2021-12-15 23:23:49 +01:00
state.backend = BackendData::X11(X11State {
handle,
allocator: Arc::new(Mutex::new(device)),
_egl: egl,
renderer,
surfaces: Vec::new(),
});
let output = state
.backend
.x11()
.add_window(event_loop.handle())
2021-12-15 23:23:49 +01:00
.with_context(|| "Failed to create wl_output")?;
state
.common
.output_configuration_state
2022-07-04 16:00:29 +02:00
.add_heads(std::iter::once(&output));
state.common.output_configuration_state.update();
2022-05-03 13:37:51 +02:00
state.common.shell.add_output(&output);
state.common.config.read_outputs(
std::iter::once(&output),
&mut state.backend,
&mut state.common.shell,
&state.common.event_loop_handle,
2022-05-03 13:37:51 +02:00
);
2022-04-14 22:16:37 +02:00
state.common.shell.refresh_outputs();
state.common.config.write_outputs(std::iter::once(&output));
2021-12-15 23:23:49 +01:00
event_loop
.handle()
.insert_source(backend, |event, _, data| match event {
2021-12-22 20:14:09 +01:00
X11Event::CloseRequested { window_id } => {
2021-12-15 23:23:49 +01:00
// TODO: drain_filter
let mut outputs_removed = Vec::new();
for surface in data
.state
2021-12-15 23:23:49 +01:00
.backend
.x11()
.surfaces
.iter()
2021-12-22 20:14:09 +01:00
.filter(|s| s.window.id() == window_id)
2021-12-15 23:23:49 +01:00
{
2021-12-22 20:14:09 +01:00
surface.window.unmap();
outputs_removed.push(surface.output.clone());
2021-12-15 23:23:49 +01:00
}
data.state
2021-12-22 20:14:09 +01:00
.backend
.x11()
.surfaces
.retain(|s| s.window.id() != window_id);
for output in outputs_removed.into_iter() {
data.state.common.shell.remove_output(&output);
}
2021-12-15 23:23:49 +01:00
}
2021-12-22 20:14:09 +01:00
X11Event::Resized {
new_size,
window_id,
} => {
let size = { (new_size.w as i32, new_size.h as i32).into() };
2021-12-15 23:23:49 +01:00
let mode = Mode {
size,
refresh: 60_000,
};
2022-07-04 16:00:29 +02:00
if let Some(surface) = data
.state
2021-12-15 23:23:49 +01:00
.backend
.x11()
.surfaces
.iter_mut()
2021-12-22 20:14:09 +01:00
.find(|s| s.window.id() == window_id)
{
let output = &surface.output;
{
let mut config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
config.mode.0 = size.into();
}
2021-12-22 20:14:09 +01:00
output.delete_mode(output.current_mode().unwrap());
output.change_current_state(Some(mode), None, None, None);
output.set_preferred(mode);
2022-09-28 12:01:29 +02:00
layer_map_for_output(output).arrange();
2022-07-04 16:00:29 +02:00
data.state.common.output_configuration_state.update();
data.state.common.shell.refresh_outputs();
2022-02-04 21:04:17 +01:00
surface.dirty = true;
if !surface.pending {
surface.render.ping();
}
2021-12-22 20:14:09 +01:00
}
2022-03-16 20:05:24 +01:00
}
2022-02-04 21:04:17 +01:00
X11Event::Refresh { window_id } | X11Event::PresentCompleted { window_id } => {
if let Some(surface) = data
.state
2022-02-04 21:04:17 +01:00
.backend
.x11()
.surfaces
.iter_mut()
.find(|s| s.window.id() == window_id)
{
if surface.dirty {
surface.render.ping();
} else {
surface.pending = false;
}
}
2022-03-16 20:05:24 +01:00
}
2022-08-31 13:01:23 +02:00
X11Event::Input(event) => data.state.process_x11_event(event),
2021-12-15 23:23:49 +01:00
})
2021-12-17 17:53:01 +01:00
.map_err(|_| anyhow::anyhow!("Failed to insert X11 Backend into event loop"))?;
2021-12-15 23:23:49 +01:00
Ok(())
}
2022-07-04 16:00:29 +02:00
fn init_egl_client_side(
dh: &DisplayHandle,
state: &mut State,
renderer: &mut Gles2Renderer,
) -> Result<()> {
let bind_result = renderer.bind_wl_display(dh);
2021-12-15 23:23:49 +01:00
match bind_result {
Ok(_) => {
slog_scope::info!("EGL hardware-acceleration enabled");
2022-07-04 16:00:29 +02:00
let dmabuf_formats = renderer.dmabuf_formats().cloned().collect::<Vec<_>>();
state
.common
.dmabuf_state
.create_global::<State, _>(dh, dmabuf_formats, None);
2021-12-15 23:23:49 +01:00
}
Err(err) => slog_scope::warn!("Unable to initialize bind display to EGL: {}", err),
};
Ok(())
}
2021-12-22 20:14:09 +01:00
impl State {
2022-08-31 13:01:23 +02:00
pub fn process_x11_event(&mut self, event: InputEvent<X11Input>) {
2021-12-22 20:14:09 +01:00
// here we can handle special cases for x11 inputs, like mapping them to windows
match &event {
InputEvent::PointerMotionAbsolute { event } => {
if let Some(window) = event.window() {
let output = self
.backend
.x11()
.surfaces
.iter()
.find(|surface| &surface.window == window.as_ref())
.map(|surface| surface.output.clone())
.unwrap();
let device = event.device();
2022-09-28 12:01:29 +02:00
for seat in self.common.seats().cloned().collect::<Vec<_>>().iter() {
2021-12-22 20:14:09 +01:00
let devices = seat.user_data().get::<Devices>().unwrap();
if devices.has_device(&device) {
2022-09-28 12:01:29 +02:00
seat.set_active_output(&output);
2021-12-22 20:14:09 +01:00
break;
}
}
}
}
_ => {}
};
2022-08-31 13:01:23 +02:00
self.process_input_event(event);
// TODO actually figure out the output
2022-03-24 20:32:31 +01:00
for output in self.common.shell.outputs() {
self.backend
.schedule_render(&self.common.event_loop_handle, output);
}
2021-12-22 20:14:09 +01:00
}
}