feat: add nested Phase 0 compositor
This commit is contained in:
1 parent
aab447ef25
commit
475d433ec4
8 files changed
+2710
-2
No files matched your search
Generated
+2091
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,59 @@
|
|||||||
|
use smithay::{
|
||||||
|
backend::renderer::utils::on_commit_buffer_handler,
|
||||||
|
reexports::wayland_server::{
|
||||||
|
Client,
|
||||||
|
protocol::{wl_buffer, wl_surface::WlSurface},
|
||||||
|
},
|
||||||
|
wayland::{
|
||||||
|
buffer::BufferHandler,
|
||||||
|
compositor::{
|
||||||
|
CompositorClientState, CompositorHandler, CompositorState, get_parent,
|
||||||
|
is_sync_subsurface,
|
||||||
|
},
|
||||||
|
shm::{ShmHandler, ShmState},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::xdg_shell;
|
||||||
|
use crate::{Ferese, state::ClientState};
|
||||||
|
|
||||||
|
impl CompositorHandler for Ferese {
|
||||||
|
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>()
|
||||||
|
.expect("all clients have Ferese client state")
|
||||||
|
.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(|window| {
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.is_some_and(|toplevel| toplevel.wl_surface() == &root)
|
||||||
|
}) {
|
||||||
|
window.on_commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xdg_shell::handle_commit(&mut self.popups, &self.space, surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferHandler for Ferese {
|
||||||
|
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShmHandler for Ferese {
|
||||||
|
fn shm_state(&self) -> &ShmState {
|
||||||
|
&self.shm_state
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
mod compositor;
|
||||||
|
mod xdg_shell;
|
||||||
|
|
||||||
|
use smithay::{
|
||||||
|
input::{Seat, SeatHandler, SeatState, pointer::CursorImageStatus},
|
||||||
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
|
wayland::output::OutputHandler,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Ferese;
|
||||||
|
|
||||||
|
impl SeatHandler for Ferese {
|
||||||
|
type KeyboardFocus = WlSurface;
|
||||||
|
type PointerFocus = WlSurface;
|
||||||
|
type TouchFocus = WlSurface;
|
||||||
|
|
||||||
|
fn seat_state(&mut self) -> &mut SeatState<Self> {
|
||||||
|
&mut self.seat_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cursor_image(&mut self, _seat: &Seat<Self>, _image: CursorImageStatus) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputHandler for Ferese {}
|
||||||
|
|
||||||
|
smithay::delegate_compositor!(Ferese);
|
||||||
|
smithay::delegate_output!(Ferese);
|
||||||
|
smithay::delegate_seat!(Ferese);
|
||||||
|
smithay::delegate_shm!(Ferese);
|
||||||
|
smithay::delegate_xdg_shell!(Ferese);
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
use smithay::{
|
||||||
|
desktop::{
|
||||||
|
PopupKind, PopupManager, Space, Window, find_popup_root_surface, get_popup_toplevel_coords,
|
||||||
|
},
|
||||||
|
reexports::wayland_server::protocol::{wl_seat, wl_surface::WlSurface},
|
||||||
|
utils::Serial,
|
||||||
|
wayland::{
|
||||||
|
compositor::with_states,
|
||||||
|
shell::xdg::{
|
||||||
|
PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState,
|
||||||
|
XdgToplevelSurfaceData,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Ferese;
|
||||||
|
|
||||||
|
impl XdgShellHandler for Ferese {
|
||||||
|
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
|
||||||
|
&mut self.xdg_shell_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_toplevel(&mut self, surface: ToplevelSurface) {
|
||||||
|
self.space
|
||||||
|
.map_element(Window::new_wayland_window(surface), (0, 0), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_popup(&mut self, surface: PopupSurface, _positioner: PositionerState) {
|
||||||
|
self.unconstrain_popup(&surface);
|
||||||
|
if let Err(error) = self.popups.track_popup(PopupKind::Xdg(surface)) {
|
||||||
|
tracing::warn!(?error, "failed to track xdg popup");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reposition_request(
|
||||||
|
&mut self,
|
||||||
|
surface: PopupSurface,
|
||||||
|
positioner: PositionerState,
|
||||||
|
token: u32,
|
||||||
|
) {
|
||||||
|
surface.with_pending_state(|state| {
|
||||||
|
state.geometry = positioner.get_geometry();
|
||||||
|
state.positioner = positioner;
|
||||||
|
});
|
||||||
|
self.unconstrain_popup(&surface);
|
||||||
|
surface.send_repositioned(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_request(&mut self, _: ToplevelSurface, _: wl_seat::WlSeat, _: Serial) {}
|
||||||
|
|
||||||
|
fn resize_request(
|
||||||
|
&mut self,
|
||||||
|
_: ToplevelSurface,
|
||||||
|
_: wl_seat::WlSeat,
|
||||||
|
_: Serial,
|
||||||
|
_: smithay::reexports::wayland_protocols::xdg::shell::server::xdg_toplevel::ResizeEdge,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn grab(&mut self, _: PopupSurface, _: wl_seat::WlSeat, _: Serial) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_commit(popups: &mut PopupManager, space: &Space<Window>, surface: &WlSurface) {
|
||||||
|
if let Some(window) = space
|
||||||
|
.elements()
|
||||||
|
.find(|window| {
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.is_some_and(|toplevel| toplevel.wl_surface() == surface)
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
{
|
||||||
|
let configured = with_states(surface, |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<XdgToplevelSurfaceData>()
|
||||||
|
.expect("xdg toplevel state exists")
|
||||||
|
.lock()
|
||||||
|
.expect("xdg toplevel state is not poisoned")
|
||||||
|
.initial_configure_sent
|
||||||
|
});
|
||||||
|
if !configured {
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.expect("mapped window has a toplevel")
|
||||||
|
.send_configure();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
popups.commit(surface);
|
||||||
|
if let Some(PopupKind::Xdg(popup)) = popups.find_popup(surface)
|
||||||
|
&& !popup.is_initial_configure_sent()
|
||||||
|
{
|
||||||
|
popup
|
||||||
|
.send_configure()
|
||||||
|
.expect("initial popup configure is valid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ferese {
|
||||||
|
fn unconstrain_popup(&self, popup: &PopupSurface) {
|
||||||
|
let Ok(root) = find_popup_root_surface(&PopupKind::Xdg(popup.clone())) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(window) = self.space.elements().find(|window| {
|
||||||
|
window
|
||||||
|
.toplevel()
|
||||||
|
.is_some_and(|toplevel| toplevel.wl_surface() == &root)
|
||||||
|
}) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(output) = self.space.outputs().next() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(output_geometry) = self.space.output_geometry(output) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(window_geometry) = self.space.element_geometry(window) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut target = output_geometry;
|
||||||
|
target.loc -= get_popup_toplevel_coords(&PopupKind::Xdg(popup.clone()));
|
||||||
|
target.loc -= window_geometry.loc;
|
||||||
|
popup.with_pending_state(|state| {
|
||||||
|
state.geometry = state.positioner.get_unconstrained_geometry(target);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
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, Serial},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Ferese;
|
||||||
|
|
||||||
|
impl Ferese {
|
||||||
|
pub fn process_input_event<I: InputBackend>(&mut self, event: InputEvent<I>) {
|
||||||
|
match event {
|
||||||
|
InputEvent::Keyboard { event, .. } => {
|
||||||
|
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
||||||
|
keyboard.input::<(), _>(
|
||||||
|
self,
|
||||||
|
event.key_code(),
|
||||||
|
event.state(),
|
||||||
|
SERIAL_COUNTER.next_serial(),
|
||||||
|
Event::time(&event) as u32,
|
||||||
|
|_, _, _| FilterResult::Forward,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
InputEvent::PointerMotionAbsolute { event, .. } => {
|
||||||
|
let Some(output) = self.space.outputs().next() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(geometry) = self.space.output_geometry(output) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let position = event.position_transformed(geometry.size) + geometry.loc.to_f64();
|
||||||
|
let pointer = self.seat.get_pointer().expect("seat has a pointer");
|
||||||
|
pointer.motion(
|
||||||
|
self,
|
||||||
|
self.surface_under(position),
|
||||||
|
&MotionEvent {
|
||||||
|
location: position,
|
||||||
|
serial: SERIAL_COUNTER.next_serial(),
|
||||||
|
time: event.time() as u32,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
pointer.frame(self);
|
||||||
|
}
|
||||||
|
InputEvent::PointerButton { event, .. } => {
|
||||||
|
let pointer = self.seat.get_pointer().expect("seat has a pointer");
|
||||||
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
if event.state() == ButtonState::Pressed && !pointer.is_grabbed() {
|
||||||
|
self.focus_window_under_pointer(serial);
|
||||||
|
}
|
||||||
|
pointer.button(
|
||||||
|
self,
|
||||||
|
&ButtonEvent {
|
||||||
|
button: event.button_code(),
|
||||||
|
state: event.state(),
|
||||||
|
serial,
|
||||||
|
time: event.time() as u32,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
pointer.frame(self);
|
||||||
|
}
|
||||||
|
InputEvent::PointerAxis { event, .. } => {
|
||||||
|
let source = event.source();
|
||||||
|
let horizontal = event.amount(Axis::Horizontal).unwrap_or_else(|| {
|
||||||
|
event.amount_v120(Axis::Horizontal).unwrap_or(0.0) * 15.0 / 120.0
|
||||||
|
});
|
||||||
|
let vertical = event.amount(Axis::Vertical).unwrap_or_else(|| {
|
||||||
|
event.amount_v120(Axis::Vertical).unwrap_or(0.0) * 15.0 / 120.0
|
||||||
|
});
|
||||||
|
let mut frame = AxisFrame::new(event.time() as u32).source(source);
|
||||||
|
if horizontal != 0.0 {
|
||||||
|
frame = frame.value(Axis::Horizontal, horizontal);
|
||||||
|
if let Some(value) = event.amount_v120(Axis::Horizontal) {
|
||||||
|
frame = frame.v120(Axis::Horizontal, value as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vertical != 0.0 {
|
||||||
|
frame = frame.value(Axis::Vertical, vertical);
|
||||||
|
if let Some(value) = event.amount_v120(Axis::Vertical) {
|
||||||
|
frame = frame.v120(Axis::Vertical, value as i32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if source == AxisSource::Finger {
|
||||||
|
if event.amount(Axis::Horizontal) == Some(0.0) {
|
||||||
|
frame = frame.stop(Axis::Horizontal);
|
||||||
|
}
|
||||||
|
if event.amount(Axis::Vertical) == Some(0.0) {
|
||||||
|
frame = frame.stop(Axis::Vertical);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let pointer = self.seat.get_pointer().expect("seat has a pointer");
|
||||||
|
pointer.axis(self, frame);
|
||||||
|
pointer.frame(self);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn focus_window_under_pointer(&mut self, serial: Serial) {
|
||||||
|
let pointer = self.seat.get_pointer().expect("seat has a pointer");
|
||||||
|
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
|
||||||
|
if let Some((window, _)) = self.space.element_under(pointer.current_location()) {
|
||||||
|
let window = window.clone();
|
||||||
|
self.space.raise_element(&window, true);
|
||||||
|
let surface = window
|
||||||
|
.toplevel()
|
||||||
|
.expect("mapped window has a toplevel")
|
||||||
|
.wl_surface()
|
||||||
|
.clone();
|
||||||
|
keyboard.set_focus(self, Some(surface), serial);
|
||||||
|
} else {
|
||||||
|
keyboard.set_focus(self, Option::<WlSurface>::None, serial);
|
||||||
|
}
|
||||||
|
self.space.elements().for_each(|window| {
|
||||||
|
if let Some(toplevel) = window.toplevel() {
|
||||||
|
toplevel.send_pending_configure();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
-2
@@ -1,3 +1,49 @@
|
|||||||
fn main() {
|
#![allow(irrefutable_let_patterns)]
|
||||||
println!("Ferese Phase 0 scaffold");
|
|
||||||
|
mod handlers;
|
||||||
|
mod input;
|
||||||
|
mod state;
|
||||||
|
mod winit;
|
||||||
|
|
||||||
|
use std::{error::Error, process::Command};
|
||||||
|
|
||||||
|
use smithay::reexports::{calloop::EventLoop, wayland_server::Display};
|
||||||
|
pub use state::Ferese;
|
||||||
|
use tracing::{info, warn};
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
init_logging();
|
||||||
|
|
||||||
|
let mut event_loop: EventLoop<Ferese> = EventLoop::try_new()?;
|
||||||
|
let display: Display<Ferese> = Display::new()?;
|
||||||
|
let mut state = Ferese::new(&mut event_loop, display)?;
|
||||||
|
winit::init(&mut event_loop, &mut state)?;
|
||||||
|
|
||||||
|
// SAFETY: the backend has already read the host display and Ferese is
|
||||||
|
// single-threaded before the event loop and optional child start.
|
||||||
|
unsafe { std::env::set_var("WAYLAND_DISPLAY", &state.socket_name) };
|
||||||
|
|
||||||
|
info!(socket = ?state.socket_name, "Ferese is accepting Wayland clients");
|
||||||
|
spawn_client_from_args();
|
||||||
|
event_loop.run(None, &mut state, |_| {})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_logging() {
|
||||||
|
let filter = tracing_subscriber::EnvFilter::try_from_default_env()
|
||||||
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("ferese=info"));
|
||||||
|
tracing_subscriber::fmt().with_env_filter(filter).init();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_client_from_args() {
|
||||||
|
let mut args = std::env::args_os().skip(1);
|
||||||
|
let Some(program) = args.next() else {
|
||||||
|
info!("no client requested; pass one after `--`, for example `-- foot`");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match Command::new(&program).args(args).spawn() {
|
||||||
|
Ok(child) => info!(program = ?program, pid = child.id(), "spawned nested client"),
|
||||||
|
Err(error) => warn!(program = ?program, %error, "failed to spawn nested client"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
use std::{error::Error, ffi::OsString, sync::Arc, time::Instant};
|
||||||
|
|
||||||
|
use smithay::{
|
||||||
|
desktop::{PopupManager, Space, Window, WindowSurfaceType},
|
||||||
|
input::{Seat, SeatState},
|
||||||
|
reexports::{
|
||||||
|
calloop::{EventLoop, Interest, LoopSignal, Mode, PostAction, generic::Generic},
|
||||||
|
wayland_server::{
|
||||||
|
Display, DisplayHandle,
|
||||||
|
backend::{ClientData, ClientId, DisconnectReason},
|
||||||
|
protocol::wl_surface::WlSurface,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
utils::{Logical, Point},
|
||||||
|
wayland::{
|
||||||
|
compositor::{CompositorClientState, CompositorState},
|
||||||
|
output::OutputManagerState,
|
||||||
|
shell::xdg::XdgShellState,
|
||||||
|
shm::ShmState,
|
||||||
|
socket::ListeningSocketSource,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Ferese {
|
||||||
|
pub start_time: Instant,
|
||||||
|
pub socket_name: OsString,
|
||||||
|
pub display_handle: DisplayHandle,
|
||||||
|
pub loop_signal: LoopSignal,
|
||||||
|
pub space: Space<Window>,
|
||||||
|
pub popups: PopupManager,
|
||||||
|
pub seat: Seat<Self>,
|
||||||
|
pub compositor_state: CompositorState,
|
||||||
|
pub output_manager_state: OutputManagerState,
|
||||||
|
pub seat_state: SeatState<Self>,
|
||||||
|
pub shm_state: ShmState,
|
||||||
|
pub xdg_shell_state: XdgShellState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ferese {
|
||||||
|
pub fn new(
|
||||||
|
event_loop: &mut EventLoop<Self>,
|
||||||
|
display: Display<Self>,
|
||||||
|
) -> Result<Self, Box<dyn Error>> {
|
||||||
|
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::new());
|
||||||
|
let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&display_handle);
|
||||||
|
let mut seat_state = SeatState::new();
|
||||||
|
let mut seat = seat_state.new_wl_seat(&display_handle, "ferese-winit");
|
||||||
|
seat.add_keyboard(Default::default(), 200, 25)?;
|
||||||
|
seat.add_pointer();
|
||||||
|
let socket_name = Self::init_wayland_listener(display, event_loop)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
start_time: Instant::now(),
|
||||||
|
socket_name,
|
||||||
|
display_handle,
|
||||||
|
loop_signal: event_loop.get_signal(),
|
||||||
|
space: Space::default(),
|
||||||
|
popups: PopupManager::default(),
|
||||||
|
seat,
|
||||||
|
compositor_state,
|
||||||
|
output_manager_state,
|
||||||
|
seat_state,
|
||||||
|
shm_state,
|
||||||
|
xdg_shell_state,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_wayland_listener(
|
||||||
|
display: Display<Self>,
|
||||||
|
event_loop: &mut EventLoop<Self>,
|
||||||
|
) -> Result<OsString, Box<dyn Error>> {
|
||||||
|
let listening_socket = ListeningSocketSource::new_auto()?;
|
||||||
|
let socket_name = listening_socket.socket_name().to_os_string();
|
||||||
|
let loop_handle = event_loop.handle();
|
||||||
|
|
||||||
|
loop_handle.insert_source(listening_socket, |client_stream, _, state| {
|
||||||
|
if let Err(error) = state
|
||||||
|
.display_handle
|
||||||
|
.insert_client(client_stream, Arc::new(ClientState::default()))
|
||||||
|
{
|
||||||
|
tracing::warn!(%error, "failed to register Wayland client");
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
loop_handle.insert_source(
|
||||||
|
Generic::new(display, Interest::READ, Mode::Level),
|
||||||
|
|_, display, state| {
|
||||||
|
// SAFETY: this event source owns the display for the loop lifetime.
|
||||||
|
unsafe { display.get_mut().dispatch_clients(state)? };
|
||||||
|
Ok(PostAction::Continue)
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
Ok(socket_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn surface_under(
|
||||||
|
&self,
|
||||||
|
position: Point<f64, Logical>,
|
||||||
|
) -> Option<(WlSurface, Point<f64, Logical>)> {
|
||||||
|
self.space
|
||||||
|
.element_under(position)
|
||||||
|
.and_then(|(window, location)| {
|
||||||
|
window
|
||||||
|
.surface_under(position - location.to_f64(), WindowSurfaceType::ALL)
|
||||||
|
.map(|(surface, surface_location)| {
|
||||||
|
(surface, (surface_location + location).to_f64())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
use std::{error::Error, time::Duration};
|
||||||
|
|
||||||
|
use smithay::{
|
||||||
|
backend::{
|
||||||
|
renderer::{
|
||||||
|
damage::OutputDamageTracker, element::surface::WaylandSurfaceRenderElement,
|
||||||
|
gles::GlesRenderer,
|
||||||
|
},
|
||||||
|
winit::{self, WinitEvent},
|
||||||
|
},
|
||||||
|
output::{Mode, Output, PhysicalProperties, Subpixel},
|
||||||
|
reexports::calloop::EventLoop,
|
||||||
|
utils::{Rectangle, Transform},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Ferese;
|
||||||
|
|
||||||
|
pub fn init(event_loop: &mut EventLoop<Ferese>, state: &mut Ferese) -> Result<(), Box<dyn Error>> {
|
||||||
|
let (mut backend, event_source) = winit::init()?;
|
||||||
|
let mode = Mode {
|
||||||
|
size: backend.window_size(),
|
||||||
|
refresh: 60_000,
|
||||||
|
};
|
||||||
|
let output = Output::new(
|
||||||
|
"ferese-winit".into(),
|
||||||
|
PhysicalProperties {
|
||||||
|
size: (0, 0).into(),
|
||||||
|
subpixel: Subpixel::Unknown,
|
||||||
|
make: "Ferese".into(),
|
||||||
|
model: "Nested".into(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
output.create_global::<Ferese>(&state.display_handle);
|
||||||
|
output.change_current_state(
|
||||||
|
Some(mode),
|
||||||
|
Some(Transform::Flipped180),
|
||||||
|
None,
|
||||||
|
Some((0, 0).into()),
|
||||||
|
);
|
||||||
|
output.set_preferred(mode);
|
||||||
|
state.space.map_output(&output, (0, 0));
|
||||||
|
|
||||||
|
let mut damage_tracker = OutputDamageTracker::from_output(&output);
|
||||||
|
event_loop
|
||||||
|
.handle()
|
||||||
|
.insert_source(event_source, move |event, _, state| match event {
|
||||||
|
WinitEvent::Resized { size, .. } => output.change_current_state(
|
||||||
|
Some(Mode {
|
||||||
|
size,
|
||||||
|
refresh: 60_000,
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
WinitEvent::Input(event) => state.process_input_event(event),
|
||||||
|
WinitEvent::Redraw => {
|
||||||
|
let damage = Rectangle::from_size(backend.window_size());
|
||||||
|
let rendered = (|| -> Result<(), Box<dyn Error>> {
|
||||||
|
{
|
||||||
|
let (renderer, mut framebuffer) = backend.bind()?;
|
||||||
|
smithay::desktop::space::render_output::<
|
||||||
|
_,
|
||||||
|
WaylandSurfaceRenderElement<GlesRenderer>,
|
||||||
|
_,
|
||||||
|
_,
|
||||||
|
>(
|
||||||
|
&output,
|
||||||
|
renderer,
|
||||||
|
&mut framebuffer,
|
||||||
|
1.0,
|
||||||
|
0,
|
||||||
|
[&state.space],
|
||||||
|
&[],
|
||||||
|
&mut damage_tracker,
|
||||||
|
[0.035, 0.04, 0.055, 1.0],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
backend.submit(Some(&[damage]))?;
|
||||||
|
Ok(())
|
||||||
|
})();
|
||||||
|
if let Err(error) = rendered {
|
||||||
|
tracing::error!(%error, "nested renderer failed");
|
||||||
|
state.loop_signal.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.space.elements().for_each(|window| {
|
||||||
|
window.send_frame(
|
||||||
|
&output,
|
||||||
|
state.start_time.elapsed(),
|
||||||
|
Some(Duration::ZERO),
|
||||||
|
|_, _| Some(output.clone()),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
state.space.refresh();
|
||||||
|
state.popups.cleanup();
|
||||||
|
if let Err(error) = state.display_handle.flush_clients() {
|
||||||
|
tracing::debug!(%error, "failed to flush clients");
|
||||||
|
}
|
||||||
|
backend.window().request_redraw();
|
||||||
|
}
|
||||||
|
WinitEvent::CloseRequested => state.loop_signal.stop(),
|
||||||
|
_ => {}
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in new issue
Block a user