aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2023-08-07 19:44:40 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2023-08-10 14:49:38 +0400
commitad3c3f8cefd38d2bf26b466d8e34eccde3bca443 (patch)
tree5783df13fa895bb6a8244556fb31b6504312b82b /src
downloadniri-ad3c3f8cefd38d2bf26b466d8e34eccde3bca443.tar.gz
niri-ad3c3f8cefd38d2bf26b466d8e34eccde3bca443.tar.bz2
niri-ad3c3f8cefd38d2bf26b466d8e34eccde3bca443.zip
Init from smallvil
Diffstat (limited to 'src')
-rw-r--r--src/grabs/mod.rs5
-rw-r--r--src/grabs/move_grab.rs75
-rw-r--r--src/grabs/resize_grab.rs278
-rw-r--r--src/handlers/compositor.rs57
-rw-r--r--src/handlers/mod.rs54
-rw-r--r--src/handlers/xdg_shell.rs163
-rw-r--r--src/input.rs131
-rw-r--r--src/main.rs52
-rw-r--r--src/state.rs164
-rw-r--r--src/winit.rs139
10 files changed, 1118 insertions, 0 deletions
diff --git a/src/grabs/mod.rs b/src/grabs/mod.rs
new file mode 100644
index 00000000..5756e1be
--- /dev/null
+++ b/src/grabs/mod.rs
@@ -0,0 +1,5 @@
+pub mod move_grab;
+pub use move_grab::MoveSurfaceGrab;
+
+pub mod resize_grab;
+pub use resize_grab::ResizeSurfaceGrab;
diff --git a/src/grabs/move_grab.rs b/src/grabs/move_grab.rs
new file mode 100644
index 00000000..c9aacbec
--- /dev/null
+++ b/src/grabs/move_grab.rs
@@ -0,0 +1,75 @@
+use crate::Smallvil;
+use smithay::{
+ desktop::Window,
+ input::pointer::{
+ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
+ PointerInnerHandle, RelativeMotionEvent,
+ },
+ reexports::wayland_server::protocol::wl_surface::WlSurface,
+ utils::{Logical, Point},
+};
+
+pub struct MoveSurfaceGrab {
+ pub start_data: PointerGrabStartData<Smallvil>,
+ pub window: Window,
+ pub initial_window_location: Point<i32, Logical>,
+}
+
+impl PointerGrab<Smallvil> for MoveSurfaceGrab {
+ fn motion(
+ &mut self,
+ data: &mut Smallvil,
+ handle: &mut PointerInnerHandle<'_, Smallvil>,
+ _focus: Option<(WlSurface, Point<i32, Logical>)>,
+ event: &MotionEvent,
+ ) {
+ // While the grab is active, no client has pointer focus
+ handle.motion(data, None, event);
+
+ let delta = event.location - self.start_data.location;
+ let new_location = self.initial_window_location.to_f64() + delta;
+ data.space
+ .map_element(self.window.clone(), new_location.to_i32_round(), true);
+ }
+
+ fn relative_motion(
+ &mut self,
+ data: &mut Smallvil,
+ handle: &mut PointerInnerHandle<'_, Smallvil>,
+ focus: Option<(WlSurface, Point<i32, Logical>)>,
+ event: &RelativeMotionEvent,
+ ) {
+ handle.relative_motion(data, focus, event);
+ }
+
+ fn button(
+ &mut self,
+ data: &mut Smallvil,
+ handle: &mut PointerInnerHandle<'_, Smallvil>,
+ event: &ButtonEvent,
+ ) {
+ handle.button(data, event);
+
+ // The button is a button code as defined in the
+ // Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT.
+ const BTN_LEFT: u32 = 0x110;
+
+ if !handle.current_pressed().contains(&BTN_LEFT) {
+ // No more buttons are pressed, release the grab.
+ handle.unset_grab(data, event.serial, event.time);
+ }
+ }
+
+ fn axis(
+ &mut self,
+ data: &mut Smallvil,
+ handle: &mut PointerInnerHandle<'_, Smallvil>,
+ details: AxisFrame,
+ ) {
+ handle.axis(data, details)
+ }
+
+ fn start_data(&self) -> &PointerGrabStartData<Smallvil> {
+ &self.start_data
+ }
+}
diff --git a/src/grabs/resize_grab.rs b/src/grabs/resize_grab.rs
new file mode 100644
index 00000000..6a39cfcd
--- /dev/null
+++ b/src/grabs/resize_grab.rs
@@ -0,0 +1,278 @@
+use crate::Smallvil;
+use smithay::{
+ desktop::{Space, Window},
+ input::pointer::{
+ AxisFrame, ButtonEvent, GrabStartData as PointerGrabStartData, MotionEvent, PointerGrab,
+ PointerInnerHandle, RelativeMotionEvent,
+ },
+ reexports::{
+ wayland_protocols::xdg::shell::server::xdg_toplevel, wayland_server::protocol::wl_surface::WlSurface,
+ },
+ utils::{Logical, Point, Rectangle, Size},
+ wayland::{compositor, shell::xdg::SurfaceCachedState},
+};
+use std::cell::RefCell;
+
+bitflags::bitflags! {
+ #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+ pub struct ResizeEdge: u32 {
+ const TOP = 0b0001;
+ const BOTTOM = 0b0010;
+ const LEFT = 0b0100;
+ const RIGHT = 0b1000;
+
+ const TOP_LEFT = Self::TOP.bits() | Self::LEFT.bits();
+ const BOTTOM_LEFT = Self::BOTTOM.bits() | Self::LEFT.bits();
+
+ const TOP_RIGHT = Self::TOP.bits() | Self::RIGHT.bits();
+ const BOTTOM_RIGHT = Self::BOTTOM.bits() | Self::RIGHT.bits();
+ }
+}
+
+impl From<xdg_toplevel::ResizeEdge> for ResizeEdge {
+ #[inline]
+ fn from(x: xdg_toplevel::ResizeEdge) -> Self {
+ Self::from_bits(x as u32).unwrap()
+ }
+}
+
+pub struct ResizeSurfaceGrab {
+ start_data: PointerGrabStartData<Smallvil>,
+ window: Window,
+
+ edges: ResizeEdge,
+
+ initial_rect: Rectangle<i32, Logical>,
+ last_window_size: Size<i32, Logical>,
+}
+
+impl ResizeSurfaceGrab {
+ pub fn start(
+ start_data: PointerGrabStartData<Smallvil>,
+ window: Window,
+ edges: ResizeEdge,
+ initial_window_rect: Rectangle<i32, Logical>,
+ ) -> Self {
+ let initial_rect = initial_window_rect;
+
+ ResizeSurfaceState::with(window.toplevel().wl_surface(), |state| {
+ *state = ResizeSurfaceState::Resizing { edges, initial_rect };
+ });
+
+ Self {
+ start_data,
+ window,
+ edges,
+ initial_rect,
+ last_window_size: initial_rect.size,
+ }
+ }
+}
+
+impl PointerGrab<Smallvil> for ResizeSurfaceGrab {
+ fn motion(
+ &mut self,
+ data: &mut Smallvil,
+ handle: &mut PointerInnerHandle<'_, Smallvil>,
+ _focus: Option<(WlSurface, Point<i32, Logical>)>,
+ event: &MotionEvent,
+ ) {
+ // While the grab is active, no client has pointer focus
+ handle.motion(data, None, event);
+
+ let mut delta = event.location - self.start_data.location;
+
+ let mut new_window_width = self.initial_rect.size.w;
+ let mut new_window_height = self.initial_rect.size.h;
+
+ if self.edges.intersects(ResizeEdge::LEFT | ResizeEdge::RIGHT) {
+ if self.edges.intersects(ResizeEdge::LEFT) {
+ delta.x = -delta.x;
+ }
+
+ new_window_width = (self.initial_rect.size.w as f64 + delta.x) as i32;
+ }
+
+ if self.edges.intersects(ResizeEdge::TOP | ResizeEdge::BOTTOM) {
+ if self.edges.intersects(ResizeEdge::TOP) {
+ delta.y = -delta.y;
+ }
+
+ new_window_height = (self.initial_rect.size.h as f64 + delta.y) as i32;
+ }
+
+ let (min_size, max_size) = compositor::with_states(self.window.toplevel().wl_surface(), |states| {
+ let data = states.cached_state.current::<SurfaceCachedState>();
+ (data.min_size, data.max_size)
+ });
+
+ let min_width = min_size.w.max(1);
+ let min_height = min_size.h.max(1);
+
+ let max_width = (max_size.w == 0).then(i32::max_value).unwrap_or(max_size.w);
+ let max_height = (max_size.h == 0).then(i32::max_value).unwrap_or(max_size.h);
+
+ self.last_window_size = Size::from((
+ new_window_width.max(min_width).min(max_width),
+ new_window_height.max(min_height).min(max_height),
+ ));
+
+ let xdg = self.window.toplevel();
+ xdg.with_pending_state(|state| {
+ state.states.set(xdg_toplevel::State::Resizing);
+ state.size = Some(self.last_window_size);
+ });
+
+ xdg.send_pending_configure();
+ }
+
+ fn relative_motion(
+ &mut self,
+ data: &mut Smallvil,
+ handle: &mut PointerInnerHandle<'_, Smallvil>,
+ focus: Option<(WlSurface, Point<i32, Logical>)>,
+ event: &RelativeMotionEvent,
+ ) {
+ handle.relative_motion(data, focus, event);
+ }
+
+ fn button(
+ &mut self,
+ data: &mut Smallvil,
+ handle: &mut PointerInnerHandle<'_, Smallvil>,
+ event: &ButtonEvent,
+ ) {
+ handle.button(data, event);
+
+ // The button is a button code as defined in the
+ // Linux kernel's linux/input-event-codes.h header file, e.g. BTN_LEFT.
+ const BTN_LEFT: u32 = 0x110;
+
+ if !handle.current_pressed().contains(&BTN_LEFT) {
+ // No more buttons are pressed, release the grab.
+ handle.unset_grab(data, event.serial, event.time);
+
+ let xdg = self.window.toplevel();
+ xdg.with_pending_state(|state| {
+ state.states.unset(xdg_toplevel::State::Resizing);
+ state.size = Some(self.last_window_size);
+ });
+
+ xdg.send_pending_configure();
+
+ ResizeSurfaceState::with(xdg.wl_surface(), |state| {
+ *state = ResizeSurfaceState::WaitingForLastCommit {
+ edges: self.edges,
+ initial_rect: self.initial_rect,
+ };
+ });
+ }
+ }
+
+ fn axis(
+ &mut self,
+ data: &mut Smallvil,
+ handle: &mut PointerInnerHandle<'_, Smallvil>,
+ details: AxisFrame,
+ ) {
+ handle.axis(data, details)
+ }
+
+ fn start_data(&self) -> &PointerGrabStartData<Smallvil> {
+ &self.start_data
+ }
+}
+
+/// State of the resize operation.
+///
+/// It is stored inside of WlSurface,
+/// and can be accessed using [`ResizeSurfaceState::with`]
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
+enum ResizeSurfaceState {
+ #[default]
+ Idle,
+ Resizing {
+ edges: ResizeEdge,
+ /// The initial window size and location.
+ initial_rect: Rectangle<i32, Logical>,
+ },
+ /// Resize is done, we are now waiting for last commit, to do the final move
+ WaitingForLastCommit {
+ edges: ResizeEdge,
+ /// The initial window size and location.
+ initial_rect: Rectangle<i32, Logical>,
+ },
+}
+
+impl ResizeSurfaceState {
+ fn with<F, T>(surface: &WlSurface, cb: F) -> T
+ where
+ F: FnOnce(&mut Self) -> T,
+ {
+ compositor::with_states(surface, |states| {
+ states.data_map.insert_if_missing(RefCell::<Self>::default);
+ let state = states.data_map.get::<RefCell<Self>>().unwrap();
+
+ cb(&mut state.borrow_mut())
+ })
+ }
+
+ fn commit(&mut self) -> Option<(ResizeEdge, Rectangle<i32, Logical>)> {
+ match *self {
+ Self::Resizing { edges, initial_rect } => Some((edges, initial_rect)),
+ Self::WaitingForLastCommit { edges, initial_rect } => {
+ // The resize is done, let's go back to idle
+ *self = Self::Idle;
+
+ Some((edges, initial_rect))
+ }
+ Self::Idle => None,
+ }
+ }
+}
+
+/// Should be called on `WlSurface::commit`
+pub fn handle_commit(space: &mut Space<Window>, surface: &WlSurface) -> Option<()> {
+ let window = space
+ .elements()
+ .find(|w| w.toplevel().wl_surface() == surface)
+ .cloned()?;
+
+ let mut window_loc = space.element_location(&window)?;
+ let geometry = window.geometry();
+
+ let new_loc: Point<Option<i32>, Logical> = ResizeSurfaceState::with(surface, |state| {
+ state
+ .commit()
+ .and_then(|(edges, initial_rect)| {
+ // If the window is being resized by top or left, its location must be adjusted
+ // accordingly.
+ edges.intersects(ResizeEdge::TOP_LEFT).then(|| {
+ let new_x = edges
+ .intersects(ResizeEdge::LEFT)
+ .then_some(initial_rect.loc.x + (initial_rect.size.w - geometry.size.w));
+
+ let new_y = edges
+ .intersects(ResizeEdge::TOP)
+ .then_some(initial_rect.loc.y + (initial_rect.size.h - geometry.size.h));
+
+ (new_x, new_y).into()
+ })
+ })
+ .unwrap_or_default()
+ });
+
+ if let Some(new_x) = new_loc.x {
+ window_loc.x = new_x;
+ }
+ if let Some(new_y) = new_loc.y {
+ window_loc.y = new_y;
+ }
+
+ if new_loc.x.is_some() || new_loc.y.is_some() {
+ // If TOP or LEFT side of the window got resized, we have to move it
+ space.map_element(window, window_loc, false);
+ }
+
+ Some(())
+}
diff --git a/src/handlers/compositor.rs b/src/handlers/compositor.rs
new file mode 100644
index 00000000..fcb0dc02
--- /dev/null
+++ b/src/handlers/compositor.rs
@@ -0,0 +1,57 @@
+use crate::{grabs::resize_grab, state::ClientState, Smallvil};
+use smithay::{
+ backend::renderer::utils::on_commit_buffer_handler,
+ delegate_compositor, delegate_shm,
+ reexports::wayland_server::{
+ protocol::{wl_buffer, wl_surface::WlSurface},
+ Client,
+ },
+ wayland::{
+ buffer::BufferHandler,
+ compositor::{
+ get_parent, is_sync_subsurface, CompositorClientState, CompositorHandler, CompositorState,
+ },
+ shm::{ShmHandler, ShmState},
+ },
+};
+
+use super::xdg_shell;
+
+impl CompositorHandler for Smallvil {
+ fn compositor_state(&mut self) -> &mut CompositorState {
+ &mut self.compositor_state
+ }
+
+ fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
+ &client.get_data::<ClientState>().unwrap().compositor_state
+ }
+
+ fn commit(&mut self, surface: &WlSurface) {
+ on_commit_buffer_handler::<Self>(surface);
+ if !is_sync_subsurface(surface) {
+ let mut root = surface.clone();
+ while let Some(parent) = get_parent(&root) {
+ root = parent;
+ }
+ if let Some(window) = self.space.elements().find(|w| w.toplevel().wl_surface() == &root) {
+ window.on_commit();
+ }
+ };
+
+ xdg_shell::handle_commit(&self.space, surface);
+ resize_grab::handle_commit(&mut self.space, surface);
+ }
+}
+
+impl BufferHandler for Smallvil {
+ fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
+}
+
+impl ShmHandler for Smallvil {
+ fn shm_state(&self) -> &ShmState {
+ &self.shm_state
+ }
+}
+
+delegate_compositor!(Smallvil);
+delegate_shm!(Smallvil);
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
new file mode 100644
index 00000000..bc2cd23f
--- /dev/null
+++ b/src/handlers/mod.rs
@@ -0,0 +1,54 @@
+mod compositor;
+mod xdg_shell;
+
+use crate::Smallvil;
+
+//
+// Wl Seat
+//
+
+use smithay::input::{SeatHandler, SeatState};
+use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
+use smithay::wayland::data_device::{ClientDndGrabHandler, DataDeviceHandler, ServerDndGrabHandler};
+use smithay::{delegate_data_device, delegate_output, delegate_seat};
+
+impl SeatHandler for Smallvil {
+ type KeyboardFocus = WlSurface;
+ type PointerFocus = WlSurface;
+
+ fn seat_state(&mut self) -> &mut SeatState<Smallvil> {
+ &mut self.seat_state
+ }
+
+ fn cursor_image(
+ &mut self,
+ _seat: &smithay::input::Seat<Self>,
+ _image: smithay::input::pointer::CursorImageStatus,
+ ) {
+ }
+ fn focus_changed(&mut self, _seat: &smithay::input::Seat<Self>, _focused: Option<&WlSurface>) {}
+}
+
+delegate_seat!(Smallvil);
+
+//
+// Wl Data Device
+//
+
+impl DataDeviceHandler for Smallvil {
+ type SelectionUserData = ();
+ fn data_device_state(&self) -> &smithay::wayland::data_device::DataDeviceState {
+ &self.data_device_state
+ }
+}
+
+impl ClientDndGrabHandler for Smallvil {}
+impl ServerDndGrabHandler for Smallvil {}
+
+delegate_data_device!(Smallvil);
+
+//
+// Wl Output & Xdg Output
+//
+
+delegate_output!(Smallvil);
diff --git a/src/handlers/xdg_shell.rs b/src/handlers/xdg_shell.rs
new file mode 100644
index 00000000..7351d3bf
--- /dev/null
+++ b/src/handlers/xdg_shell.rs
@@ -0,0 +1,163 @@
+use smithay::{
+ delegate_xdg_shell,
+ desktop::{Space, Window},
+ input::{
+ pointer::{Focus, GrabStartData as PointerGrabStartData},
+ Seat,
+ },
+ reexports::{
+ wayland_protocols::xdg::shell::server::xdg_toplevel,
+ wayland_server::{
+ protocol::{wl_seat, wl_surface::WlSurface},
+ Resource,
+ },
+ },
+ utils::{Rectangle, Serial},
+ wayland::{
+ compositor::with_states,
+ shell::xdg::{
+ PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
+ XdgToplevelSurfaceData,
+ },
+ },
+};
+
+use crate::{
+ grabs::{MoveSurfaceGrab, ResizeSurfaceGrab},
+ Smallvil,
+};
+
+impl XdgShellHandler for Smallvil {
+ fn xdg_shell_state(&mut self) -> &mut XdgShellState {
+ &mut self.xdg_shell_state
+ }
+
+ fn new_toplevel(&mut self, surface: ToplevelSurface) {
+ let window = Window::new(surface);
+ self.space.map_element(window, (0, 0), false);
+ }
+
+ fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) {
+ // TODO: Popup handling using PopupManager
+ }
+
+ fn move_request(&mut self, surface: ToplevelSurface, seat: wl_seat::WlSeat, serial: Serial) {
+ let seat = Seat::from_resource(&seat).unwrap();
+
+ let wl_surface = surface.wl_surface();
+
+ if let Some(start_data) = check_grab(&seat, wl_surface, serial) {
+ let pointer = seat.get_pointer().unwrap();
+
+ let window = self
+ .space
+ .elements()
+ .find(|w| w.toplevel().wl_surface() == wl_surface)
+ .unwrap()
+ .clone();
+ let initial_window_location = self.space.element_location(&window).unwrap();
+
+ let grab = MoveSurfaceGrab {
+ start_data,
+ window,
+ initial_window_location,
+ };
+
+ pointer.set_grab(self, grab, serial, Focus::Clear);
+ }
+ }
+
+ fn resize_request(
+ &mut self,
+ surface: ToplevelSurface,
+ seat: wl_seat::WlSeat,
+ serial: Serial,
+ edges: xdg_toplevel::ResizeEdge,
+ ) {
+ let seat = Seat::from_resource(&seat).unwrap();
+
+ let wl_surface = surface.wl_surface();
+
+ if let Some(start_data) = check_grab(&seat, wl_surface, serial) {
+ let pointer = seat.get_pointer().unwrap();
+
+ let window = self
+ .space
+ .elements()
+ .find(|w| w.toplevel().wl_surface() == wl_surface)
+ .unwrap()
+ .clone();
+ let initial_window_location = self.space.element_location(&window).unwrap();
+ let initial_window_size = window.geometry().size;
+
+ surface.with_pending_state(|state| {
+ state.states.set(xdg_toplevel::State::Resizing);
+ });
+
+ surface.send_pending_configure();
+
+ let grab = ResizeSurfaceGrab::start(
+ start_data,
+ window,
+ edges.into(),
+ Rectangle::from_loc_and_size(initial_window_location, initial_window_size),
+ );
+
+ pointer.set_grab(self, grab, serial, Focus::Clear);
+ }
+ }
+
+ fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
+ // TODO popup grabs
+ }
+}
+
+// Xdg Shell
+delegate_xdg_shell!(Smallvil);
+
+fn check_grab(
+ seat: &Seat<Smallvil>,
+ surface: &WlSurface,
+ serial: Serial,
+) -> Option<PointerGrabStartData<Smallvil>> {
+ let pointer = seat.get_pointer()?;
+
+ // Check that this surface has a click grab.
+ if !pointer.has_grab(serial) {
+ return None;
+ }
+
+ let start_data = pointer.grab_start_data()?;
+
+ let (focus, _) = start_data.focus.as_ref()?;
+ // If the focus was for a different surface, ignore the request.
+ if !focus.id().same_client_as(&surface.id()) {
+ return None;
+ }
+
+ Some(start_data)
+}
+
+/// Should be called on `WlSurface::commit`
+pub fn handle_commit(space: &Space<Window>, surface: &WlSurface) -> Option<()> {
+ let window = space
+ .elements()
+ .find(|w| w.toplevel().wl_surface() == surface)
+ .cloned()?;
+
+ let initial_configure_sent = with_states(surface, |states| {
+ states
+ .data_map
+ .get::<XdgToplevelSurfaceData>()
+ .unwrap()
+ .lock()
+ .unwrap()
+ .initial_configure_sent
+ });
+
+ if !initial_configure_sent {
+ window.toplevel().send_configure();
+ }
+
+ Some(())
+}
diff --git a/src/input.rs b/src/input.rs
new file mode 100644
index 00000000..0531c29b
--- /dev/null
+++ b/src/input.rs
@@ -0,0 +1,131 @@
+use smithay::{
+ backend::input::{
+ AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event, InputBackend, InputEvent,
+ KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent,
+ },
+ input::{
+ keyboard::FilterResult,
+ pointer::{AxisFrame, ButtonEvent, MotionEvent},
+ },
+ reexports::wayland_server::protocol::wl_surface::WlSurface,
+ utils::SERIAL_COUNTER,
+};
+
+use crate::state::Smallvil;
+
+impl Smallvil {
+ pub fn process_input_event<I: InputBackend>(&mut self, event: InputEvent<I>) {
+ match event {
+ InputEvent::Keyboard { event, .. } => {
+ let serial = SERIAL_COUNTER.next_serial();
+ let time = Event::time_msec(&event);
+
+ self.seat.get_keyboard().unwrap().input::<(), _>(
+ self,
+ event.key_code(),
+ event.state(),
+ serial,
+ time,
+ |_, _, _| FilterResult::Forward,
+ );
+ }
+ InputEvent::PointerMotion { .. } => {}
+ InputEvent::PointerMotionAbsolute { event, .. } => {
+ let output = self.space.outputs().next().unwrap();
+
+ let output_geo = self.space.output_geometry(output).unwrap();
+
+ let pos = event.position_transformed(output_geo.size) + output_geo.loc.to_f64();
+
+ let serial = SERIAL_COUNTER.next_serial();
+
+ let pointer = self.seat.get_pointer().unwrap();
+
+ let under = self.surface_under_pointer(&pointer);
+
+ pointer.motion(
+ self,
+ under,
+ &MotionEvent {
+ location: pos,
+ serial,
+ time: event.time_msec(),
+ },
+ );
+ }
+ InputEvent::PointerButton { event, .. } => {
+ let pointer = self.seat.get_pointer().unwrap();
+ let keyboard = self.seat.get_keyboard().unwrap();
+
+ let serial = SERIAL_COUNTER.next_serial();
+
+ let button = event.button_code();
+
+ let button_state = event.state();
+
+ if ButtonState::Pressed == button_state && !pointer.is_grabbed() {
+ if let Some((window, _loc)) = self
+ .space
+ .element_under(pointer.current_location())
+ .map(|(w, l)| (w.clone(), l))
+ {
+ self.space.raise_element(&window, true);
+ keyboard.set_focus(self, Some(window.toplevel().wl_surface().clone()), serial);
+ self.space.elements().for_each(|window| {
+ window.toplevel().send_pending_configure();
+ });
+ } else {
+ self.space.elements().for_each(|window| {
+ window.set_activated(false);
+ window.toplevel().send_pending_configure();
+ });
+ keyboard.set_focus(self, Option::<WlSurface>::None, serial);
+ }
+ };
+
+ pointer.button(
+ self,
+ &ButtonEvent {
+ button,
+ state: button_state,
+ serial,
+ time: event.time_msec(),
+ },
+ );
+ }
+ InputEvent::PointerAxis { event, .. } => {
+ let source = event.source();
+
+ let horizontal_amount = event
+ .amount(Axis::Horizontal)
+ .unwrap_or_else(|| event.amount_discrete(Axis::Horizontal).unwrap() * 3.0);
+ let vertical_amount = event
+ .amount(Axis::Vertical)
+ .unwrap_or_else(|| event.amount_discrete(Axis::Vertical).unwrap() * 3.0);
+ let horizontal_amount_discrete = event.amount_discrete(Axis::Horizontal);
+ let vertical_amount_discrete = event.amount_discrete(Axis::Vertical);
+
+ let mut frame = AxisFrame::new(event.time_msec()).source(source);
+ if horizontal_amount != 0.0 {
+ frame = frame.value(Axis::Horizontal, horizontal_amount);
+ if let Some(discrete) = horizontal_amount_discrete {
+ frame = frame.discrete(Axis::Horizontal, discrete as i32);
+ }
+ } else if source == AxisSource::Finger {
+ frame = frame.stop(Axis::Horizontal);
+ }
+ if vertical_amount != 0.0 {
+ frame = frame.value(Axis::Vertical, vertical_amount);
+ if let Some(discrete) = vertical_amount_discrete {
+ frame = frame.discrete(Axis::Vertical, discrete as i32);
+ }
+ } else if source == AxisSource::Finger {
+ frame = frame.stop(Axis::Vertical);
+ }
+
+ self.seat.get_pointer().unwrap().axis(self, frame);
+ }
+ _ => {}
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 00000000..9d472a7d
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,52 @@
+#![allow(irrefutable_let_patterns)]
+
+mod handlers;
+
+mod grabs;
+mod input;
+mod state;
+mod winit;
+
+use smithay::reexports::{calloop::EventLoop, wayland_server::Display};
+pub use state::Smallvil;
+
+pub struct CalloopData {
+ state: Smallvil,
+ display: Display<Smallvil>,
+}
+
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+ if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
+ tracing_subscriber::fmt().with_env_filter(env_filter).init();
+ } else {
+ tracing_subscriber::fmt().init();
+ }
+
+ let mut event_loop: EventLoop<CalloopData> = EventLoop::try_new()?;
+
+ let mut display: Display<Smallvil> = Display::new()?;
+ let state = Smallvil::new(&mut event_loop, &mut display);
+
+ let mut data = CalloopData { state, display };
+
+ crate::winit::init_winit(&mut event_loop, &mut data)?;
+
+ let mut args = std::env::args().skip(1);
+ let flag = args.next();
+ let arg = args.next();
+
+ 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();
+ }
+ }
+
+ event_loop.run(None, &mut data, move |_| {
+ // Smallvil is running
+ })?;
+
+ Ok(())
+}
diff --git a/src/state.rs b/src/state.rs
new file mode 100644
index 00000000..a0405b77
--- /dev/null
+++ b/src/state.rs
@@ -0,0 +1,164 @@
+use std::{ffi::OsString, os::unix::io::AsRawFd, sync::Arc};
+
+use smithay::{
+ desktop::{Space, Window, WindowSurfaceType},
+ input::{pointer::PointerHandle, Seat, SeatState},
+ reexports::{
+ calloop::{generic::Generic, EventLoop, Interest, LoopSignal, Mode, PostAction},
+ wayland_server::{
+ backend::{ClientData, ClientId, DisconnectReason},
+ protocol::wl_surface::WlSurface,
+ Display,
+ },
+ },
+ utils::{Logical, Point},
+ wayland::{
+ compositor::{CompositorClientState, CompositorState},
+ data_device::DataDeviceState,
+ output::OutputManagerState,
+ shell::xdg::XdgShellState,
+ shm::ShmState,
+ 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: &mut 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: &mut 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,
+ Mod