aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend.rs15
-rw-r--r--src/grabs/move_grab.rs24
-rw-r--r--src/grabs/resize_grab.rs26
-rw-r--r--src/handlers/compositor.rs14
-rw-r--r--src/handlers/mod.rs18
-rw-r--r--src/handlers/xdg_shell.rs10
-rw-r--r--src/input.rs34
-rw-r--r--src/main.rs123
-rw-r--r--src/niri.rs168
-rw-r--r--src/state.rs165
-rw-r--r--src/tty.rs423
-rw-r--r--src/winit.rs225
12 files changed, 876 insertions, 369 deletions
diff --git a/src/backend.rs b/src/backend.rs
new file mode 100644
index 00000000..e190ed05
--- /dev/null
+++ b/src/backend.rs
@@ -0,0 +1,15 @@
+use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
+use smithay::backend::renderer::gles::GlesRenderer;
+use smithay::desktop::space::SpaceRenderElements;
+
+use crate::Niri;
+
+pub trait Backend {
+ fn seat_name(&self) -> String;
+ fn renderer(&mut self) -> &mut GlesRenderer;
+ fn render(
+ &mut self,
+ niri: &mut Niri,
+ elements: &[SpaceRenderElements<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>],
+ );
+}
diff --git a/src/grabs/move_grab.rs b/src/grabs/move_grab.rs
index 63cdb3bd..699921d1 100644
--- a/src/grabs/move_grab.rs
+++ b/src/grabs/move_grab.rs
@@ -6,19 +6,19 @@ use smithay::input::pointer::{
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::{Logical, Point};
-use crate::Smallvil;
+use crate::Niri;
pub struct MoveSurfaceGrab {
- pub start_data: PointerGrabStartData<Smallvil>,
+ pub start_data: PointerGrabStartData<Niri>,
pub window: Window,
pub initial_window_location: Point<i32, Logical>,
}
-impl PointerGrab<Smallvil> for MoveSurfaceGrab {
+impl PointerGrab<Niri> for MoveSurfaceGrab {
fn motion(
&mut self,
- data: &mut Smallvil,
- handle: &mut PointerInnerHandle<'_, Smallvil>,
+ data: &mut Niri,
+ handle: &mut PointerInnerHandle<'_, Niri>,
_focus: Option<(WlSurface, Point<i32, Logical>)>,
event: &MotionEvent,
) {
@@ -33,8 +33,8 @@ impl PointerGrab<Smallvil> for MoveSurfaceGrab {
fn relative_motion(
&mut self,
- data: &mut Smallvil,
- handle: &mut PointerInnerHandle<'_, Smallvil>,
+ data: &mut Niri,
+ handle: &mut PointerInnerHandle<'_, Niri>,
focus: Option<(WlSurface, Point<i32, Logical>)>,
event: &RelativeMotionEvent,
) {
@@ -43,8 +43,8 @@ impl PointerGrab<Smallvil> for MoveSurfaceGrab {
fn button(
&mut self,
- data: &mut Smallvil,
- handle: &mut PointerInnerHandle<'_, Smallvil>,
+ data: &mut Niri,
+ handle: &mut PointerInnerHandle<'_, Niri>,
event: &ButtonEvent,
) {
handle.button(data, event);
@@ -61,14 +61,14 @@ impl PointerGrab<Smallvil> for MoveSurfaceGrab {
fn axis(
&mut self,
- data: &mut Smallvil,
- handle: &mut PointerInnerHandle<'_, Smallvil>,
+ data: &mut Niri,
+ handle: &mut PointerInnerHandle<'_, Niri>,
details: AxisFrame,
) {
handle.axis(data, details)
}
- fn start_data(&self) -> &PointerGrabStartData<Smallvil> {
+ fn start_data(&self) -> &PointerGrabStartData<Niri> {
&self.start_data
}
}
diff --git a/src/grabs/resize_grab.rs b/src/grabs/resize_grab.rs
index ee8aae5d..2bc2f194 100644
--- a/src/grabs/resize_grab.rs
+++ b/src/grabs/resize_grab.rs
@@ -11,7 +11,7 @@ use smithay::utils::{Logical, Point, Rectangle, Size};
use smithay::wayland::compositor;
use smithay::wayland::shell::xdg::SurfaceCachedState;
-use crate::Smallvil;
+use crate::Niri;
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -37,7 +37,7 @@ impl From<xdg_toplevel::ResizeEdge> for ResizeEdge {
}
pub struct ResizeSurfaceGrab {
- start_data: PointerGrabStartData<Smallvil>,
+ start_data: PointerGrabStartData<Niri>,
window: Window,
edges: ResizeEdge,
@@ -48,7 +48,7 @@ pub struct ResizeSurfaceGrab {
impl ResizeSurfaceGrab {
pub fn start(
- start_data: PointerGrabStartData<Smallvil>,
+ start_data: PointerGrabStartData<Niri>,
window: Window,
edges: ResizeEdge,
initial_window_rect: Rectangle<i32, Logical>,
@@ -72,11 +72,11 @@ impl ResizeSurfaceGrab {
}
}
-impl PointerGrab<Smallvil> for ResizeSurfaceGrab {
+impl PointerGrab<Niri> for ResizeSurfaceGrab {
fn motion(
&mut self,
- data: &mut Smallvil,
- handle: &mut PointerInnerHandle<'_, Smallvil>,
+ data: &mut Niri,
+ handle: &mut PointerInnerHandle<'_, Niri>,
_focus: Option<(WlSurface, Point<i32, Logical>)>,
event: &MotionEvent,
) {
@@ -132,8 +132,8 @@ impl PointerGrab<Smallvil> for ResizeSurfaceGrab {
fn relative_motion(
&mut self,
- data: &mut Smallvil,
- handle: &mut PointerInnerHandle<'_, Smallvil>,
+ data: &mut Niri,
+ handle: &mut PointerInnerHandle<'_, Niri>,
focus: Option<(WlSurface, Point<i32, Logical>)>,
event: &RelativeMotionEvent,
) {
@@ -142,8 +142,8 @@ impl PointerGrab<Smallvil> for ResizeSurfaceGrab {
fn button(
&mut self,
- data: &mut Smallvil,
- handle: &mut PointerInnerHandle<'_, Smallvil>,
+ data: &mut Niri,
+ handle: &mut PointerInnerHandle<'_, Niri>,
event: &ButtonEvent,
) {
handle.button(data, event);
@@ -175,14 +175,14 @@ impl PointerGrab<Smallvil> for ResizeSurfaceGrab {
fn axis(
&mut self,
- data: &mut Smallvil,
- handle: &mut PointerInnerHandle<'_, Smallvil>,
+ data: &mut Niri,
+ handle: &mut PointerInnerHandle<'_, Niri>,
details: AxisFrame,
) {
handle.axis(data, details)
}
- fn start_data(&self) -> &PointerGrabStartData<Smallvil> {
+ fn start_data(&self) -> &PointerGrabStartData<Niri> {
&self.start_data
}
}
diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs
index 99041f34..b5f6291d 100644
--- a/src/handlers/compositor.rs
+++ b/src/handlers/compositor.rs
@@ -11,10 +11,10 @@ use smithay::{delegate_compositor, delegate_shm};
use super::xdg_shell;
use crate::grabs::resize_grab;
-use crate::state::ClientState;
-use crate::Smallvil;
+use crate::niri::ClientState;
+use crate::Niri;
-impl CompositorHandler for Smallvil {
+impl CompositorHandler for Niri {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
@@ -44,15 +44,15 @@ impl CompositorHandler for Smallvil {
}
}
-impl BufferHandler for Smallvil {
+impl BufferHandler for Niri {
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
}
-impl ShmHandler for Smallvil {
+impl ShmHandler for Niri {
fn shm_state(&self) -> &ShmState {
&self.shm_state
}
}
-delegate_compositor!(Smallvil);
-delegate_shm!(Smallvil);
+delegate_compositor!(Niri);
+delegate_shm!(Niri);
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
index 58157b27..ab2a1596 100644
--- a/src/handlers/mod.rs
+++ b/src/handlers/mod.rs
@@ -10,13 +10,13 @@ use smithay::wayland::data_device::{
};
use smithay::{delegate_data_device, delegate_output, delegate_seat};
-use crate::Smallvil;
+use crate::Niri;
-impl SeatHandler for Smallvil {
+impl SeatHandler for Niri {
type KeyboardFocus = WlSurface;
type PointerFocus = WlSurface;
- fn seat_state(&mut self) -> &mut SeatState<Smallvil> {
+ fn seat_state(&mut self) -> &mut SeatState<Niri> {
&mut self.seat_state
}
@@ -29,26 +29,26 @@ impl SeatHandler for Smallvil {
fn focus_changed(&mut self, _seat: &smithay::input::Seat<Self>, _focused: Option<&WlSurface>) {}
}
-delegate_seat!(Smallvil);
+delegate_seat!(Niri);
//
// Wl Data Device
//
-impl DataDeviceHandler for Smallvil {
+impl DataDeviceHandler for Niri {
type SelectionUserData = ();
fn data_device_state(&self) -> &smithay::wayland::data_device::DataDeviceState {
&self.data_device_state
}
}
-impl ClientDndGrabHandler for Smallvil {}
-impl ServerDndGrabHandler for Smallvil {}
+impl ClientDndGrabHandler for Niri {}
+impl ServerDndGrabHandler for Niri {}
-delegate_data_device!(Smallvil);
+delegate_data_device!(Niri);
//
// Wl Output & Xdg Output
//
-delegate_output!(Smallvil);
+delegate_output!(Niri);
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs
index 3b7f53ff..e681d0ef 100644
--- a/src/handlers/xdg_shell.rs
+++ b/src/handlers/xdg_shell.rs
@@ -14,9 +14,9 @@ use smithay::wayland::shell::xdg::{
};
use crate::grabs::{MoveSurfaceGrab, ResizeSurfaceGrab};
-use crate::Smallvil;
+use crate::Niri;
-impl XdgShellHandler for Smallvil {
+impl XdgShellHandler for Niri {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.xdg_shell_state
}
@@ -102,13 +102,13 @@ impl XdgShellHandler for Smallvil {
}
// Xdg Shell
-delegate_xdg_shell!(Smallvil);
+delegate_xdg_shell!(Niri);
fn check_grab(
- seat: &Seat<Smallvil>,
+ seat: &Seat<Niri>,
surface: &WlSurface,
serial: Serial,
-) -> Option<PointerGrabStartData<Smallvil>> {
+) -> Option<PointerGrabStartData<Niri>> {
let pointer = seat.get_pointer()?;
// Check that this surface has a click grab.
diff --git a/src/input.rs b/src/input.rs
index 9fd894b5..9d167a0a 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -2,28 +2,52 @@ use smithay::backend::input::{
AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
};
-use smithay::input::keyboard::FilterResult;
+use smithay::input::keyboard::{keysyms, FilterResult};
use smithay::input::pointer::{AxisFrame, ButtonEvent, MotionEvent};
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
use smithay::utils::SERIAL_COUNTER;
-use crate::state::Smallvil;
+use crate::niri::Niri;
-impl Smallvil {
+enum InputAction {
+ Quit,
+ ChangeVt(i32),
+}
+
+impl Niri {
pub fn process_input_event<I: InputBackend>(&mut self, event: InputEvent<I>) {
+ trace!("process_input_event");
+
match event {
InputEvent::Keyboard { event, .. } => {
let serial = SERIAL_COUNTER.next_serial();
let time = Event::time_msec(&event);
- self.seat.get_keyboard().unwrap().input::<(), _>(
+ let action = self.seat.get_keyboard().unwrap().input(
self,
event.key_code(),
event.state(),
serial,
time,
- |_, _, _| FilterResult::Forward,
+ |_, _, keysym| match keysym.modified_sym() {
+ keysyms::KEY_Escape => FilterResult::Intercept(InputAction::Quit),
+ keysym @ keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12 => {
+ let vt = (keysym - keysyms::KEY_XF86Switch_VT_1 + 1) as i32;
+ FilterResult::Intercept(InputAction::ChangeVt(vt))
+ }
+ _ => FilterResult::Forward,
+ },
);
+
+ if let Some(action) = action {
+ match action {
+ InputAction::Quit => {
+ info!("quitting because Esc was pressed");
+ self.stop_signal.stop()
+ }
+ InputAction::ChangeVt(vt) => todo!(),
+ }
+ }
}
InputEvent::PointerMotion { .. } => {}
InputEvent::PointerMotionAbsolute { event, .. } => {
diff --git a/src/main.rs b/src/main.rs
index 3e285536..7a977bd0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,54 +1,109 @@
+#[macro_use]
+extern crate tracing;
+
mod handlers;
+mod backend;
mod grabs;
mod input;
-mod state;
+mod niri;
+mod tty;
mod winit;
+use std::env;
+use std::ffi::OsString;
+
+use backend::Backend;
+use clap::Parser;
+use niri::Niri;
use smithay::reexports::calloop::EventLoop;
-use smithay::reexports::wayland_server::Display;
-pub use state::Smallvil;
+use smithay::reexports::wayland_server::{Display, DisplayHandle};
+use tracing_subscriber::EnvFilter;
+use tty::Tty;
+use winit::Winit;
-pub struct CalloopData {
- state: Smallvil,
- display: Display<Smallvil>,
+#[derive(Parser)]
+#[command(author, version, about, long_about = None)]
+struct Cli {
+ #[arg(last = true)]
+ command: Option<OsString>,
}
-fn main() -> Result<(), Box<dyn std::error::Error>> {
- if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
- tracing_subscriber::fmt()
- .compact()
- .with_env_filter(env_filter)
- .init();
- } else {
- tracing_subscriber::fmt().compact().init();
- }
+pub struct LoopData {
+ niri: Niri,
+ display_handle: DisplayHandle,
- let mut event_loop: EventLoop<CalloopData> = EventLoop::try_new()?;
+ // Last so that it's dropped after the Smithay state in Niri.
+ display: Display<Niri>,
- let mut display: Display<Smallvil> = Display::new()?;
- let state = Smallvil::new(&event_loop, &mut display);
+ tty: Option<Tty>,
+ winit: Option<Winit>,
+}
+
+fn main() {
+ env::set_var("RUST_BACKTRACE", "1");
- let mut data = CalloopData { state, display };
+ let directives = env::var("RUST_LOG").unwrap_or_else(|_| "niri=debug,info".to_owned());
+ let env_filter = EnvFilter::builder().parse_lossy(directives);
+ tracing_subscriber::fmt()
+ .compact()
+ .with_env_filter(env_filter)
+ .init();
- crate::winit::init_winit(&event_loop, &mut data)?;
+ let cli = Cli::parse();
- let mut args = std::env::args().skip(1);
- let flag = args.next();
- let arg = args.next();
+ let mut event_loop = EventLoop::try_new().unwrap();
- match (flag.as_deref(), arg) {
- (Some("-c") | Some("--command"), Some(command)) => {
- std::process::Command::new(command).spawn().ok();
- }
- _ => {
- std::process::Command::new("weston-terminal").spawn().ok();
- }
+ let has_display = env::var_os("WAYLAND_DISPLAY").is_some() || env::var_os("DISPLAY").is_some();
+
+ let mut winit = None;
+ let mut tty = None;
+ let backend: &mut dyn Backend = if has_display {
+ winit = Some(Winit::new(event_loop.handle()));
+ winit.as_mut().unwrap()
+ } else {
+ tty = Some(Tty::new(event_loop.handle()));
+ tty.as_mut().unwrap()
+ };
+
+ let mut display = Display::new().unwrap();
+ let display_handle = display.handle();
+ let niri = Niri::new(
+ event_loop.handle(),
+ event_loop.get_signal(),
+ &mut display,
+ backend.seat_name(),
+ );
+
+ let mut data = LoopData {
+ niri,
+ display_handle,
+ display,
+
+ tty,
+ winit,
+ };
+
+ if let Some(tty) = data.tty.as_mut() {
+ tty.init(&mut data.niri);
+ }
+ if let Some(winit) = data.winit.as_mut() {
+ winit.init(&mut data.niri);
}
- event_loop.run(None, &mut data, move |_| {
- // Smallvil is running
- })?;
+ let res = if let Some(command) = &cli.command {
+ std::process::Command::new(command).spawn()
+ } else {
+ std::process::Command::new("weston-terminal").spawn()
+ };
+ if let Err(err) = res {
+ warn!("error spawning command: {err}");
+ }
- Ok(())
+ event_loop
+ .run(None, &mut data, move |data| {
+ // niri is running.
+ data.display.flush_clients().unwrap();
+ })
+ .unwrap();
}
diff --git a/src/niri.rs b/src/niri.rs
new file mode 100644
index 00000000..1750ed3d
--- /dev/null
+++ b/src/niri.rs
@@ -0,0 +1,168 @@
+use std::os::unix::io::AsRawFd;
+use std::sync::Arc;
+use std::time::Duration;
+
+use smithay::desktop::space::space_render_elements;
+use smithay::desktop::{Space, Window, WindowSurfaceType};
+use smithay::input::keyboard::XkbConfig;
+use smithay::input::pointer::PointerHandle;
+use smithay::input::{Seat, SeatState};
+use smithay::output::Output;
+use smithay::reexports::calloop::generic::Generic;
+use smithay::reexports::calloop::{Interest, LoopHandle, LoopSignal, Mode, PostAction};
+use smithay::reexports::wayland_server::backend::{ClientData, ClientId, DisconnectReason};
+use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
+use smithay::reexports::wayland_server::{Display, DisplayHandle};
+use smithay::utils::{Logical, Point};
+use smithay::wayland::compositor::{CompositorClientState, CompositorState};
+use smithay::wayland::data_device::DataDeviceState;
+use smithay::wayland::output::OutputManagerState;
+use smithay::wayland::shell::xdg::XdgShellState;
+use smithay::wayland::shm::ShmState;
+use smithay::wayland::socket::ListeningSocketSource;
+
+use crate::backend::Backend;
+use crate::LoopData;
+
+pub struct Niri {
+ pub start_time: std::time::Instant,
+ pub event_loop: LoopHandle<'static, LoopData>,
+ pub stop_signal: LoopSignal,
+ pub display_handle: DisplayHandle,
+
+ pub space: Space<Window>,
+
+ // Smithay state.
+ pub compositor_state: CompositorState,
+ pub xdg_shell_state: XdgShellState,
+ pub shm_state: ShmState,
+ pub output_manager_state: OutputManagerState,
+ pub seat_state: SeatState<Self>,
+ pub data_device_state: DataDeviceState,
+
+ pub seat: Seat<Self>,
+ pub output: Option<Output>,
+}
+
+impl Niri {
+ pub fn new(
+ event_loop: LoopHandle<'static, LoopData>,
+ stop_signal: LoopSignal,
+ display: &mut Display<Self>,
+ seat_name: String,
+ ) -> Self {
+ let start_time = std::time::Instant::now();
+
+ let display_handle = display.handle();
+
+ let compositor_state = CompositorState::new::<Self>(&display_handle);
+ let xdg_shell_state = XdgShellState::new::<Self>(&display_handle);
+ let shm_state = ShmState::new::<Self>(&display_handle, vec![]);
+ let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle);
+ let mut seat_state = SeatState::new();
+ let data_device_state = DataDeviceState::new::<Self>(&display_handle);
+
+ let mut seat: Seat<Self> = seat_state.new_wl_seat(&display_handle, seat_name);
+ // FIXME: get Xkb and repeat interval from GNOME dconf.
+ seat.add_keyboard(XkbConfig::default(), 400, 30).unwrap();
+ seat.add_pointer();
+
+ let space = Space::default();
+
+ let socket_source = ListeningSocketSource::new_auto().unwrap();
+ let socket_name = socket_source.socket_name().to_os_string();
+ event_loop
+ .insert_source(socket_source, move |client, _, data| {
+ if let Err(err) = data
+ .display_handle
+ .insert_client(client, Arc::new(ClientState::default()))
+ {
+ error!("error inserting client: {err}");
+ }
+ })
+ .unwrap();
+ std::env::set_var("WAYLAND_DISPLAY", &socket_name);
+ info!(
+ "listening on Wayland socket: {}",
+ socket_name.to_string_lossy()
+ );
+
+ let display_source = Generic::new(
+ display.backend().poll_fd().as_raw_fd(),
+ Interest::READ,
+ Mode::Level,
+ );
+ event_loop
+ .insert_source(display_source, |_, _, data| {
+ data.display.dispatch_clients(&mut data.niri).unwrap();
+ Ok(PostAction::Continue)
+ })
+ .unwrap();
+
+ Self {
+ start_time,
+ event_loop,
+ stop_signal,
+ display_handle,
+
+ space,
+
+ compositor_state,
+ xdg_shell_state,
+ shm_state,
+ output_manager_state,
+ seat_state,
+ data_device_state,
+
+ seat,
+ output: None,
+ }
+ }
+
+ pub fn surface_under_pointer(
+ &self,
+ pointer: &PointerHandle<Self>,
+ ) -> Option<(WlSurface, Point<i32, Logical>)> {
+ let pos = pointer.current_location();
+ self.space
+ .element_under(pos)
+ .and_then(|(window, location)| {
+ window
+ .surface_under(pos - location.to_f64(), WindowSurfaceType::ALL)
+ .map(|(s, p)| (s, p + location))
+ })
+ }
+
+ pub fn redraw(&mut self, backend: &mut dyn Backend) {
+ let elements = space_render_elements(
+ backend.renderer(),
+ [&self.space],
+ self.output.as_ref().unwrap(),
+ 1.,
+ )
+ .unwrap();
+ backend.render(self, &elements);
+
+ let output = self.output.as_ref().unwrap();
+ self.space.elements().for_each(|window| {
+ window.send_frame(
+ output,
+ self.start_time.elapsed(),
+ Some(Duration::ZERO),
+ |_, _| Some(output.clone()),
+ )
+ });
+
+ self.space.refresh();
+ }
+}
+
+#[derive(Default)]
+pub struct ClientState {
+ pub compositor_state: CompositorClientState,
+}
+
+impl ClientData for ClientState {
+ fn initialized(&self, _client_id: ClientId) {}
+ fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}
+}
diff --git a/src/state.rs b/src/state.rs
deleted file mode 100644
index 15bdf3a0..00000000
--- a/src/state.rs
+++ /dev/null
@@ -1,165 +0,0 @@
-use std::ffi::OsString;
-use std::os::unix::io::AsRawFd;
-use std::sync::Arc;
-
-use smithay::desktop::{Space, Window, WindowSurfaceType};
-use smithay::input::pointer::PointerHandle;
-use smithay::input::{Seat, SeatState};
-use smithay::reexports::calloop::generic::Generic;
-use smithay::reexports::calloop::{EventLoop, Interest, LoopSignal, Mode, PostAction};
-use smithay::reexports::wayland_server::backend::{ClientData, ClientId, DisconnectReason};
-use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
-use smithay::reexports::wayland_server::Display;
-use smithay::utils::{Logical, Point};
-use smithay::wayland::compositor::{CompositorClientState, CompositorState};
-use smithay::wayland::data_device::DataDeviceState;
-use smithay::wayland::output::OutputManagerState;
-use smithay::wayland::shell::xdg::XdgShellState;
-use smithay::wayland::shm::ShmState;
-use smithay::wayland::socket::ListeningSocketSource;
-
-use crate::CalloopData;
-
-pub struct Smallvil {
- pub start_time: std::time::Instant,
- pub socket_name: OsString,
-
- pub space: Space<Window>,
- pub loop_signal: LoopSignal,
-
- // Smithay State
- pub compositor_state: CompositorState,
- pub xdg_shell_state: XdgShellState,
- pub shm_state: ShmState,
- pub output_manager_state: OutputManagerState,
- pub seat_state: SeatState<Smallvil>,
- pub data_device_state: DataDeviceState,
-
- pub seat: Seat<Self>,
-}
-
-impl Smallvil {
- pub fn new(event_loop: &EventLoop<CalloopData>, display: &mut Display<Self>) -> Self {
- let start_time = std::time::Instant::now();
-
- let dh = display.handle();
-
- let compositor_state = CompositorState::new::<Self>(&dh);
- let xdg_shell_state = XdgShellState::new::<Self>(&dh);
- let shm_state = ShmState::new::<Self>(&dh, vec![]);
- let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&dh);
- let mut seat_state = SeatState::new();
- let data_device_state = DataDeviceState::new::<Self>(&dh);
-
- // A seat is a group of keyboards, pointer and touch devices.
- // A seat typically has a pointer and maintains a keyboard focus and a pointer focus.
- let mut seat: Seat<Self> = seat_state.new_wl_seat(&dh, "winit");
-
- // Notify clients that we have a keyboard, for the sake of the example we assume that
- // keyboard is always present. You may want to track keyboard hot-plug in real
- // compositor.
- seat.add_keyboard(Default::default(), 200, 200).unwrap();
-
- // Notify clients that we have a pointer (mouse)
- // Here we assume that there is always pointer plugged in
- seat.add_pointer();
-
- // A space represents a two-dimensional plane. Windows and Outputs can be mapped onto it.
- //
- // Windows get a position and stacking order through mapping.
- // Outputs become views of a part of the Space and can be rendered via Space::render_output.
- let space = Space::default();
-
- let socket_name = Self::init_wayland_listener(display, event_loop);
-
- // Get the loop signal, used to stop the event loop
- let loop_signal = event_loop.get_signal();
-
- Self {
- start_time,
-
- space,
- loop_signal,
- socket_name,
-
- compositor_state,
- xdg_shell_state,
- shm_state,
- output_manager_state,
- seat_state,
- data_device_state,
- seat,
- }
- }
-
- fn init_wayland_listener(
- display: &mut Display<Smallvil>,
- event_loop: &EventLoop<CalloopData>,
- ) -> OsString {
- // Creates a new listening socket, automatically choosing the next available `wayland`
- // socket name.
- let listening_socket = ListeningSocketSource::new_auto().unwrap();
-
- // Get the name of the listening socket.
- // Clients will connect to this socket.
- let socket_name = listening_socket.socket_name().to_os_string();
-
- let handle = event_loop.handle();
-
- event_loop
- .handle()
- .insert_source(listening_socket, move |client_stream, _, state| {
- // Inside the callback, you should insert the client into the display.
- //
- // You may also associate some data with the client when inserting the client.
- state
- .display
- .handle()
- .insert_client(client_stream, Arc::new(ClientState::default()))
- .unwrap();
- })
- .expect("Failed to init the wayland event source.");
-
- // You also need to add the display itself to the event loop, so that client events will be
- // processed by wayland-server.
- handle
- .insert_source(
- Generic::new(
- display.backend().poll_fd().as_raw_fd(),
- Interest::READ,
- Mode::Level,
- ),
- |_, _, state| {
- state.display.dispatch_clients(&mut state.state).unwrap();
- Ok(PostAction::Continue)
- },
- )
- .unwrap();
-
- socket_name
- }
-
- pub fn surface_under_pointer(
- &self,
- pointer: &PointerHandle<Self>,
- ) -> Option<(WlSurface, Point<i32, Logical>)> {
- let pos = pointer.current_location();
- self.space
- .element_under(pos)
- .and_then(|(window, location)| {
- window
- .surface_under(pos - location.to_f64(), WindowSurfaceType::ALL)
- .map(|(s, p)| (s, p + location))
- })
- }
-}
-
-#[derive(Default)]
-pub struct ClientState {
- pub compositor_state: CompositorClientState,
-}
-
-impl ClientData for ClientState {
- fn initialized(&self, _client_id: ClientId) {}
- fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {}
-}
diff --git a/src/tty.rs b/src/tty.rs
new file mode 100644
index 00000000..1f0b72c3
--- /dev/null
+++ b/src/tty.rs
@@ -0,0 +1,423 @@
+use std::os::fd::FromRawFd;
+use std::path::PathBuf;
+use std::time::Duration;
+
+use anyhow::anyhow;
+use smithay::backend::allocator::dmabuf::Dmabuf;
+use smithay::backend::allocator::gbm::{GbmAllocator, GbmBufferFlags, GbmDevice};
+use smithay::backend::allocator::Fourcc;
+use smithay::backend::drm::compositor::DrmCompositor;
+use smithay::backend::drm::{DrmDevice, DrmDeviceFd, DrmEvent};
+use smithay::backend::egl::{EGLContext, EGLDisplay};
+use smithay::backend::libinput::{LibinputInputBackend, LibinputSessionInterface};
+use smithay::backend::renderer::element::surface::WaylandSurfaceRenderElement;
+use smithay::backend::renderer::gles::{GlesRenderbuffer, GlesRenderer};
+use smithay::backend::renderer::{Bind, ImportEgl};
+use smithay::backend::session::libseat::LibSeatSession;
+use smithay::backend::session::{Event as SessionEvent, Session};
+use smithay::backend::udev::{self, UdevBackend, UdevEvent};
+use smithay::desktop::space::SpaceRenderElements;
+use smithay::output::{Mode, Output, OutputModeSource, PhysicalProperties, Subpixel};
+use smithay::reexports::calloop::timer::{TimeoutAction, Timer};
+use smithay::reexports::calloop::{LoopHandle, RegistrationToken};
+use smithay::reexports::drm::control::connector::{
+ Interface as ConnectorInterface, State as ConnectorState,
+};
+use smithay::reexports::drm::control::{Device, ModeTypeFlags};
+use smithay::reexports::input::Libinput;
+use smithay::reexports::nix::fcntl::OFlag;
+use smithay::reexports::nix::libc::dev_t;
+use smithay::utils::DeviceFd;
+use smithay_drm_extras::edid::EdidInfo;
+
+use crate::backend::Backend;
+use crate::{LoopData, Niri};
+
+const SUPPORTED_COLOR_FORMATS: &[Fourcc] = &[Fourcc::Argb8888, Fourcc::Abgr8888];
+
+pub struct Tty {
+ session: LibSeatSession,
+ primary_gpu_path: PathBuf,
+ output_device: Option<OutputDevice>,
+}
+
+type GbmDrmCompositor =
+ DrmCompositor<GbmAllocator<DrmDeviceFd>, GbmDevice<DrmDeviceFd>, (), DrmDeviceFd>;
+
+struct OutputDevice {
+ id: dev_t,
+ path: PathBuf,
+ token: RegistrationToken,
+ drm: DrmDevice,
+ gles: GlesRenderer,
+ drm_compositor: GbmDrmCompositor,
+}
+
+impl Backend for Tty {
+ fn seat_name(&self) -> String {
+ self.session.seat()
+ }
+
+ fn renderer(&mut self) -> &mut GlesRenderer {
+ &mut self.output_device.as_mut().unwrap().gles
+ }
+
+ fn render(
+ &mut self,
+ niri: &mut Niri,
+ elements: &[SpaceRenderElements<GlesRenderer, WaylandSurfaceRenderElement<GlesRenderer>>],
+ ) {
+ let output_device = self.output_device.as_mut().unwrap();
+ let res = output_device
+ .drm_compositor
+ .render_frame::<_, _, GlesRenderbuffer>(
+ &mut output_device.gles,
+ elements,
+ [0.1, 0.1, 0.1, 1.],
+ )
+ .unwrap();
+ assert!(!res.needs_sync());
+ if res.damage.is_some() {
+ output_device.drm_compositor.queue_frame(()).unwrap();
+ } else {
+ niri.event_loop
+ .insert_source(
+ Timer::from_duration(Duration::from_millis(6)),
+ |_, _, data| {
+ data.niri.redraw(data.tty.as_mut().unwrap());
+ TimeoutAction::Drop
+ },
+ )
+ .unwrap();
+ }
+ }
+}
+
+impl Tty {
+ pub fn new(event_loop: LoopHandle<LoopData>) -> Self {
+ let (session, notifier) = LibSeatSession::new().unwrap();
+ let seat_name = session.seat();
+
+ let mut libinput = Libinput::new_with_udev(LibinputSessionInterface::from(session.clone()));
+ libinput.udev_assign_seat(&seat_name).unwrap();
+
+ let input_backend = LibinputInputBackend::new(libinput.clone());
+ event_loop
+ .insert_source(input_backend, |event, _, data| {
+ data.niri.process_input_event(event)
+ })
+ .unwrap();
+
+ event_loop
+ .insert_source(notifier, move |event, _, data| {
+ let tty = data.tty.as_mut().unwrap();
+ let niri = &mut data.niri;
+
+ match event {
+ SessionEvent::PauseSession => {
+ libinput.suspend();
+
+ if let Some(output_device) = &tty.output_device {
+ output_device.drm.pause();
+ }
+ }
+ SessionEvent::Activa