Add a Headless backend for tests

Rendering and stuff is unimplemented.
This commit is contained in:
Ivan Molodetskikh
2024-12-20 08:49:18 +03:00
parent 2d3c36edae
commit fbb0054232
4 changed files with 174 additions and 2 deletions
+139
View File
@@ -0,0 +1,139 @@
//! Headless backend for tests.
//!
//! This can eventually grow into a more complete backend if needed, but for now it's missing some
//! crucial parts like rendering.
use std::mem;
use std::sync::{Arc, Mutex};
use niri_config::OutputName;
use smithay::backend::allocator::dmabuf::Dmabuf;
use smithay::backend::renderer::element::RenderElementStates;
use smithay::backend::renderer::gles::GlesRenderer;
use smithay::output::{Mode, Output, PhysicalProperties, Subpixel};
use smithay::reexports::wayland_protocols::wp::presentation_time::server::wp_presentation_feedback;
use smithay::utils::Size;
use super::{IpcOutputMap, OutputId, RenderResult};
use crate::niri::{Niri, RedrawState};
use crate::utils::{get_monotonic_time, logical_output};
pub struct Headless {
ipc_outputs: Arc<Mutex<IpcOutputMap>>,
}
impl Headless {
pub fn new() -> Self {
Self {
ipc_outputs: Default::default(),
}
}
pub fn init(&mut self, _niri: &mut Niri) {}
pub fn add_output(&mut self, niri: &mut Niri, n: u8, size: (u16, u16)) {
let connector = format!("headless-{n}");
let make = "niri".to_string();
let model = "headless".to_string();
let serial = n.to_string();
let output = Output::new(
connector.clone(),
PhysicalProperties {
size: (0, 0).into(),
subpixel: Subpixel::Unknown,
make: make.clone(),
model: model.clone(),
},
);
let mode = Mode {
size: Size::from((i32::from(size.0), i32::from(size.1))),
refresh: 60_000,
};
output.change_current_state(Some(mode), None, None, None);
output.set_preferred(mode);
output.user_data().insert_if_missing(|| OutputName {
connector,
make: Some(make),
model: Some(model),
serial: Some(serial),
});
let physical_properties = output.physical_properties();
self.ipc_outputs.lock().unwrap().insert(
OutputId::next(),
niri_ipc::Output {
name: output.name(),
make: physical_properties.make,
model: physical_properties.model,
serial: None,
physical_size: None,
modes: vec![niri_ipc::Mode {
width: size.0,
height: size.1,
refresh_rate: 60_000,
is_preferred: true,
}],
current_mode: Some(0),
vrr_supported: false,
vrr_enabled: false,
logical: Some(logical_output(&output)),
},
);
niri.add_output(output, None, false);
}
pub fn seat_name(&self) -> String {
"headless".to_owned()
}
pub fn with_primary_renderer<T>(
&mut self,
_f: impl FnOnce(&mut GlesRenderer) -> T,
) -> Option<T> {
None
}
pub fn render(&mut self, niri: &mut Niri, output: &Output) -> RenderResult {
let states = RenderElementStates::default();
let mut presentation_feedbacks = niri.take_presentation_feedbacks(output, &states);
presentation_feedbacks.presented::<_, smithay::utils::Monotonic>(
get_monotonic_time(),
std::time::Duration::ZERO,
0,
wp_presentation_feedback::Kind::empty(),
);
let output_state = niri.output_state.get_mut(output).unwrap();
match mem::replace(&mut output_state.redraw_state, RedrawState::Idle) {
RedrawState::Idle => unreachable!(),
RedrawState::Queued => (),
RedrawState::WaitingForVBlank { .. } => unreachable!(),
RedrawState::WaitingForEstimatedVBlank(_) => unreachable!(),
RedrawState::WaitingForEstimatedVBlankAndQueued(_) => unreachable!(),
}
output_state.frame_callback_sequence = output_state.frame_callback_sequence.wrapping_add(1);
// FIXME: request redraw on unfinished animations remain
RenderResult::Submitted
}
pub fn import_dmabuf(&mut self, _dmabuf: &Dmabuf) -> bool {
unimplemented!()
}
pub fn ipc_outputs(&self) -> Arc<Mutex<IpcOutputMap>> {
self.ipc_outputs.clone()
}
}
impl Default for Headless {
fn default() -> Self {
Self::new()
}
}
+28
View File
@@ -17,9 +17,13 @@ pub use tty::Tty;
pub mod winit;
pub use winit::Winit;
pub mod headless;
pub use headless::Headless;
pub enum Backend {
Tty(Tty),
Winit(Winit),
Headless(Headless),
}
#[derive(PartialEq, Eq)]
@@ -54,6 +58,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.init(niri),
Backend::Winit(winit) => winit.init(niri),
Backend::Headless(headless) => headless.init(niri),
}
}
@@ -61,6 +66,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.seat_name(),
Backend::Winit(winit) => winit.seat_name(),
Backend::Headless(headless) => headless.seat_name(),
}
}
@@ -71,6 +77,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.with_primary_renderer(f),
Backend::Winit(winit) => winit.with_primary_renderer(f),
Backend::Headless(headless) => headless.with_primary_renderer(f),
}
}
@@ -83,6 +90,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.render(niri, output, target_presentation_time),
Backend::Winit(winit) => winit.render(niri, output),
Backend::Headless(headless) => headless.render(niri, output),
}
}
@@ -90,6 +98,7 @@ impl Backend {
match self {
Backend::Tty(_) => CompositorMod::Super,
Backend::Winit(_) => CompositorMod::Alt,
Backend::Headless(_) => CompositorMod::Super,
}
}
@@ -97,6 +106,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.change_vt(vt),
Backend::Winit(_) => (),
Backend::Headless(_) => (),
}
}
@@ -104,6 +114,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.suspend(),
Backend::Winit(_) => (),
Backend::Headless(_) => (),
}
}
@@ -111,6 +122,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.toggle_debug_tint(),
Backend::Winit(winit) => winit.toggle_debug_tint(),
Backend::Headless(_) => (),
}
}
@@ -118,6 +130,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.import_dmabuf(dmabuf),
Backend::Winit(winit) => winit.import_dmabuf(dmabuf),
Backend::Headless(headless) => headless.import_dmabuf(dmabuf),
}
}
@@ -125,6 +138,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.early_import(surface),
Backend::Winit(_) => (),
Backend::Headless(_) => (),
}
}
@@ -132,6 +146,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.ipc_outputs(),
Backend::Winit(winit) => winit.ipc_outputs(),
Backend::Headless(headless) => headless.ipc_outputs(),
}
}
@@ -143,6 +158,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.primary_gbm_device(),
Backend::Winit(_) => None,
Backend::Headless(_) => None,
}
}
@@ -150,6 +166,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.set_monitors_active(active),
Backend::Winit(_) => (),
Backend::Headless(_) => (),
}
}
@@ -157,6 +174,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.set_output_on_demand_vrr(niri, output, enable_vrr),
Backend::Winit(_) => (),
Backend::Headless(_) => (),
}
}
@@ -164,6 +182,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.on_output_config_changed(niri),
Backend::Winit(_) => (),
Backend::Headless(_) => (),
}
}
@@ -171,6 +190,7 @@ impl Backend {
match self {
Backend::Tty(tty) => tty.on_debug_config_changed(),
Backend::Winit(_) => (),
Backend::Headless(_) => (),
}
}
@@ -197,4 +217,12 @@ impl Backend {
panic!("backend is not Winit")
}
}
pub fn headless(&mut self) -> &mut Headless {
if let Self::Headless(v) = self {
v
} else {
panic!("backend is not Headless")
}
}
}
+1
View File
@@ -176,6 +176,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
event_loop.handle(),
event_loop.get_signal(),
display,
false,
)
.unwrap();
+6 -2
View File
@@ -102,7 +102,7 @@ use smithay::wayland::xdg_foreign::XdgForeignState;
use crate::animation::Clock;
use crate::backend::tty::SurfaceDmabufFeedback;
use crate::backend::{Backend, RenderResult, Tty, Winit};
use crate::backend::{Backend, Headless, RenderResult, Tty, Winit};
use crate::cursor::{CursorManager, CursorTextureCache, RenderCursor, XCursor};
#[cfg(feature = "dbus")]
use crate::dbus::gnome_shell_introspect::{self, IntrospectToNiri, NiriToIntrospect};
@@ -520,6 +520,7 @@ impl State {
event_loop: LoopHandle<'static, State>,
stop_signal: LoopSignal,
display: Display<State>,
headless: bool,
) -> Result<Self, Box<dyn std::error::Error>> {
let _span = tracy_client::span!("State::new");
@@ -528,7 +529,10 @@ impl State {
let has_display =
env::var_os("WAYLAND_DISPLAY").is_some() || env::var_os("DISPLAY").is_some();
let mut backend = if has_display {
let mut backend = if headless {
let headless = Headless::new();
Backend::Headless(headless)
} else if has_display {
let winit = Winit::new(config.clone(), event_loop.clone())?;
Backend::Winit(winit)
} else {