mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-29 20:34:46 +01:00
Merge pull request #69 from Ottatop/fix_rendering
Fix rendering (hopefully)
This commit is contained in:
commit
68c47f15ae
14 changed files with 276 additions and 170 deletions
|
@ -1333,18 +1333,19 @@ impl State {
|
|||
};
|
||||
|
||||
let result = render_surface(
|
||||
&mut self.cursor_status,
|
||||
&self.space,
|
||||
&self.windows,
|
||||
self.dnd_icon.as_ref(),
|
||||
&self.focus_state.focus_stack,
|
||||
surface,
|
||||
&mut renderer,
|
||||
&self.space,
|
||||
&output,
|
||||
self.seat.input_method(),
|
||||
self.pointer_location,
|
||||
&pointer_image,
|
||||
&mut backend.pointer_element,
|
||||
&mut self.cursor_status,
|
||||
self.dnd_icon.as_ref(),
|
||||
self.pointer_location,
|
||||
&self.clock,
|
||||
&self.focus_state.focus_stack,
|
||||
);
|
||||
let reschedule = match &result {
|
||||
Ok(has_rendered) => !has_rendered,
|
||||
|
@ -1437,30 +1438,32 @@ impl State {
|
|||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn render_surface<'a>(
|
||||
cursor_status: &mut CursorImageStatus,
|
||||
space: &Space<WindowElement>,
|
||||
windows: &[WindowElement],
|
||||
dnd_icon: Option<&WlSurface>,
|
||||
focus_stack: &[WindowElement],
|
||||
surface: &'a mut SurfaceData,
|
||||
renderer: &mut UdevRenderer<'a, '_>,
|
||||
space: &Space<WindowElement>,
|
||||
output: &Output,
|
||||
input_method: &InputMethodHandle,
|
||||
pointer_location: Point<f64, Logical>,
|
||||
pointer_image: &TextureBuffer<MultiTexture>,
|
||||
pointer_element: &mut PointerElement<MultiTexture>,
|
||||
cursor_status: &mut CursorImageStatus,
|
||||
dnd_icon: Option<&WlSurface>,
|
||||
pointer_location: Point<f64, Logical>,
|
||||
clock: &Clock<Monotonic>,
|
||||
focus_stack: &[WindowElement],
|
||||
) -> Result<bool, SwapBuffersError> {
|
||||
let output_render_elements = crate::render::generate_render_elements(
|
||||
renderer,
|
||||
space,
|
||||
output,
|
||||
input_method,
|
||||
windows,
|
||||
pointer_location,
|
||||
pointer_element,
|
||||
Some(pointer_image),
|
||||
cursor_status,
|
||||
dnd_icon,
|
||||
focus_stack,
|
||||
renderer,
|
||||
output,
|
||||
input_method,
|
||||
pointer_element,
|
||||
Some(pointer_image),
|
||||
);
|
||||
|
||||
let res = surface.compositor.render_frame::<_, _, GlesTexture>(
|
||||
|
|
|
@ -246,17 +246,20 @@ pub fn run_winit() -> anyhow::Result<()> {
|
|||
let full_redraw = &mut backend.full_redraw;
|
||||
*full_redraw = full_redraw.saturating_sub(1);
|
||||
|
||||
state.focus_state.fix_up_focus(&mut state.space);
|
||||
|
||||
let output_render_elements = crate::render::generate_render_elements(
|
||||
backend.backend.renderer(),
|
||||
&state.space,
|
||||
&output,
|
||||
state.seat.input_method(),
|
||||
&state.windows,
|
||||
state.pointer_location,
|
||||
&mut pointer_element,
|
||||
None,
|
||||
&mut state.cursor_status,
|
||||
state.dnd_icon.as_ref(),
|
||||
&state.focus_state.focus_stack,
|
||||
backend.backend.renderer(),
|
||||
&output,
|
||||
state.seat.input_method(),
|
||||
&mut pointer_element,
|
||||
None,
|
||||
);
|
||||
|
||||
let render_res = backend.backend.bind().and_then(|_| {
|
||||
|
@ -281,6 +284,7 @@ pub fn run_winit() -> anyhow::Result<()> {
|
|||
Ok(render_output_result) => {
|
||||
let has_rendered = render_output_result.damage.is_some();
|
||||
if let Some(damage) = render_output_result.damage {
|
||||
// tracing::debug!("damage rects are {damage:?}");
|
||||
if let Err(err) = backend.backend.submit(Some(&damage)) {
|
||||
tracing::warn!("{}", err);
|
||||
}
|
||||
|
|
|
@ -50,9 +50,7 @@ impl FocusState {
|
|||
/// to back to correct their z locations.
|
||||
pub fn fix_up_focus(&self, space: &mut Space<WindowElement>) {
|
||||
for win in self.focus_stack.iter() {
|
||||
if let Some(loc) = space.element_location(win) {
|
||||
space.map_element(win.clone(), loc, false);
|
||||
}
|
||||
space.raise_element(win, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,6 +121,7 @@ impl CompositorHandler for State {
|
|||
if let Some(window) = self.window_for_surface(surface) {
|
||||
window.with_state(|state| {
|
||||
if let LocationRequestState::Acknowledged(new_pos) = state.loc_request_state {
|
||||
tracing::debug!("Mapping Acknowledged window");
|
||||
state.loc_request_state = LocationRequestState::Idle;
|
||||
self.space.map_element(window.clone(), new_pos, false);
|
||||
}
|
||||
|
@ -128,7 +129,8 @@ impl CompositorHandler for State {
|
|||
}
|
||||
|
||||
// correct focus layering
|
||||
self.focus_state.fix_up_focus(&mut self.space);
|
||||
// TODO: maybe do this at the end of every event loop cycle instead?
|
||||
// self.focus_state.fix_up_focus(&mut self.space);
|
||||
}
|
||||
|
||||
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
|
||||
|
|
|
@ -72,6 +72,8 @@ impl XdgShellHandler for State {
|
|||
self.space.map_element(window.clone(), (0, 0), true);
|
||||
|
||||
let win_clone = window.clone();
|
||||
|
||||
// Let the initial configure happen before updating the windows
|
||||
self.schedule(
|
||||
move |_data| {
|
||||
if let WindowElement::Wayland(window) = &win_clone {
|
||||
|
@ -123,10 +125,8 @@ impl XdgShellHandler for State {
|
|||
self.update_windows(&focused_output);
|
||||
}
|
||||
|
||||
// let mut windows: Vec<Window> = self.space.elements().cloned().collect();
|
||||
// windows.retain(|window| window.toplevel() != &surface);
|
||||
// Layouts::master_stack(self, windows, crate::layout::Direction::Left);
|
||||
let focus = self.focus_state.current_focus().map(FocusTarget::Window);
|
||||
|
||||
self.seat
|
||||
.get_keyboard()
|
||||
.expect("Seat had no keyboard")
|
||||
|
@ -145,7 +145,7 @@ impl XdgShellHandler for State {
|
|||
crate::grab::move_grab::move_request_client(
|
||||
self,
|
||||
surface.wl_surface(),
|
||||
&Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"),
|
||||
&Seat::from_resource(&seat).expect("couldn't get seat from WlSeat"),
|
||||
serial,
|
||||
BUTTON_LEFT,
|
||||
);
|
||||
|
@ -162,7 +162,7 @@ impl XdgShellHandler for State {
|
|||
crate::grab::resize_grab::resize_request_client(
|
||||
self,
|
||||
surface.wl_surface(),
|
||||
&Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat"),
|
||||
&Seat::from_resource(&seat).expect("couldn't get seat from WlSeat"),
|
||||
serial,
|
||||
edges.into(),
|
||||
BUTTON_LEFT,
|
||||
|
@ -183,7 +183,7 @@ impl XdgShellHandler for State {
|
|||
}
|
||||
|
||||
fn grab(&mut self, surface: PopupSurface, seat: WlSeat, serial: Serial) {
|
||||
let seat: Seat<Self> = Seat::from_resource(&seat).expect("Couldn't get seat from WlSeat");
|
||||
let seat: Seat<Self> = Seat::from_resource(&seat).expect("couldn't get seat from WlSeat");
|
||||
let popup_kind = PopupKind::Xdg(surface);
|
||||
if let Some(root) = find_popup_root_surface(&popup_kind).ok().and_then(|root| {
|
||||
self.window_for_surface(&root)
|
||||
|
|
24
src/input.rs
24
src/input.rs
|
@ -73,6 +73,7 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the [`FocusTarget`] under `point`.
|
||||
pub fn surface_under<P>(&self, point: P) -> Option<(FocusTarget, Point<i32, Logical>)>
|
||||
where
|
||||
P: Into<Point<f64, Logical>>,
|
||||
|
@ -101,9 +102,12 @@ impl State {
|
|||
});
|
||||
|
||||
// I think I'm going a bit too far with the functional stuff
|
||||
|
||||
// The topmost fullscreen window
|
||||
top_fullscreen_window
|
||||
.map(|window| (FocusTarget::from(window.clone()), output_geo.loc))
|
||||
.or_else(|| {
|
||||
// The topmost layer surface in Overlay or Top
|
||||
layers
|
||||
.layer_under(wlr_layer::Layer::Overlay, point)
|
||||
.or_else(|| layers.layer_under(wlr_layer::Layer::Top, point))
|
||||
|
@ -113,11 +117,27 @@ impl State {
|
|||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
// The topmost window
|
||||
self.space
|
||||
.element_under(point)
|
||||
.map(|(window, loc)| (window.clone().into(), loc))
|
||||
.elements()
|
||||
.rev()
|
||||
.filter(|win| win.is_on_active_tag(self.space.outputs()))
|
||||
.find_map(|win| {
|
||||
let loc = self
|
||||
.space
|
||||
.element_location(win)
|
||||
.expect("called elem loc on unmapped win")
|
||||
- win.geometry().loc;
|
||||
|
||||
if win.is_in_input_region(&(point - loc.to_f64())) {
|
||||
Some((win.clone().into(), loc))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
// The topmost layer surface in Bottom or Background
|
||||
layers
|
||||
.layer_under(wlr_layer::Layer::Bottom, point)
|
||||
.or_else(|| layers.layer_under(wlr_layer::Layer::Background, point))
|
||||
|
|
139
src/layout.rs
139
src/layout.rs
|
@ -4,21 +4,17 @@ use itertools::{Either, Itertools};
|
|||
use smithay::{
|
||||
desktop::layer_map_for_output,
|
||||
output::Output,
|
||||
reexports::wayland_server::Resource,
|
||||
utils::{Logical, Point, Rectangle, Size},
|
||||
wayland::compositor::{self, CompositorHandler},
|
||||
utils::{IsAlive, Logical, Point, Rectangle, Size},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
state::{State, WithState},
|
||||
window::{
|
||||
window_state::{FloatingOrTiled, FullscreenOrMaximized, LocationRequestState},
|
||||
WindowElement, BLOCKER_COUNTER,
|
||||
WindowElement,
|
||||
},
|
||||
};
|
||||
|
||||
// -------------------------------------------
|
||||
|
||||
impl State {
|
||||
/// Compute the positions and sizes of tiled windows on
|
||||
/// `output` according to the provided [`Layout`].
|
||||
|
@ -55,9 +51,11 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
/// Compute tiled window locations and sizes, size maximized and fullscreen windows correctly,
|
||||
/// and send configures and that cool stuff.
|
||||
pub fn update_windows(&mut self, output: &Output) {
|
||||
let Some(layout) = output.with_state(|state| {
|
||||
state.focused_tags().next().cloned().map(|tag| tag.layout())
|
||||
state.focused_tags().next().map(|tag| tag.layout())
|
||||
}) else { return };
|
||||
|
||||
let (windows_on_foc_tags, mut windows_not_on_foc_tags): (Vec<_>, _) =
|
||||
|
@ -68,6 +66,7 @@ impl State {
|
|||
})
|
||||
});
|
||||
|
||||
// Don't unmap windows that aren't on `output` (that would clear all other monitors)
|
||||
windows_not_on_foc_tags.retain(|win| win.output(self) == Some(output.clone()));
|
||||
|
||||
let tiled_windows = windows_on_foc_tags
|
||||
|
@ -122,12 +121,19 @@ impl State {
|
|||
WindowElement::Wayland(win) => {
|
||||
// If the above didn't cause any change to size or other state, simply
|
||||
// map the window.
|
||||
if !win.toplevel().has_pending_changes() {
|
||||
let current_state = win.toplevel().current_state();
|
||||
let is_pending = win
|
||||
.toplevel()
|
||||
.with_pending_state(|state| state.size != current_state.size);
|
||||
// for whatever reason vscode on wayland likes to have pending state
|
||||
// (like tiled states) but not commit, so we check for just the size
|
||||
// here
|
||||
if !is_pending {
|
||||
tracing::debug!("No pending changes");
|
||||
state.loc_request_state = LocationRequestState::Idle;
|
||||
non_pending_wins.push((loc, window.clone()));
|
||||
// TODO: wait for windows with pending state to ack and commit
|
||||
// self.space.map_element(window.clone(), loc, false);
|
||||
} else {
|
||||
tracing::debug!("Pending changes");
|
||||
let serial = win.toplevel().send_configure();
|
||||
state.loc_request_state =
|
||||
LocationRequestState::Requested(serial, loc);
|
||||
|
@ -149,76 +155,30 @@ impl State {
|
|||
});
|
||||
}
|
||||
|
||||
BLOCKER_COUNTER.store(1, std::sync::atomic::Ordering::SeqCst);
|
||||
tracing::debug!(
|
||||
"blocker {}",
|
||||
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
||||
);
|
||||
|
||||
let start_time = self.clock.now();
|
||||
|
||||
// Pause rendering. Here we'll wait until all windows have ack'ed and committed,
|
||||
// then resume rendering. This prevents flickering because some windows will commit before
|
||||
// others.
|
||||
//
|
||||
// This *will* cause everything to freeze for a few frames, but it should'nt impact
|
||||
// This *will* cause everything to freeze for a few frames, but it shouldn't impact
|
||||
// anything meaningfully.
|
||||
self.pause_rendering = true;
|
||||
|
||||
for (_loc, win) in pending_wins.iter() {
|
||||
if let Some(surf) = win.wl_surface() {
|
||||
tracing::debug!("adding blocker");
|
||||
compositor::add_blocker(&surf, crate::window::WindowBlocker);
|
||||
}
|
||||
}
|
||||
|
||||
let pending_wins_clone = pending_wins.clone();
|
||||
|
||||
// schedule on all idle
|
||||
self.schedule(
|
||||
move |_data| {
|
||||
pending_wins_clone.iter().all(|(_, win)| {
|
||||
win.with_state(|state| state.loc_request_state.is_acknowledged())
|
||||
})
|
||||
},
|
||||
move |data| {
|
||||
// remove and trigger blockers
|
||||
BLOCKER_COUNTER.store(0, std::sync::atomic::Ordering::SeqCst);
|
||||
tracing::debug!(
|
||||
"blocker {}",
|
||||
BLOCKER_COUNTER.load(std::sync::atomic::Ordering::SeqCst)
|
||||
);
|
||||
for client in pending_wins
|
||||
move |_dt| {
|
||||
// tracing::debug!("Waiting for all to be idle");
|
||||
let all_idle = pending_wins
|
||||
.iter()
|
||||
.filter_map(|(_, win)| win.wl_surface()?.client())
|
||||
{
|
||||
data.state
|
||||
.client_compositor_state(&client)
|
||||
.blocker_cleared(&mut data.state, &data.display.handle())
|
||||
}
|
||||
.filter(|(_, win)| win.alive())
|
||||
.all(|(_, win)| win.with_state(|state| state.loc_request_state.is_idle()));
|
||||
|
||||
// schedule on all idle
|
||||
data.state.schedule(
|
||||
move |_dt| {
|
||||
pending_wins.iter().all(|(_, win)| {
|
||||
win.with_state(|state| state.loc_request_state.is_idle())
|
||||
})
|
||||
},
|
||||
move |dt| {
|
||||
for (loc, win) in non_pending_wins {
|
||||
dt.state.space.map_element(win, loc, false);
|
||||
}
|
||||
for win in windows_not_on_foc_tags {
|
||||
dt.state.space.unmap_elem(&win);
|
||||
}
|
||||
dt.state.pause_rendering = false;
|
||||
let finish_time =
|
||||
smithay::utils::Time::elapsed(&start_time, dt.state.clock.now());
|
||||
tracing::debug!(
|
||||
"spent {} microseconds not rendering",
|
||||
finish_time.as_micros()
|
||||
);
|
||||
},
|
||||
);
|
||||
all_idle
|
||||
},
|
||||
move |dt| {
|
||||
for (loc, win) in non_pending_wins {
|
||||
dt.state.space.map_element(win, loc, false);
|
||||
}
|
||||
dt.state.pause_rendering = false;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -538,13 +498,36 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: don't use the focused output, use the outputs the two windows are on
|
||||
let output = self
|
||||
.focus_state
|
||||
.focused_output
|
||||
.clone()
|
||||
.expect("no focused output");
|
||||
self.update_windows(&output);
|
||||
// self.re_layout(&output);
|
||||
let mut same_suggested_size = false;
|
||||
|
||||
if let WindowElement::Wayland(w1) = win1 {
|
||||
if let WindowElement::Wayland(w2) = win2 {
|
||||
if let Some(w1_size) = w1.toplevel().current_state().size {
|
||||
if let Some(w2_size) = w2.toplevel().current_state().size {
|
||||
same_suggested_size = w1_size == w2_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if same_suggested_size {
|
||||
let win1_loc = self.space.element_location(win1);
|
||||
let win2_loc = self.space.element_location(win2);
|
||||
|
||||
if let Some(win1_loc) = win1_loc {
|
||||
if let Some(win2_loc) = win2_loc {
|
||||
self.space.map_element(win1.clone(), win2_loc, false);
|
||||
self.space.map_element(win2.clone(), win1_loc, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: don't use the focused output, use the outputs the two windows are on
|
||||
let output = self
|
||||
.focus_state
|
||||
.focused_output
|
||||
.clone()
|
||||
.expect("no focused output");
|
||||
self.update_windows(&output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//! While Pinnacle is not a library, this documentation serves to guide those who want to
|
||||
//! contribute or learn how building something like this works.
|
||||
|
||||
#![deny(unused_imports)] // gonna force myself to keep stuff clean
|
||||
// #![deny(unused_imports)] // gonna force myself to keep stuff clean
|
||||
#![warn(clippy::unwrap_used)]
|
||||
|
||||
use clap::Parser;
|
||||
|
|
106
src/render.rs
106
src/render.rs
|
@ -11,7 +11,8 @@ use smithay::{
|
|||
ImportAll, ImportMem, Renderer, Texture,
|
||||
},
|
||||
desktop::{
|
||||
space::{self, SpaceRenderElements, SurfaceTree},
|
||||
layer_map_for_output,
|
||||
space::{SpaceRenderElements, SurfaceTree},
|
||||
Space,
|
||||
},
|
||||
input::pointer::{CursorImageAttributes, CursorImageStatus},
|
||||
|
@ -22,10 +23,10 @@ use smithay::{
|
|||
},
|
||||
render_elements,
|
||||
utils::{IsAlive, Logical, Physical, Point, Scale},
|
||||
wayland::{compositor, input_method::InputMethodHandle},
|
||||
wayland::{compositor, input_method::InputMethodHandle, shell::wlr_layer},
|
||||
};
|
||||
|
||||
use crate::{state::WithState, window::WindowElement};
|
||||
use crate::{state::WithState, tag::Tag, window::WindowElement};
|
||||
|
||||
use self::pointer::{PointerElement, PointerRenderElement};
|
||||
|
||||
|
@ -76,18 +77,71 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
struct LayerRenderElements<R> {
|
||||
background: Vec<WaylandSurfaceRenderElement<R>>,
|
||||
bottom: Vec<WaylandSurfaceRenderElement<R>>,
|
||||
top: Vec<WaylandSurfaceRenderElement<R>>,
|
||||
overlay: Vec<WaylandSurfaceRenderElement<R>>,
|
||||
}
|
||||
|
||||
fn layer_render_elements<R>(output: &Output, renderer: &mut R) -> LayerRenderElements<R>
|
||||
where
|
||||
R: Renderer + ImportAll,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
{
|
||||
let layer_map = layer_map_for_output(output);
|
||||
let mut overlay = vec![];
|
||||
let mut top = vec![];
|
||||
let mut bottom = vec![];
|
||||
let mut background = vec![];
|
||||
|
||||
let layer_elements = layer_map
|
||||
.layers()
|
||||
.filter_map(|surface| {
|
||||
layer_map
|
||||
.layer_geometry(surface)
|
||||
.map(|geo| (surface, geo.loc))
|
||||
})
|
||||
.map(|(surface, loc)| {
|
||||
let render_elements = surface.render_elements::<WaylandSurfaceRenderElement<R>>(
|
||||
renderer,
|
||||
loc.to_physical(1),
|
||||
Scale::from(1.0),
|
||||
1.0,
|
||||
);
|
||||
(surface.layer(), render_elements)
|
||||
});
|
||||
|
||||
for (layer, elements) in layer_elements {
|
||||
match layer {
|
||||
wlr_layer::Layer::Background => background.extend(elements),
|
||||
wlr_layer::Layer::Bottom => bottom.extend(elements),
|
||||
wlr_layer::Layer::Top => top.extend(elements),
|
||||
wlr_layer::Layer::Overlay => overlay.extend(elements),
|
||||
}
|
||||
}
|
||||
|
||||
LayerRenderElements {
|
||||
background,
|
||||
bottom,
|
||||
top,
|
||||
overlay,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn generate_render_elements<R, T>(
|
||||
renderer: &mut R,
|
||||
space: &Space<WindowElement>,
|
||||
output: &Output,
|
||||
input_method: &InputMethodHandle,
|
||||
windows: &[WindowElement],
|
||||
pointer_location: Point<f64, Logical>,
|
||||
pointer_element: &mut PointerElement<T>,
|
||||
pointer_image: Option<&TextureBuffer<T>>,
|
||||
cursor_status: &mut CursorImageStatus,
|
||||
dnd_icon: Option<&WlSurface>,
|
||||
focus_stack: &[WindowElement],
|
||||
renderer: &mut R,
|
||||
output: &Output,
|
||||
input_method: &InputMethodHandle,
|
||||
pointer_element: &mut PointerElement<T>,
|
||||
pointer_image: Option<&TextureBuffer<T>>,
|
||||
) -> Vec<OutputRenderElements<R, WaylandSurfaceRenderElement<R>>>
|
||||
where
|
||||
R: Renderer<TextureId = T> + ImportAll + ImportMem,
|
||||
|
@ -210,24 +264,48 @@ where
|
|||
|
||||
output_render_elements
|
||||
} else {
|
||||
// render everything
|
||||
let space_render_elements =
|
||||
space::space_render_elements(renderer, [space], output, 1.0)
|
||||
.expect("Failed to get render elements");
|
||||
let LayerRenderElements {
|
||||
background,
|
||||
bottom,
|
||||
top,
|
||||
overlay,
|
||||
} = layer_render_elements(output, renderer);
|
||||
|
||||
let window_render_elements: Vec<WaylandSurfaceRenderElement<R>> =
|
||||
Tag::tag_render_elements(windows, space, renderer);
|
||||
|
||||
let mut output_render_elements =
|
||||
Vec::<OutputRenderElements<_, WaylandSurfaceRenderElement<_>>>::new();
|
||||
Vec::<OutputRenderElements<R, WaylandSurfaceRenderElement<R>>>::new();
|
||||
|
||||
// let space_render_elements =
|
||||
// smithay::desktop::space::space_render_elements(renderer, [space], output, 1.0)
|
||||
// .expect("failed to get space_render_elements");
|
||||
|
||||
// Elements render from top to bottom
|
||||
|
||||
output_render_elements.extend(
|
||||
custom_render_elements
|
||||
.into_iter()
|
||||
.map(OutputRenderElements::from),
|
||||
);
|
||||
|
||||
output_render_elements.extend(
|
||||
space_render_elements
|
||||
overlay
|
||||
.into_iter()
|
||||
.chain(top)
|
||||
.chain(window_render_elements)
|
||||
.chain(bottom)
|
||||
.chain(background)
|
||||
.map(CustomRenderElements::from)
|
||||
.map(OutputRenderElements::from),
|
||||
);
|
||||
|
||||
// output_render_elements.extend(
|
||||
// space_render_elements
|
||||
// .into_iter()
|
||||
// .map(OutputRenderElements::from),
|
||||
// );
|
||||
|
||||
output_render_elements
|
||||
}
|
||||
};
|
||||
|
|
|
@ -581,6 +581,9 @@ impl ApiState {
|
|||
|
||||
pub trait WithState {
|
||||
type State;
|
||||
/// Access data map state.
|
||||
///
|
||||
/// RefCell Safety: This function will panic if called within itself.
|
||||
fn with_state<F, T>(&self, func: F) -> T
|
||||
where
|
||||
F: FnMut(&mut Self::State) -> T;
|
||||
|
|
|
@ -4,6 +4,7 @@ use async_process::Stdio;
|
|||
use futures_lite::AsyncBufReadExt;
|
||||
use smithay::{
|
||||
desktop::space::SpaceElement,
|
||||
utils::Rectangle,
|
||||
wayland::{compositor, shell::xdg::XdgToplevelSurfaceData},
|
||||
};
|
||||
|
||||
|
@ -68,7 +69,10 @@ impl State {
|
|||
if let Some(height) = height {
|
||||
window_size.h = height;
|
||||
}
|
||||
window.request_size_change(&mut self.space, window_loc, window_size);
|
||||
window.change_geometry(Rectangle::from_loc_and_size(window_loc, window_size));
|
||||
if let Some(output) = window.output(self) {
|
||||
self.update_windows(&output);
|
||||
}
|
||||
}
|
||||
Msg::MoveWindowToTag { window_id, tag_id } => {
|
||||
let Some(window) = window_id.window(self) else { return };
|
||||
|
|
39
src/tag.rs
39
src/tag.rs
|
@ -7,11 +7,20 @@ use std::{
|
|||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use smithay::output::Output;
|
||||
use smithay::{
|
||||
backend::renderer::{
|
||||
element::{surface::WaylandSurfaceRenderElement, AsRenderElements},
|
||||
ImportAll, ImportMem, Renderer,
|
||||
},
|
||||
desktop::{space::SpaceElement, Space},
|
||||
output::Output,
|
||||
utils::Scale,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
layout::Layout,
|
||||
state::{State, WithState},
|
||||
window::WindowElement,
|
||||
};
|
||||
|
||||
static TAG_ID_COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||
|
@ -64,6 +73,7 @@ impl Eq for TagInner {}
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Tag(Rc<RefCell<TagInner>>);
|
||||
|
||||
// RefCell Safety: These methods should never panic because they are all self-contained or Copy.
|
||||
impl Tag {
|
||||
pub fn id(&self) -> TagId {
|
||||
self.0.borrow().id
|
||||
|
@ -107,4 +117,31 @@ impl Tag {
|
|||
.find(|output| output.with_state(|state| state.tags.iter().any(|tg| tg == self)))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Get the render_elements for the provided tags.
|
||||
pub fn tag_render_elements<R, C>(
|
||||
windows: &[WindowElement],
|
||||
space: &Space<WindowElement>,
|
||||
renderer: &mut R,
|
||||
) -> Vec<C>
|
||||
where
|
||||
R: Renderer + ImportAll + ImportMem,
|
||||
<R as Renderer>::TextureId: 'static,
|
||||
C: From<WaylandSurfaceRenderElement<R>>,
|
||||
{
|
||||
let elements = windows
|
||||
.iter()
|
||||
.rev() // rev because I treat the focus stack backwards vs how the renderer orders it
|
||||
.filter(|win| win.is_on_active_tag(space.outputs()))
|
||||
.flat_map(|win| {
|
||||
// subtract win.geometry().loc to align decorations correctly
|
||||
let loc = (space.element_location(win).unwrap_or((0, 0).into())
|
||||
- win.geometry().loc)
|
||||
.to_physical(1);
|
||||
win.render_elements::<C>(renderer, loc, Scale::from(1.0), 1.0)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
elements
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use smithay::{
|
|||
take_presentation_feedback_surface_tree, under_from_surface_tree,
|
||||
with_surfaces_surface_tree, OutputPresentationFeedback,
|
||||
},
|
||||
Space, Window, WindowSurfaceType,
|
||||
Window, WindowSurfaceType,
|
||||
},
|
||||
input::{
|
||||
keyboard::{KeyboardTarget, KeysymHandle, ModifiersState},
|
||||
|
@ -23,7 +23,7 @@ use smithay::{
|
|||
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback,
|
||||
wayland_server::protocol::wl_surface::WlSurface,
|
||||
},
|
||||
utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial, Size},
|
||||
utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial},
|
||||
wayland::{
|
||||
compositor::{self, Blocker, BlockerState, SurfaceData},
|
||||
dmabuf::DmabufFeedback,
|
||||
|
@ -203,39 +203,6 @@ impl WindowElement {
|
|||
});
|
||||
}
|
||||
|
||||
/// Request a size and loc change.
|
||||
pub fn request_size_change(
|
||||
&self,
|
||||
space: &mut Space<WindowElement>,
|
||||
new_loc: Point<i32, Logical>,
|
||||
new_size: Size<i32, Logical>,
|
||||
) {
|
||||
match self {
|
||||
WindowElement::Wayland(window) => {
|
||||
window.toplevel().with_pending_state(|state| {
|
||||
state.size = Some(new_size);
|
||||
});
|
||||
self.with_state(|state| {
|
||||
state.loc_request_state =
|
||||
LocationRequestState::Requested(window.toplevel().send_configure(), new_loc)
|
||||
});
|
||||
}
|
||||
WindowElement::X11(surface) => {
|
||||
tracing::debug!("sending size change to x11 win");
|
||||
surface
|
||||
.configure(Rectangle::from_loc_and_size(new_loc, new_size))
|
||||
.expect("failed to configure x11 win");
|
||||
|
||||
if !surface.is_override_redirect() {
|
||||
surface
|
||||
.set_mapped(true)
|
||||
.expect("failed to set x11 win to mapped");
|
||||
}
|
||||
space.map_element(self.clone(), new_loc, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn class(&self) -> Option<String> {
|
||||
match self {
|
||||
WindowElement::Wayland(window) => {
|
||||
|
@ -279,6 +246,21 @@ impl WindowElement {
|
|||
self.with_state(|st| st.tags.first().and_then(|tag| tag.output(state)))
|
||||
}
|
||||
|
||||
/// RefCell Safety: This uses RefCells on both `self` and everything in `outputs`.
|
||||
pub fn is_on_active_tag<'a>(&self, outputs: impl IntoIterator<Item = &'a Output>) -> bool {
|
||||
let tags = outputs
|
||||
.into_iter()
|
||||
.flat_map(|op| op.with_state(|state| state.focused_tags().cloned().collect::<Vec<_>>()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.with_state(|state| {
|
||||
state
|
||||
.tags
|
||||
.iter()
|
||||
.any(|tag| tags.iter().any(|tag2| tag == tag2))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if the window element is [`Wayland`].
|
||||
///
|
||||
/// [`Wayland`]: WindowElement::Wayland
|
||||
|
|
|
@ -122,14 +122,6 @@ impl LocationRequestState {
|
|||
pub fn is_idle(&self) -> bool {
|
||||
matches!(self, Self::Idle)
|
||||
}
|
||||
|
||||
/// Returns `true` if the location request state is [`Acknowledged`].
|
||||
///
|
||||
/// [`Acknowledged`]: LocationRequestState::Acknowledged
|
||||
#[must_use]
|
||||
pub fn is_acknowledged(&self) -> bool {
|
||||
matches!(self, Self::Acknowledged(..))
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowElement {
|
||||
|
|
Loading…
Add table
Reference in a new issue