cosmic-comp/src/backend/winit.rs
2022-04-05 16:36:39 +02:00

220 lines
7.2 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-only
use crate::{
backend::render,
input::{set_active_output, Devices},
state::{BackendData, Common, State},
};
use anyhow::{Context, Result};
use smithay::{
backend::{
renderer::{ImportDma, ImportEgl},
winit::{self, WinitEvent, WinitGraphicsBackend, WinitVirtualDevice},
},
desktop::layer_map_for_output,
reexports::{
calloop::{ping, EventLoop},
wayland_server::{
protocol::wl_output::{Subpixel, Transform},
Display,
},
},
wayland::{
dmabuf::init_dmabuf_global,
output::{Mode, Output, PhysicalProperties},
},
};
use std::{cell::RefCell, rc::Rc};
#[cfg(feature = "debug")]
use crate::state::Fps;
pub struct WinitState {
// The winit backend currently has no notion of multiple windows
backend: Rc<RefCell<WinitGraphicsBackend>>,
//_global: GlobalDrop<WlOutput>,
output: Output,
#[cfg(feature = "debug")]
fps: Fps,
}
impl WinitState {
pub fn render_output(&mut self, state: &mut Common) -> Result<()> {
let backend = &mut *self.backend.borrow_mut();
backend.bind().with_context(|| "Failed to bind buffer")?;
let age = backend.buffer_age().unwrap_or(0);
match render::render_output(
None,
backend.renderer(),
age as u8,
state,
&self.output,
true,
#[cfg(feature = "debug")]
&mut self.fps,
) {
Ok(damage) => {
state
.shell
.active_space_mut(&self.output)
.space
.send_frames(state.start_time.elapsed().as_millis() as u32);
backend
.submit(damage.as_ref().map(|x| &**x), 1.0)
.with_context(|| "Failed to submit buffer for display")?;
}
Err(err) => {
anyhow::bail!("Rendering failed: {}", err);
}
};
Ok(())
}
}
pub fn init_backend(event_loop: &mut EventLoop<State>, state: &mut State) -> Result<()> {
let (backend, mut input) =
winit::init(None).with_context(|| "Failed to initilize winit backend")?;
let backend = Rc::new(RefCell::new(backend));
init_egl_client_side(&mut *state.common.display.borrow_mut(), backend.clone())?;
let name = format!("WINIT-0");
let size = backend.borrow().window_size();
let props = PhysicalProperties {
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: "COSMIC".to_string(),
model: name.clone(),
};
let mode = Mode {
size: (size.physical_size.w as i32, size.physical_size.h as i32).into(),
refresh: 60_000,
};
let (output, _global) = Output::new(&mut *state.common.display.borrow_mut(), name, props, None);
//let _global = global.into();
output.change_current_state(
Some(mode),
Some(Transform::Flipped180),
None,
Some((0, 0).into()),
);
output.set_preferred(mode);
let (event_ping, event_source) =
ping::make_ping().with_context(|| "Failed to init eventloop timer for winit")?;
let (render_ping, render_source) =
ping::make_ping().with_context(|| "Failed to init eventloop timer for winit")?;
let event_ping_handle = event_ping.clone();
let render_ping_handle = render_ping.clone();
let mut token = Some(
event_loop
.handle()
.insert_source(render_source, move |_, _, state| {
if let Err(err) = state.backend.winit().render_output(&mut state.common) {
slog_scope::error!("Failed to render frame: {}", err);
render_ping.ping();
}
})
.map_err(|_| anyhow::anyhow!("Failed to init eventloop timer for winit"))?,
);
let event_loop_handle = event_loop.handle();
event_loop
.handle()
.insert_source(event_source, move |_, _, state| {
match input
.dispatch_new_events(|event| state.process_winit_event(event, &render_ping_handle))
{
Ok(_) => {
event_ping_handle.ping();
render_ping_handle.ping();
}
Err(winit::WinitError::WindowClosed) => {
let winit_state = state.backend.winit();
state.common.shell.unmap_output(&winit_state.output);
if let Some(token) = token.take() {
event_loop_handle.remove(token);
}
}
};
})
.map_err(|_| anyhow::anyhow!("Failed to init eventloop timer for winit"))?;
event_ping.ping();
state.backend = BackendData::Winit(WinitState {
backend,
output,
#[cfg(feature = "debug")]
fps: Fps::default(),
});
state.common.shell.map_output(&output, &mut state.backend, &state.common.config);
Ok(())
}
fn init_egl_client_side(
display: &mut Display,
renderer: Rc<RefCell<WinitGraphicsBackend>>,
) -> Result<()> {
let bind_result = renderer.borrow_mut().renderer().bind_wl_display(display);
match bind_result {
Ok(_) => {
slog_scope::info!("EGL hardware-acceleration enabled");
let dmabuf_formats = renderer
.borrow_mut()
.renderer()
.dmabuf_formats()
.cloned()
.collect::<Vec<_>>();
init_dmabuf_global(
display,
dmabuf_formats,
move |buffer, _| {
renderer
.borrow_mut()
.renderer()
.import_dmabuf(buffer, None)
.is_ok()
},
None,
);
}
Err(err) => slog_scope::warn!("Unable to initialize bind display to EGL: {}", err),
};
Ok(())
}
impl State {
pub fn process_winit_event(&mut self, event: WinitEvent, render_ping: &ping::Ping) {
// here we can handle special cases for winit inputs
match event {
WinitEvent::Focus(true) => {
for seat in self.common.seats.clone().iter() {
let devices = seat.user_data().get::<Devices>().unwrap();
if devices.has_device(&WinitVirtualDevice) {
set_active_output(seat, &self.backend.winit().output);
break;
}
}
}
WinitEvent::Resized { size, .. } => {
let winit_state = self.backend.winit();
let output = &winit_state.output;
let mode = Mode {
size,
refresh: 60_000,
};
output.delete_mode(output.current_mode().unwrap());
output.change_current_state(Some(mode), None, None, None);
output.set_preferred(mode);
layer_map_for_output(output).arrange();
render_ping.ping();
}
WinitEvent::Refresh => render_ping.ping(),
WinitEvent::Input(event) => self.common.process_input_event(event),
_ => {}
};
}
}