Simplify snapshots at call-site

This commit is contained in:
Ottatop 2024-06-19 14:04:56 -05:00
parent fe182fdfcb
commit 05b8196319
10 changed files with 147 additions and 280 deletions

View file

@ -58,7 +58,6 @@ use crate::{
config::ConnectorSavedState,
input::{KeybindData, ModifierMask},
output::{OutputMode, OutputName},
render::util::snapshot::capture_snapshots_on_output,
state::{State, WithState},
tag::{Tag, TagId},
util::restore_nofile_rlimit,
@ -803,9 +802,7 @@ impl tag_service_server::TagService for TagService {
return;
};
let snapshots = state.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut state.pinnacle, renderer, &output, [])
});
state.capture_snapshots_on_output(&output, []);
match set_or_toggle {
SetOrToggle::Set => tag.set_active(true, &mut state.pinnacle),
@ -816,17 +813,9 @@ impl tag_service_server::TagService for TagService {
state.pinnacle.fixup_xwayland_window_layering();
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
state.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
state.pinnacle.begin_layout_transaction(&output);
state.pinnacle.request_layout(&output);
state.update_keyboard_focus(&output);
state.schedule_render(&output);
})
@ -848,9 +837,7 @@ impl tag_service_server::TagService for TagService {
return;
};
let snapshots = state.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut state.pinnacle, renderer, &output, [])
});
state.capture_snapshots_on_output(&output, []);
output.with_state(|op_state| {
for op_tag in op_state.tags.iter() {
@ -861,17 +848,9 @@ impl tag_service_server::TagService for TagService {
state.pinnacle.fixup_xwayland_window_layering();
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
state.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
state.pinnacle.begin_layout_transaction(&output);
state.pinnacle.request_layout(&output);
state.update_keyboard_focus(&output);
state.schedule_render(&output);
})
@ -1218,9 +1197,7 @@ impl output_service_server::OutputService for OutputService {
current_scale = f64::max(current_scale, 0.25);
let snapshots = state.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut state.pinnacle, renderer, &output, [])
});
state.capture_snapshots_on_output(&output, []);
state.pinnacle.change_output_state(
&mut state.backend,
@ -1231,17 +1208,9 @@ impl output_service_server::OutputService for OutputService {
None,
);
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
state.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
state.pinnacle.begin_layout_transaction(&output);
state.pinnacle.request_layout(&output);
state.schedule_render(&output);
state
.pinnacle

View file

@ -21,10 +21,7 @@ use smithay::{
use tonic::{Request, Response, Status};
use tracing::warn;
use crate::{
output::OutputName, render::util::snapshot::capture_snapshots_on_output, state::WithState,
tag::TagId, window::window_state::WindowId,
};
use crate::{output::OutputName, state::WithState, tag::TagId, window::window_state::WindowId};
use super::{run_unary, run_unary_no_response, StateFnSender};
@ -220,16 +217,15 @@ impl window_service_server::WindowService for WindowService {
}
run_unary_no_response(&self.sender, move |state| {
let pinnacle = &mut state.pinnacle;
let Some(window) = window_id.window(pinnacle) else {
let Some(window) = window_id.window(&state.pinnacle) else {
return;
};
let snapshots = window.output(pinnacle).map(|output| {
state.backend.with_renderer(|renderer| {
capture_snapshots_on_output(pinnacle, renderer, &output, [window.clone()])
})
});
let output = window.output(&state.pinnacle);
if let Some(output) = output.as_ref() {
state.capture_snapshots_on_output(output, [window.clone()]);
}
match set_or_toggle {
SetOrToggle::Set => {
@ -246,21 +242,13 @@ impl window_service_server::WindowService for WindowService {
SetOrToggle::Unspecified => unreachable!(),
}
let Some(output) = window.output(pinnacle) else {
let Some(output) = output else {
return;
};
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots.flatten() {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
state.pinnacle.begin_layout_transaction(&output);
state.pinnacle.request_layout(&output);
pinnacle.request_layout(&output);
state.schedule_render(&output);
})
.await
@ -362,37 +350,29 @@ impl window_service_server::WindowService for WindowService {
);
run_unary_no_response(&self.sender, move |state| {
let pinnacle = &mut state.pinnacle;
let Some(window) = window_id.window(pinnacle) else {
let Some(window) = window_id.window(&state.pinnacle) else {
return;
};
let Some(tag) = tag_id.tag(pinnacle) else { return };
let Some(tag) = tag_id.tag(&state.pinnacle) else { return };
let snapshots = window.output(pinnacle).map(|output| {
state.backend.with_renderer(|renderer| {
capture_snapshots_on_output(pinnacle, renderer, &output, [window.clone()])
})
});
let output = window.output(&state.pinnacle);
if let Some(output) = output.as_ref() {
state.capture_snapshots_on_output(output, [window.clone()]);
}
window.with_state_mut(|state| {
state.tags = vec![tag.clone()];
});
let Some(output) = tag.output(pinnacle) else { return };
let Some(output) = tag.output(&state.pinnacle) else {
return;
};
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots.flatten() {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
state.pinnacle.begin_layout_transaction(&output);
state.pinnacle.request_layout(&output);
pinnacle.request_layout(&output);
state.schedule_render(&output);
state.pinnacle.fixup_xwayland_window_layering();
@ -422,17 +402,16 @@ impl window_service_server::WindowService for WindowService {
}
run_unary_no_response(&self.sender, move |state| {
let pinnacle = &mut state.pinnacle;
let Some(window) = window_id.window(pinnacle) else {
let Some(window) = window_id.window(&state.pinnacle) else {
return;
};
let Some(tag) = tag_id.tag(pinnacle) else { return };
let Some(tag) = tag_id.tag(&state.pinnacle) else { return };
let snapshots = window.output(pinnacle).map(|output| {
state.backend.with_renderer(|renderer| {
capture_snapshots_on_output(pinnacle, renderer, &output, [window.clone()])
})
});
let output = window.output(&state.pinnacle);
if let Some(output) = output.as_ref() {
state.capture_snapshots_on_output(output, [window.clone()]);
}
// TODO: turn state.tags into a hashset
match set_or_toggle {
@ -453,19 +432,13 @@ impl window_service_server::WindowService for WindowService {
SetOrToggle::Unspecified => unreachable!(),
}
let Some(output) = tag.output(pinnacle) else { return };
let Some(output) = tag.output(&state.pinnacle) else {
return;
};
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots.flatten() {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
state.pinnacle.begin_layout_transaction(&output);
state.pinnacle.request_layout(&output);
pinnacle.request_layout(&output);
state.schedule_render(&output);
state.pinnacle.fixup_xwayland_window_layering();

View file

@ -87,7 +87,6 @@ use crate::{
output_power_management::{OutputPowerManagementHandler, OutputPowerManagementState},
screencopy::{Screencopy, ScreencopyHandler},
},
render::util::snapshot::capture_snapshots_on_output,
state::{ClientState, Pinnacle, State, WithState},
};
@ -188,20 +187,18 @@ impl CompositorHandler for State {
.with_state_mut(|state| state.snapshot_hook_id = Some(hook_id));
}
let snapshots = if let Some(output) = self.pinnacle.focused_output().cloned() {
let output = self.pinnacle.focused_output().cloned();
if let Some(output) = output.as_ref() {
tracing::debug!("Placing toplevel");
unmapped_window.place_on_output(&output);
unmapped_window.place_on_output(output);
output.with_state_mut(|state| {
state.focus_stack.set_focus(unmapped_window.clone())
});
Some(self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [])
}))
} else {
None
};
self.capture_snapshots_on_output(output, []);
}
self.pinnacle
.unmapped_windows
@ -212,21 +209,12 @@ impl CompositorHandler for State {
self.pinnacle.apply_window_rules(&unmapped_window);
if let Some(focused_output) = self.pinnacle.focused_output().cloned() {
if let Some(focused_output) = output {
if unmapped_window.is_on_active_tag() {
self.update_keyboard_focus(&focused_output);
if let Some((fs_and_up_snapshots, under_fs_snapshots)) =
snapshots.flatten()
{
focused_output.with_state_mut(|state| {
state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
self.pinnacle.begin_layout_transaction(&focused_output);
self.pinnacle.request_layout(&focused_output);
// It seems wlcs needs immediate frame sends for client tests to work
#[cfg(feature = "testing")]
@ -236,8 +224,6 @@ impl CompositorHandler for State {
Some(std::time::Duration::ZERO),
|_, _| None,
);
self.pinnacle.request_layout(&focused_output);
}
}
} else {
@ -269,30 +255,16 @@ impl CompositorHandler for State {
compositor::remove_pre_commit_hook(surface, hook_id);
}
if let Some(output) = window.output(&self.pinnacle) {
let snapshots = self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(
&mut self.pinnacle,
renderer,
&output,
[],
)
});
let output = window.output(&self.pinnacle);
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
if let Some(output) = output.as_ref() {
self.capture_snapshots_on_output(output, []);
self.pinnacle.begin_layout_transaction(output);
}
self.pinnacle.remove_window(&window, true);
if let Some(output) = window.output(&self.pinnacle) {
if let Some(output) = output {
self.update_keyboard_focus(&output);
self.pinnacle.request_layout(&output);
}
@ -848,11 +820,7 @@ impl OutputManagementHandler for State {
// TODO: split
self.backend.set_output_powered(&output, true);
self.schedule_render(&output);
let snapshots = self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [])
});
self.capture_snapshots_on_output(&output, []);
let mode = mode.map(|(size, refresh)| {
if let Some(refresh) = refresh {
@ -886,17 +854,10 @@ impl OutputManagementHandler for State {
position,
);
if let Some((a, b)) = snapshots {
output.with_state_mut(|state| {
state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
a,
b,
)
});
}
self.pinnacle.begin_layout_transaction(&output);
self.pinnacle.request_layout(&output);
self.schedule_render(&output);
}
}
}

View file

@ -3,7 +3,6 @@ use smithay::reexports::wayland_server::protocol::{wl_output::WlOutput, wl_surfa
use crate::{
delegate_foreign_toplevel,
protocol::foreign_toplevel::{ForeignToplevelHandler, ForeignToplevelManagerState},
render::util::snapshot::capture_snapshots_on_output,
state::{State, WithState},
};
@ -23,10 +22,9 @@ impl ForeignToplevelHandler for State {
if !window.is_on_active_tag() {
let new_active_tag =
window.with_state(|state| state.tags.iter().min_by_key(|tag| tag.id().0).cloned());
if let Some(tag) = new_active_tag {
let snapshots = self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [])
});
self.capture_snapshots_on_output(&output, []);
output.with_state(|state| {
if state.tags.contains(&tag) {
@ -37,15 +35,8 @@ impl ForeignToplevelHandler for State {
}
});
if let Some((above, below)) = snapshots {
output.with_state_mut(|state| {
state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
above,
below,
)
});
}
self.pinnacle.begin_layout_transaction(&output);
self.pinnacle.request_layout(&output);
}
}
@ -53,7 +44,6 @@ impl ForeignToplevelHandler for State {
self.pinnacle.raise_window(window, true);
self.update_keyboard_focus(&output);
self.pinnacle.request_layout(&output);
self.schedule_render(&output);
}

View file

@ -1,16 +1,14 @@
use crate::{
render::util::snapshot::capture_snapshots_on_output,
state::{State, WithState},
window::WindowElement,
};
impl State {
pub fn set_window_maximized(&mut self, window: &WindowElement, maximized: bool) {
let snapshots = window.output(&self.pinnacle).map(|output| {
self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [window.clone()])
})
});
let output = window.output(&self.pinnacle);
if let Some(output) = output.as_ref() {
self.capture_snapshots_on_output(output, [window.clone()]);
}
if maximized {
if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
@ -20,28 +18,19 @@ impl State {
window.toggle_maximized();
}
if let Some(output) = window.output(&self.pinnacle) {
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots.flatten() {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
if let Some(output) = output {
self.pinnacle.begin_layout_transaction(&output);
self.pinnacle.request_layout(&output);
self.schedule_render(&output);
}
}
pub fn set_window_fullscreen(&mut self, window: &WindowElement, fullscreen: bool) {
let snapshots = window.output(&self.pinnacle).map(|output| {
self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [window.clone()])
})
});
let output = window.output(&self.pinnacle);
if let Some(output) = output.as_ref() {
self.capture_snapshots_on_output(output, [window.clone()]);
}
if fullscreen {
if !window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
@ -52,17 +41,9 @@ impl State {
}
if let Some(output) = window.output(&self.pinnacle) {
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots.flatten() {
output.with_state_mut(|op_state| {
op_state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
self.pinnacle.begin_layout_transaction(&output);
self.pinnacle.request_layout(&output);
self.schedule_render(&output);
}
}

View file

@ -25,7 +25,6 @@ use tracing::trace;
use crate::{
focus::keyboard::KeyboardFocusTarget,
render::util::snapshot::capture_snapshots_on_output,
state::{State, WithState},
window::WindowElement,
};
@ -55,36 +54,19 @@ impl XdgShellHandler for State {
return;
};
let snapshots = if let Some(output) = window.output(&self.pinnacle) {
self.backend.with_renderer(|renderer| {
Some(capture_snapshots_on_output(
&mut self.pinnacle,
renderer,
&output,
[],
))
})
} else {
None
};
let output = window.output(&self.pinnacle);
if let Some(output) = output.as_ref() {
self.capture_snapshots_on_output(output, []);
}
self.pinnacle.remove_window(&window, false);
if let Some(output) = window.output(&self.pinnacle) {
if let Some(output) = output {
self.pinnacle.begin_layout_transaction(&output);
self.pinnacle.request_layout(&output);
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots.flatten() {
output.with_state_mut(|state| {
state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
);
});
}
self.update_keyboard_focus(&output);
self.schedule_render(&output);
}
}

View file

@ -26,7 +26,6 @@ use tracing::{debug, error, trace, warn};
use crate::{
cursor::Cursor,
focus::keyboard::KeyboardFocusTarget,
render::util::snapshot::capture_snapshots_on_output,
state::{Pinnacle, State, WithState},
window::{window_state::FloatingOrTiled, WindowElement},
};
@ -102,13 +101,11 @@ impl XwmHandler for State {
// TODO: do snapshot and transaction here BUT ONLY IF TILED AND ON ACTIVE TAG
let snapshots = if let Some(output) = window.output(&self.pinnacle) {
Some(self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [])
}))
} else {
None
};
let output = window.output(&self.pinnacle);
if let Some(output) = output.as_ref() {
self.capture_snapshots_on_output(output, []);
}
self.pinnacle.windows.push(window.clone());
self.pinnacle.raise_window(window.clone(), true);
@ -116,20 +113,11 @@ impl XwmHandler for State {
self.pinnacle.apply_window_rules(&window);
if window.is_on_active_tag() {
if let Some(output) = window.output(&self.pinnacle) {
if let Some(output) = output {
output.with_state_mut(|state| state.focus_stack.set_focus(window.clone()));
self.update_keyboard_focus(&output);
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots.flatten() {
output.with_state_mut(|state| {
state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
self.pinnacle.begin_layout_transaction(&output);
self.pinnacle.request_layout(&output);
}
}
@ -421,27 +409,20 @@ impl State {
if let Some(win) = win {
debug!("removing x11 window from windows");
let snapshots = win.output(&self.pinnacle).map(|output| {
self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [])
})
});
let output = win.output(&self.pinnacle);
if let Some(output) = output.as_ref() {
self.capture_snapshots_on_output(output, []);
}
self.pinnacle.remove_window(&win, false);
if let Some(output) = win.output(&self.pinnacle) {
if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots.flatten() {
output.with_state_mut(|state| {
state.new_wait_layout_transaction(
self.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
)
});
}
self.pinnacle.begin_layout_transaction(&output);
self.pinnacle.request_layout(&output);
self.update_keyboard_focus(&output);
// FIXME: schedule renders on all the outputs this window intersected
self.schedule_render(&output);
}
}

View file

@ -16,7 +16,6 @@ use tracing::warn;
use crate::{
output::OutputName,
render::util::snapshot::capture_snapshots_on_output,
state::{Pinnacle, State, WithState},
window::{
window_state::{FloatingOrTiled, FullscreenOrMaximized},
@ -278,9 +277,7 @@ impl State {
.fulfilled_requests
.insert(output.clone(), current_pending);
let snapshots = self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, &output, [])
});
self.capture_snapshots_on_output(&output, []);
let pending_windows = self
.pinnacle
@ -289,11 +286,11 @@ impl State {
output.with_state_mut(|state| {
if let Some(ts) = state.layout_transaction.as_mut() {
ts.update_pending(pending_windows);
} else if let Some((fs_and_up_snapshots, under_fs_snapshots)) = snapshots {
} else {
state.layout_transaction = Some(LayoutTransaction::new(
self.pinnacle.loop_handle.clone(),
fs_and_up_snapshots,
under_fs_snapshots,
std::mem::take(&mut state.snapshots.fullscreen_and_above),
std::mem::take(&mut state.snapshots.under_fullscreen),
pending_windows,
));
}

View file

@ -19,6 +19,7 @@ use crate::{
focus::WindowKeyboardFocusStack,
layout::transaction::{LayoutTransaction, SnapshotTarget},
protocol::screencopy::Screencopy,
render::util::snapshot::OutputSnapshots,
state::{Pinnacle, State, WithState},
tag::Tag,
window::window_state::FloatingOrTiled,
@ -71,6 +72,7 @@ pub struct OutputState {
/// Unpowered monitors aren't drawn to but their tags and windows
/// still exist and can be interacted with.
pub powered: bool,
pub snapshots: OutputSnapshots,
}
impl Default for OutputState {
@ -85,6 +87,7 @@ impl Default for OutputState {
blanking_state: Default::default(),
layout_transaction: Default::default(),
powered: true,
snapshots: OutputSnapshots::default(),
}
}
}
@ -120,7 +123,7 @@ impl OutputState {
self.tags.iter().filter(|tag| tag.active())
}
pub fn new_wait_layout_transaction(
fn new_wait_layout_transaction(
&mut self,
loop_handle: LoopHandle<'static, State>,
fullscreen_and_up_snapshots: impl IntoIterator<Item = SnapshotTarget>,
@ -138,6 +141,18 @@ impl OutputState {
}
}
impl Pinnacle {
pub fn begin_layout_transaction(&self, output: &Output) {
output.with_state_mut(|state| {
let (fullscreen_and_up, under) = (
std::mem::take(&mut state.snapshots.under_fullscreen),
std::mem::take(&mut state.snapshots.fullscreen_and_above),
);
state.new_wait_layout_transaction(self.loop_handle.clone(), fullscreen_and_up, under);
})
}
}
#[derive(Debug, Clone, Copy)]
pub enum OutputMode {
Smithay(Mode),

View file

@ -22,7 +22,7 @@ use tracing::debug;
use crate::layout::transaction::{LayoutSnapshot, SnapshotRenderElement, SnapshotTarget};
use crate::render::texture::CommonTextureRenderElement;
use crate::render::{AsGlesRenderer, PRenderer};
use crate::state::{Pinnacle, WithState};
use crate::state::{Pinnacle, State, WithState};
use crate::window::WindowElement;
use super::{render_to_encompassing_texture, EncompassingTexture};
@ -149,18 +149,33 @@ impl WindowElement {
}
}
impl State {
/// Capture snapshots for all tiled windows on this output.
///
/// Any windows in `also_include` are also included in the capture.
///
/// ret.1 = fullscreen and up,
/// ret.2 = under fullscreen
pub fn capture_snapshots_on_output(
&mut self,
output: &Output,
also_include: impl IntoIterator<Item = WindowElement>,
) {
self.backend.with_renderer(|renderer| {
capture_snapshots_on_output(&mut self.pinnacle, renderer, output, also_include);
});
}
}
#[derive(Debug, Default)]
pub struct OutputSnapshots {
pub fullscreen_and_above: Vec<SnapshotTarget>,
pub under_fullscreen: Vec<SnapshotTarget>,
}
pub fn capture_snapshots_on_output(
pinnacle: &mut Pinnacle,
renderer: &mut GlesRenderer,
output: &Output,
also_include: impl IntoIterator<Item = WindowElement>,
) -> (Vec<SnapshotTarget>, Vec<SnapshotTarget>) {
) {
let split_index = pinnacle
.space
.elements()
@ -216,5 +231,8 @@ pub fn capture_snapshots_on_output(
.flat_map(&mut flat_map)
.collect();
(fullscreen_and_up_snapshots, under_fullscreen_snapshots)
output.with_state_mut(|state| {
state.snapshots.fullscreen_and_above = fullscreen_and_up_snapshots;
state.snapshots.under_fullscreen = under_fullscreen_snapshots;
});
}