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
@@ -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() {
|
||||
println!("Ferese Phase 0 scaffold");
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
|
||||
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