cosmic-comp/src/backend/winit.rs

278 lines
8.9 KiB
Rust
Raw Normal View History

// 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::*,
};
use anyhow::{anyhow, Context, Result};
use smithay::{
backend::{
2022-09-28 12:01:29 +02:00
renderer::{damage::DamageTrackedRenderer, ImportDma, ImportEgl},
2022-01-18 19:42:56 +01:00
winit::{self, WinitEvent, WinitGraphicsBackend, WinitVirtualDevice},
},
desktop::layer_map_for_output,
output::{Mode, Output, PhysicalProperties, Scale, Subpixel},
reexports::{
calloop::{ping, EventLoop},
wayland_server::DisplayHandle,
},
utils::Transform,
};
use std::cell::RefCell;
#[cfg(feature = "debug")]
2022-02-04 21:04:17 +01:00
use crate::state::Fps;
pub struct WinitState {
// The winit backend currently has no notion of multiple windows
pub backend: WinitGraphicsBackend,
output: Output,
2022-09-28 12:01:29 +02:00
damage_tracker: DamageTrackedRenderer,
#[cfg(feature = "debug")]
fps: Fps,
}
impl WinitState {
2022-01-18 19:42:56 +01:00
pub fn render_output(&mut self, state: &mut Common) -> Result<()> {
2022-07-04 16:00:29 +02:00
self.backend
.bind()
.with_context(|| "Failed to bind buffer")?;
2022-09-28 12:01:29 +02:00
let age = self.backend.buffer_age().unwrap_or(0);
2022-02-04 21:04:17 +01:00
match render::render_output(
None,
self.backend.renderer(),
2022-09-28 12:01:29 +02:00
&mut self.damage_tracker,
age,
2022-02-04 21:04:17 +01:00
state,
&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),
) {
Ok(damage) => {
2022-09-28 12:01:29 +02:00
state.send_frames(&self.output);
self.backend
.submit(damage.as_ref().map(|x| &**x))
.with_context(|| "Failed to submit buffer for display")?;
}
Err(err) => {
anyhow::bail!("Rendering failed: {}", err);
}
};
Ok(())
}
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.backend.window_size();
let mut config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
if dbg!(config.mode.0) != dbg!((size.physical_size.w as i32, size.physical_size.h as i32)) {
if !test_only {
config.mode = (
(size.physical_size.w as i32, size.physical_size.h as i32),
None,
);
}
Err(anyhow::anyhow!("Cannot set window size"))
} else {
Ok(())
}
}
}
2022-07-04 16:00:29 +02:00
pub fn init_backend(
dh: &DisplayHandle,
event_loop: &mut EventLoop<Data>,
state: &mut State,
) -> Result<()> {
let (mut backend, mut input) =
winit::init(None).map_err(|_| anyhow!("Failed to initilize winit backend"))?;
init_egl_client_side(dh, state, &mut backend)?;
2022-01-18 19:42:56 +01:00
let name = format!("WINIT-0");
let size = backend.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 = Output::new(name, props, None);
let _global = output.create_global::<State>(dh);
output.add_mode(mode);
output.set_preferred(mode);
2022-01-18 19:42:56 +01:00
output.change_current_state(
Some(mode),
Some(Transform::Flipped180),
Some(Scale::Integer(1)),
2022-01-18 19:42:56 +01:00
Some((0, 0).into()),
);
output.user_data().insert_if_missing(|| {
RefCell::new(OutputConfig {
mode: (
(size.physical_size.w as i32, size.physical_size.h as i32),
None,
),
transform: Transform::Flipped180.into(),
..Default::default()
})
});
2022-01-18 19:42:56 +01:00
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();
2022-01-18 19:42:56 +01:00
let mut token = Some(
event_loop
.handle()
.insert_source(render_source, move |_, _, data| {
2022-07-04 16:00:29 +02:00
if let Err(err) = data
.state
.backend
.winit()
.render_output(&mut data.state.common)
{
slog_scope::error!("Failed to render frame: {}", err);
render_ping.ping();
}
2022-01-18 19:42:56 +01:00
})
.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 |_, _, data| {
2022-07-04 16:00:29 +02:00
match input.dispatch_new_events(|event| {
2022-09-28 12:01:29 +02:00
data.state.process_winit_event(event, &render_ping_handle)
2022-07-04 16:00:29 +02:00
}) {
2022-01-18 19:42:56 +01:00
Ok(_) => {
event_ping_handle.ping();
render_ping_handle.ping();
}
Err(winit::WinitError::WindowClosed) => {
let output = data.state.backend.winit().output.clone();
data.state.common.shell.remove_output(&output);
2022-01-18 19:42:56 +01:00
if let Some(token) = token.take() {
event_loop_handle.remove(token);
}
2022-01-18 19:42:56 +01:00
}
};
})
.map_err(|_| anyhow::anyhow!("Failed to init eventloop timer for winit"))?;
event_ping.ping();
state.backend = BackendData::Winit(WinitState {
backend,
output: output.clone(),
2022-09-28 12:01:29 +02:00
damage_tracker: DamageTrackedRenderer::from_output(&output),
#[cfg(feature = "debug")]
fps: Fps::default(),
});
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));
2022-01-18 19:42:56 +01:00
Ok(())
}
2022-01-18 19:42:56 +01:00
fn init_egl_client_side(
dh: &DisplayHandle,
state: &mut State,
renderer: &mut WinitGraphicsBackend,
2022-01-18 19:42:56 +01:00
) -> Result<()> {
let bind_result = renderer.renderer().bind_wl_display(dh);
match bind_result {
Ok(_) => {
slog_scope::info!("EGL hardware-acceleration enabled");
let dmabuf_formats = renderer
.renderer()
.dmabuf_formats()
.cloned()
.collect::<Vec<_>>();
2022-07-04 16:00:29 +02:00
state
.common
.dmabuf_state
.create_global::<State, _>(dh, dmabuf_formats, None);
}
Err(err) => slog_scope::warn!("Unable to initialize bind display to EGL: {}", err),
};
Ok(())
}
impl State {
2022-09-28 12:01:29 +02:00
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) => {
2022-09-28 12:01:29 +02:00
for seat in self.common.seats().cloned().collect::<Vec<_>>().iter() {
let devices = seat.user_data().get::<Devices>().unwrap();
if devices.has_device(&WinitVirtualDevice) {
2022-09-28 12:01:29 +02:00
seat.set_active_output(&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,
};
{
let mut config = output
.user_data()
.get::<RefCell<OutputConfig>>()
.unwrap()
.borrow_mut();
config.mode.0 = size.into();
}
output.delete_mode(output.current_mode().unwrap());
output.set_preferred(mode);
output.change_current_state(Some(mode), None, None, None);
2022-09-28 12:01:29 +02:00
layer_map_for_output(output).arrange();
2022-07-04 16:00:29 +02:00
self.common.output_configuration_state.update();
2022-04-14 22:16:37 +02:00
self.common.shell.refresh_outputs();
render_ping.ping();
2022-01-18 19:42:56 +01:00
}
WinitEvent::Refresh => render_ping.ping(),
2022-08-31 13:01:23 +02:00
WinitEvent::Input(event) => self.process_input_event(event),
2022-01-18 19:42:56 +01:00
_ => {}
};
}
}