aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-12-20 08:49:18 +0300
committerIvan Molodetskikh <yalterz@gmail.com>2024-12-22 15:19:46 +0300
commitfbb0054232fb859636ea7e32bdc45c71e185f0c6 (patch)
tree0a36a33a9b38c91423854c17eb96ace9bd10f14a /src/backend
parent2d3c36edae2cbce10ec57995c5a4418fb2424bd2 (diff)
downloadniri-fbb0054232fb859636ea7e32bdc45c71e185f0c6.tar.gz
niri-fbb0054232fb859636ea7e32bdc45c71e185f0c6.tar.bz2
niri-fbb0054232fb859636ea7e32bdc45c71e185f0c6.zip
Add a Headless backend for tests
Rendering and stuff is unimplemented.
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/headless.rs139
-rw-r--r--src/backend/mod.rs28
2 files changed, 167 insertions, 0 deletions
diff --git a/src/backend/headless.rs b/src/backend/headless.rs
new file mode 100644
index 00000000..5b8c7428
--- /dev/null
+++ b/src/backend/headless.rs
@@ -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()
+ }
+}
diff --git a/src/backend/mod.rs b/src/backend/mod.rs
index 2213b8bd..9652eec9 100644
--- a/src/backend/mod.rs
+++ b/src/backend/mod.rs
@@ -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")
+ }
+ }
}