mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-02-05 20:46:27 +01:00
Improve window state handling
This commit is contained in:
parent
7896d88c8c
commit
c741c2cfd6
14 changed files with 432 additions and 411 deletions
|
@ -94,26 +94,21 @@ impl window_service_server::WindowService for WindowService {
|
||||||
window_loc.x = x.unwrap_or(window_loc.x);
|
window_loc.x = x.unwrap_or(window_loc.x);
|
||||||
window_loc.y = y.unwrap_or(window_loc.y);
|
window_loc.y = y.unwrap_or(window_loc.y);
|
||||||
|
|
||||||
|
// TODO: window.geometry.size or space.elem_geo
|
||||||
let mut window_size = window.geometry().size;
|
let mut window_size = window.geometry().size;
|
||||||
window_size.w = width.unwrap_or(window_size.w);
|
window_size.w = width.unwrap_or(window_size.w);
|
||||||
window_size.h = height.unwrap_or(window_size.h);
|
window_size.h = height.unwrap_or(window_size.h);
|
||||||
|
|
||||||
window.with_state_mut(|state| {
|
window.with_state_mut(|state| {
|
||||||
use crate::window::window_state::FloatingOrTiled;
|
state.floating_loc = Some(window_loc.to_f64());
|
||||||
state.floating_or_tiled = match state.floating_or_tiled {
|
state.floating_size = Some(window_size);
|
||||||
FloatingOrTiled::Floating { .. } => FloatingOrTiled::Floating {
|
|
||||||
loc: window_loc.to_f64(),
|
|
||||||
size: window_size,
|
|
||||||
},
|
|
||||||
FloatingOrTiled::Tiled(_) => {
|
|
||||||
FloatingOrTiled::Tiled(Some((window_loc.to_f64(), window_size)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for output in state.pinnacle.space.outputs_for_element(&window) {
|
if window.with_state(|state| state.floating_or_tiled.is_floating()) {
|
||||||
state.pinnacle.request_layout(&output);
|
window.change_geometry(window_loc.to_f64(), window_size);
|
||||||
state.schedule_render(&output);
|
if let Some(toplevel) = window.toplevel() {
|
||||||
|
toplevel.send_pending_configure();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
@ -150,11 +145,11 @@ impl window_service_server::WindowService for WindowService {
|
||||||
};
|
};
|
||||||
|
|
||||||
match fullscreen {
|
match fullscreen {
|
||||||
Some(fullscreen) => state.set_window_fullscreen(&window, fullscreen),
|
Some(fullscreen) => state.set_window_fullscreen_and_layout(&window, fullscreen),
|
||||||
None => {
|
None => {
|
||||||
let is_fullscreen = window
|
let is_fullscreen = window
|
||||||
.with_state(|win_state| win_state.fullscreen_or_maximized.is_fullscreen());
|
.with_state(|win_state| win_state.fullscreen_or_maximized.is_fullscreen());
|
||||||
state.set_window_fullscreen(&window, !is_fullscreen);
|
state.set_window_fullscreen_and_layout(&window, !is_fullscreen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -192,11 +187,11 @@ impl window_service_server::WindowService for WindowService {
|
||||||
};
|
};
|
||||||
|
|
||||||
match maximized {
|
match maximized {
|
||||||
Some(maximized) => state.set_window_maximized(&window, maximized),
|
Some(maximized) => state.set_window_maximized_and_layout(&window, maximized),
|
||||||
None => {
|
None => {
|
||||||
let is_maximized = window
|
let is_maximized = window
|
||||||
.with_state(|win_state| win_state.fullscreen_or_maximized.is_maximized());
|
.with_state(|win_state| win_state.fullscreen_or_maximized.is_maximized());
|
||||||
state.set_window_maximized(&window, !is_maximized);
|
state.set_window_maximized_and_layout(&window, !is_maximized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -226,26 +221,22 @@ impl window_service_server::WindowService for WindowService {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let floating = match set_or_toggle {
|
||||||
|
SetOrToggle::Unspecified => unreachable!(),
|
||||||
|
SetOrToggle::Set => true,
|
||||||
|
SetOrToggle::Unset => false,
|
||||||
|
SetOrToggle::Toggle => {
|
||||||
|
window.with_state(|state| !state.floating_or_tiled.is_floating())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let output = window.output(&state.pinnacle);
|
let output = window.output(&state.pinnacle);
|
||||||
|
|
||||||
if let Some(output) = output.as_ref() {
|
if let Some(output) = output.as_ref() {
|
||||||
state.capture_snapshots_on_output(output, [window.clone()]);
|
state.capture_snapshots_on_output(output, [window.clone()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
match set_or_toggle {
|
state.pinnacle.set_window_floating(&window, floating);
|
||||||
SetOrToggle::Set => {
|
|
||||||
if !window.with_state(|state| state.floating_or_tiled.is_floating()) {
|
|
||||||
window.toggle_floating();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetOrToggle::Unset => {
|
|
||||||
if window.with_state(|state| state.floating_or_tiled.is_floating()) {
|
|
||||||
window.toggle_floating();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SetOrToggle::Toggle => window.toggle_floating(),
|
|
||||||
SetOrToggle::Unspecified => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(output) = output else {
|
let Some(output) = output else {
|
||||||
return;
|
return;
|
||||||
|
@ -795,8 +786,8 @@ impl From<WindowRule> for crate::window::rules::WindowRule {
|
||||||
false => Some(rule.tags.into_iter().map(TagId).collect::<Vec<_>>()),
|
false => Some(rule.tags.into_iter().map(TagId).collect::<Vec<_>>()),
|
||||||
};
|
};
|
||||||
let floating_or_tiled = rule.floating.map(|floating| match floating {
|
let floating_or_tiled = rule.floating.map(|floating| match floating {
|
||||||
true => crate::window::rules::FloatingOrTiled::Floating,
|
true => crate::window::window_state::FloatingOrTiled::Floating,
|
||||||
false => crate::window::rules::FloatingOrTiled::Tiled,
|
false => crate::window::window_state::FloatingOrTiled::Tiled,
|
||||||
});
|
});
|
||||||
let size = rule.width.and_then(|w| {
|
let size = rule.width.and_then(|w| {
|
||||||
rule.height.and_then(|h| {
|
rule.height.and_then(|h| {
|
||||||
|
|
|
@ -21,7 +21,7 @@ use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
state::{State, WithState},
|
state::{State, WithState},
|
||||||
window::{window_state::FloatingOrTiled, WindowElement},
|
window::WindowElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Data for moving a window.
|
/// Data for moving a window.
|
||||||
|
@ -70,11 +70,39 @@ impl PointerGrab<State> for MoveSurfaceGrab {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_tiled = self
|
let can_move = self.window.with_state(|state| {
|
||||||
.window
|
state.floating_or_tiled.is_floating() && state.fullscreen_or_maximized.is_neither()
|
||||||
.with_state(|state| state.floating_or_tiled.is_tiled());
|
});
|
||||||
|
|
||||||
if is_tiled {
|
if can_move {
|
||||||
|
let delta = event.location - self.start_data.location;
|
||||||
|
let new_loc = self.initial_window_loc.to_f64() + delta;
|
||||||
|
// FIXME: space maps locs as i32 not f64
|
||||||
|
state
|
||||||
|
.pinnacle
|
||||||
|
.space
|
||||||
|
.map_element(self.window.clone(), new_loc.to_i32_round(), true);
|
||||||
|
|
||||||
|
self.window.with_state_mut(|state| {
|
||||||
|
state.floating_loc = Some(new_loc);
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(surface) = self.window.x11_surface() {
|
||||||
|
if !surface.is_override_redirect() {
|
||||||
|
let geo = surface.geometry();
|
||||||
|
// FIXME: prolly not fixable but xwayland configures with loc i32 not f64
|
||||||
|
let new_geo = Rectangle::from_loc_and_size(new_loc.to_i32_round(), geo.size);
|
||||||
|
surface
|
||||||
|
.configure(new_geo)
|
||||||
|
.expect("failed to configure x11 win");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let outputs = state.pinnacle.space.outputs_for_element(&self.window);
|
||||||
|
for output in outputs {
|
||||||
|
state.schedule_render(&output);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// INFO: this is being used instead of space.element_under(event.location) because that
|
// INFO: this is being used instead of space.element_under(event.location) because that
|
||||||
// | uses the bounding box, which is different from the actual geometry
|
// | uses the bounding box, which is different from the actual geometry
|
||||||
let window_under = state
|
let window_under = state
|
||||||
|
@ -114,43 +142,6 @@ impl PointerGrab<State> for MoveSurfaceGrab {
|
||||||
.pinnacle
|
.pinnacle
|
||||||
.swap_window_positions(&self.window, &window_under);
|
.swap_window_positions(&self.window, &window_under);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
let delta = event.location - self.start_data.location;
|
|
||||||
let new_loc = self.initial_window_loc.to_f64() + delta;
|
|
||||||
// FIXME: space maps locs as i32 not f64
|
|
||||||
state
|
|
||||||
.pinnacle
|
|
||||||
.space
|
|
||||||
.map_element(self.window.clone(), new_loc.to_i32_round(), true);
|
|
||||||
|
|
||||||
let size = state
|
|
||||||
.pinnacle
|
|
||||||
.space
|
|
||||||
.element_geometry(&self.window)
|
|
||||||
.expect("window wasn't mapped")
|
|
||||||
.size;
|
|
||||||
|
|
||||||
self.window.with_state_mut(|state| {
|
|
||||||
if state.floating_or_tiled.is_floating() {
|
|
||||||
state.floating_or_tiled = FloatingOrTiled::Floating { loc: new_loc, size };
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(surface) = self.window.x11_surface() {
|
|
||||||
if !surface.is_override_redirect() {
|
|
||||||
let geo = surface.geometry();
|
|
||||||
// FIXME: prolly not fixable but xwayland configures with loc i32 not f64
|
|
||||||
let new_geo = Rectangle::from_loc_and_size(new_loc.to_i32_round(), geo.size);
|
|
||||||
surface
|
|
||||||
.configure(new_geo)
|
|
||||||
.expect("failed to configure x11 win");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let outputs = state.pinnacle.space.outputs_for_element(&self.window);
|
|
||||||
for output in outputs {
|
|
||||||
state.schedule_render(&output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ use smithay::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
state::{Pinnacle, State, WithState},
|
state::{Pinnacle, State, WithState},
|
||||||
window::{window_state::FloatingOrTiled, WindowElement},
|
window::WindowElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -451,19 +451,8 @@ impl Pinnacle {
|
||||||
window_loc.y = new_y;
|
window_loc.y = new_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = self
|
|
||||||
.space
|
|
||||||
.element_geometry(&window)
|
|
||||||
.expect("called element_geometry on unmapped window")
|
|
||||||
.size;
|
|
||||||
|
|
||||||
window.with_state_mut(|state| {
|
window.with_state_mut(|state| {
|
||||||
if state.floating_or_tiled.is_floating() {
|
state.floating_loc = Some(window_loc);
|
||||||
state.floating_or_tiled = FloatingOrTiled::Floating {
|
|
||||||
loc: window_loc,
|
|
||||||
size,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if new_loc.0.is_some() || new_loc.1.is_some() {
|
if new_loc.0.is_some() || new_loc.1.is_some() {
|
||||||
|
|
|
@ -73,7 +73,7 @@ use smithay::{
|
||||||
},
|
},
|
||||||
shell::{
|
shell::{
|
||||||
wlr_layer::{self, Layer, LayerSurfaceData, WlrLayerShellHandler, WlrLayerShellState},
|
wlr_layer::{self, Layer, LayerSurfaceData, WlrLayerShellHandler, WlrLayerShellState},
|
||||||
xdg::{PopupSurface, XdgPopupSurfaceData, XdgToplevelSurfaceData},
|
xdg::{PopupSurface, SurfaceCachedState, XdgPopupSurfaceData, XdgToplevelSurfaceData},
|
||||||
},
|
},
|
||||||
shm::{ShmHandler, ShmState},
|
shm::{ShmHandler, ShmState},
|
||||||
tablet_manager::TabletSeatHandler,
|
tablet_manager::TabletSeatHandler,
|
||||||
|
@ -103,6 +103,7 @@ use crate::{
|
||||||
screencopy::{Screencopy, ScreencopyHandler},
|
screencopy::{Screencopy, ScreencopyHandler},
|
||||||
},
|
},
|
||||||
state::{ClientState, Pinnacle, State, WithState},
|
state::{ClientState, Pinnacle, State, WithState},
|
||||||
|
window::window_state::FloatingOrTiled,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl BufferHandler for State {
|
impl BufferHandler for State {
|
||||||
|
@ -213,6 +214,39 @@ impl CompositorHandler for State {
|
||||||
self.capture_snapshots_on_output(output, []);
|
self.capture_snapshots_on_output(output, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unmapped_window.with_state_mut(|state| {
|
||||||
|
if state.floating_size.is_none() {
|
||||||
|
state.floating_size = Some(unmapped_window.geometry().size);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Float windows if necessary
|
||||||
|
if let Some(toplevel) = unmapped_window.toplevel() {
|
||||||
|
let has_parent = toplevel.parent().is_some();
|
||||||
|
let (min_size, max_size) =
|
||||||
|
compositor::with_states(toplevel.wl_surface(), |states| {
|
||||||
|
let mut guard = states.cached_state.get::<SurfaceCachedState>();
|
||||||
|
let state = guard.current();
|
||||||
|
(state.min_size, state.max_size)
|
||||||
|
});
|
||||||
|
|
||||||
|
let requests_constrained_size = min_size.w > 0
|
||||||
|
&& min_size.h > 0
|
||||||
|
&& (min_size.w == max_size.w || min_size.h == max_size.h);
|
||||||
|
|
||||||
|
let should_float = has_parent || requests_constrained_size;
|
||||||
|
|
||||||
|
if should_float {
|
||||||
|
unmapped_window.with_state_mut(|state| {
|
||||||
|
state.floating_or_tiled = FloatingOrTiled::Floating
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if unmapped_window.with_state(|state| state.floating_or_tiled.is_floating()) {
|
||||||
|
self.pinnacle.set_window_floating(&unmapped_window, true);
|
||||||
|
}
|
||||||
|
|
||||||
self.pinnacle
|
self.pinnacle
|
||||||
.unmapped_windows
|
.unmapped_windows
|
||||||
.retain(|win| win != unmapped_window);
|
.retain(|win| win != unmapped_window);
|
||||||
|
@ -224,8 +258,27 @@ impl CompositorHandler for State {
|
||||||
if unmapped_window.is_on_active_tag() {
|
if unmapped_window.is_on_active_tag() {
|
||||||
self.update_keyboard_focus(&focused_output);
|
self.update_keyboard_focus(&focused_output);
|
||||||
|
|
||||||
self.pinnacle.begin_layout_transaction(&focused_output);
|
if unmapped_window.with_state(|state| {
|
||||||
self.pinnacle.request_layout(&focused_output);
|
state.floating_or_tiled.is_floating()
|
||||||
|
&& state.fullscreen_or_maximized.is_neither()
|
||||||
|
}) {
|
||||||
|
// TODO: make this sync with commit
|
||||||
|
let loc = unmapped_window
|
||||||
|
.with_state(|state| state.floating_loc)
|
||||||
|
.unwrap();
|
||||||
|
self.pinnacle.space.map_element(
|
||||||
|
unmapped_window.clone(),
|
||||||
|
loc.to_i32_round(),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
unmapped_window
|
||||||
|
.toplevel()
|
||||||
|
.expect("unreachable")
|
||||||
|
.send_pending_configure();
|
||||||
|
} else {
|
||||||
|
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
|
// It seems wlcs needs immediate frame sends for client tests to work
|
||||||
#[cfg(feature = "testing")]
|
#[cfg(feature = "testing")]
|
||||||
|
@ -242,6 +295,17 @@ impl CompositorHandler for State {
|
||||||
unmapped_window.place_on_output(&output);
|
unmapped_window.place_on_output(&output);
|
||||||
}
|
}
|
||||||
self.pinnacle.apply_window_rules(&unmapped_window);
|
self.pinnacle.apply_window_rules(&unmapped_window);
|
||||||
|
if unmapped_window.with_state(|state| {
|
||||||
|
state.floating_or_tiled.is_floating()
|
||||||
|
&& state.fullscreen_or_maximized.is_neither()
|
||||||
|
}) {
|
||||||
|
if let Some(size) = unmapped_window.with_state(|state| state.floating_size)
|
||||||
|
{
|
||||||
|
if let Some(toplevel) = unmapped_window.toplevel() {
|
||||||
|
toplevel.with_pending_state(|state| state.size = Some(size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// Still unmapped
|
// Still unmapped
|
||||||
unmapped_window.on_commit();
|
unmapped_window.on_commit();
|
||||||
self.pinnacle.ensure_initial_configure(surface);
|
self.pinnacle.ensure_initial_configure(surface);
|
||||||
|
|
|
@ -60,7 +60,7 @@ impl ForeignToplevelHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_fullscreen(&window, true);
|
self.set_window_fullscreen_and_layout(&window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unset_fullscreen(&mut self, wl_surface: WlSurface) {
|
fn unset_fullscreen(&mut self, wl_surface: WlSurface) {
|
||||||
|
@ -68,7 +68,7 @@ impl ForeignToplevelHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_fullscreen(&window, false);
|
self.set_window_fullscreen_and_layout(&window, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_maximized(&mut self, wl_surface: WlSurface) {
|
fn set_maximized(&mut self, wl_surface: WlSurface) {
|
||||||
|
@ -76,7 +76,7 @@ impl ForeignToplevelHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_maximized(&window, true);
|
self.set_window_maximized_and_layout(&window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unset_maximized(&mut self, wl_surface: WlSurface) {
|
fn unset_maximized(&mut self, wl_surface: WlSurface) {
|
||||||
|
@ -84,7 +84,7 @@ impl ForeignToplevelHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_maximized(&window, false);
|
self.set_window_maximized_and_layout(&window, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_minimized(&mut self, wl_surface: WlSurface) {
|
fn set_minimized(&mut self, wl_surface: WlSurface) {
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
use crate::{
|
use crate::{state::State, window::WindowElement};
|
||||||
state::{State, WithState},
|
|
||||||
window::WindowElement,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn set_window_maximized(&mut self, window: &WindowElement, maximized: bool) {
|
pub fn set_window_maximized_and_layout(&mut self, window: &WindowElement, maximized: bool) {
|
||||||
let output = window.output(&self.pinnacle);
|
let output = window.output(&self.pinnacle);
|
||||||
if let Some(output) = output.as_ref() {
|
if let Some(output) = output.as_ref() {
|
||||||
self.capture_snapshots_on_output(output, [window.clone()]);
|
self.capture_snapshots_on_output(output, [window.clone()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if maximized {
|
self.pinnacle.set_window_maximized(window, maximized);
|
||||||
if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
|
|
||||||
window.toggle_maximized();
|
|
||||||
}
|
|
||||||
} else if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
|
|
||||||
window.toggle_maximized();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
self.pinnacle.begin_layout_transaction(&output);
|
self.pinnacle.begin_layout_transaction(&output);
|
||||||
|
@ -26,19 +17,13 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_window_fullscreen(&mut self, window: &WindowElement, fullscreen: bool) {
|
pub fn set_window_fullscreen_and_layout(&mut self, window: &WindowElement, fullscreen: bool) {
|
||||||
let output = window.output(&self.pinnacle);
|
let output = window.output(&self.pinnacle);
|
||||||
if let Some(output) = output.as_ref() {
|
if let Some(output) = output.as_ref() {
|
||||||
self.capture_snapshots_on_output(output, [window.clone()]);
|
self.capture_snapshots_on_output(output, [window.clone()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if fullscreen {
|
self.pinnacle.set_window_fullscreen(window, fullscreen);
|
||||||
if !window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
|
|
||||||
window.toggle_fullscreen();
|
|
||||||
}
|
|
||||||
} else if window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
|
|
||||||
window.toggle_fullscreen();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(output) = window.output(&self.pinnacle) {
|
if let Some(output) = window.output(&self.pinnacle) {
|
||||||
self.pinnacle.begin_layout_transaction(&output);
|
self.pinnacle.begin_layout_transaction(&output);
|
||||||
|
|
|
@ -231,7 +231,7 @@ impl XdgShellHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_fullscreen(&window, true);
|
self.set_window_fullscreen_and_layout(&window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
surface.send_configure();
|
surface.send_configure();
|
||||||
|
@ -246,7 +246,7 @@ impl XdgShellHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_fullscreen(&window, false);
|
self.set_window_fullscreen_and_layout(&window, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maximize_request(&mut self, surface: ToplevelSurface) {
|
fn maximize_request(&mut self, surface: ToplevelSurface) {
|
||||||
|
@ -254,7 +254,7 @@ impl XdgShellHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_maximized(&window, true);
|
self.set_window_maximized_and_layout(&window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmaximize_request(&mut self, surface: ToplevelSurface) {
|
fn unmaximize_request(&mut self, surface: ToplevelSurface) {
|
||||||
|
@ -262,7 +262,7 @@ impl XdgShellHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_maximized(&window, false);
|
self.set_window_maximized_and_layout(&window, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn minimize_request(&mut self, _surface: ToplevelSurface) {
|
fn minimize_request(&mut self, _surface: ToplevelSurface) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use std::{process::Stdio, time::Duration};
|
use std::{process::Stdio, time::Duration};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
desktop::Window,
|
desktop::{space::SpaceElement, Window},
|
||||||
input::pointer::CursorIcon,
|
input::pointer::CursorIcon,
|
||||||
utils::{Logical, Point, Rectangle, Size, SERIAL_COUNTER},
|
utils::{Logical, Point, Rectangle, Size, SERIAL_COUNTER},
|
||||||
wayland::selection::{
|
wayland::selection::{
|
||||||
|
@ -27,7 +27,7 @@ use tracing::{debug, error, trace, warn};
|
||||||
use crate::{
|
use crate::{
|
||||||
focus::keyboard::KeyboardFocusTarget,
|
focus::keyboard::KeyboardFocusTarget,
|
||||||
state::{Pinnacle, State, WithState},
|
state::{Pinnacle, State, WithState},
|
||||||
window::{window_state::FloatingOrTiled, WindowElement},
|
window::WindowElement,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl XwmHandler for State {
|
impl XwmHandler for State {
|
||||||
|
@ -47,8 +47,14 @@ impl XwmHandler for State {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
surface.set_mapped(true).expect("failed to map x11 window");
|
||||||
let window = WindowElement::new(Window::new_x11_window(surface));
|
let window = WindowElement::new(Window::new_x11_window(surface));
|
||||||
let bbox = window.bbox();
|
|
||||||
|
if let Some(output) = self.pinnacle.focused_output() {
|
||||||
|
window.place_on_output(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pinnacle.apply_window_rules(&window);
|
||||||
|
|
||||||
let output_size = self
|
let output_size = self
|
||||||
.pinnacle
|
.pinnacle
|
||||||
|
@ -63,13 +69,17 @@ impl XwmHandler for State {
|
||||||
.map(|op| op.current_location())
|
.map(|op| op.current_location())
|
||||||
.unwrap_or((0, 0).into());
|
.unwrap_or((0, 0).into());
|
||||||
|
|
||||||
|
let size = window
|
||||||
|
.with_state(|state| state.floating_size)
|
||||||
|
.unwrap_or(window.bbox().size);
|
||||||
|
|
||||||
// Center the popup in the middle of the output.
|
// Center the popup in the middle of the output.
|
||||||
// Once I find a way to get an X11Surface's parent it will be centered on the parent if
|
// Once I find a way to get an X11Surface's parent it will be centered on the parent if
|
||||||
// applicable.
|
// applicable.
|
||||||
// FIXME: loc is i32
|
// FIXME: loc is i32
|
||||||
let loc: Point<i32, Logical> = (
|
let loc: Point<i32, Logical> = (
|
||||||
output_loc.x + output_size.w / 2 - bbox.size.w / 2,
|
output_loc.x + output_size.w / 2 - size.w / 2,
|
||||||
output_loc.y + output_size.h / 2 - bbox.size.h / 2,
|
output_loc.y + output_size.h / 2 - size.h / 2,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
@ -77,30 +87,27 @@ impl XwmHandler for State {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
};
|
};
|
||||||
|
|
||||||
surface.set_mapped(true).expect("failed to map x11 window");
|
let geo = Rectangle::from_loc_and_size(loc, size);
|
||||||
|
|
||||||
let bbox = Rectangle::from_loc_and_size(loc, bbox.size);
|
|
||||||
|
|
||||||
surface
|
surface
|
||||||
.configure(bbox)
|
.configure(geo)
|
||||||
.expect("failed to configure x11 window");
|
.expect("failed to configure x11 window");
|
||||||
|
|
||||||
if let Some(output) = self.pinnacle.focused_output() {
|
let will_float = should_float(surface)
|
||||||
window.place_on_output(output);
|
|| window.with_state(|state| state.floating_or_tiled.is_floating());
|
||||||
}
|
|
||||||
|
|
||||||
if should_float(surface) {
|
if will_float {
|
||||||
window.with_state_mut(|state| {
|
window.with_state_mut(|state| {
|
||||||
state.floating_or_tiled = FloatingOrTiled::Floating {
|
if state.floating_loc.is_none() {
|
||||||
loc: bbox.loc.to_f64(),
|
state.floating_loc = Some(geo.loc.to_f64());
|
||||||
size: bbox.size,
|
}
|
||||||
|
if state.floating_size.is_none() {
|
||||||
|
tracing::info!(?geo.size);
|
||||||
|
state.floating_size = Some(geo.size);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.pinnacle.space.map_element(window.clone(), loc, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: do snapshot and transaction here BUT ONLY IF TILED AND ON ACTIVE TAG
|
|
||||||
|
|
||||||
let output = window.output(&self.pinnacle);
|
let output = window.output(&self.pinnacle);
|
||||||
|
|
||||||
if let Some(output) = output.as_ref() {
|
if let Some(output) = output.as_ref() {
|
||||||
|
@ -110,15 +117,18 @@ impl XwmHandler for State {
|
||||||
self.pinnacle.windows.push(window.clone());
|
self.pinnacle.windows.push(window.clone());
|
||||||
self.pinnacle.raise_window(window.clone(), true);
|
self.pinnacle.raise_window(window.clone(), true);
|
||||||
|
|
||||||
self.pinnacle.apply_window_rules(&window);
|
|
||||||
|
|
||||||
if window.is_on_active_tag() {
|
if window.is_on_active_tag() {
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
output.with_state_mut(|state| state.focus_stack.set_focus(window.clone()));
|
output.with_state_mut(|state| state.focus_stack.set_focus(window.clone()));
|
||||||
self.update_keyboard_focus(&output);
|
self.update_keyboard_focus(&output);
|
||||||
|
|
||||||
self.pinnacle.begin_layout_transaction(&output);
|
if will_float {
|
||||||
self.pinnacle.request_layout(&output);
|
self.pinnacle.set_window_floating(&window, true);
|
||||||
|
self.pinnacle.space.map_element(window.clone(), loc, true);
|
||||||
|
} else {
|
||||||
|
self.pinnacle.begin_layout_transaction(&output);
|
||||||
|
self.pinnacle.request_layout(&output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +194,8 @@ impl XwmHandler for State {
|
||||||
_reorder: Option<Reorder>,
|
_reorder: Option<Reorder>,
|
||||||
) {
|
) {
|
||||||
trace!("XwmHandler::configure_request");
|
trace!("XwmHandler::configure_request");
|
||||||
let floating_or_override_redirect = self
|
tracing::info!(?x, ?y, ?w, ?h);
|
||||||
|
let should_configure = self
|
||||||
.pinnacle
|
.pinnacle
|
||||||
.windows
|
.windows
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -193,9 +204,11 @@ impl XwmHandler for State {
|
||||||
win.is_x11_override_redirect()
|
win.is_x11_override_redirect()
|
||||||
|| win.with_state(|state| state.floating_or_tiled.is_floating())
|
|| win.with_state(|state| state.floating_or_tiled.is_floating())
|
||||||
})
|
})
|
||||||
.unwrap_or(false);
|
.unwrap_or(true);
|
||||||
|
// If we unwrap_or here then the window hasn't requested a map yet.
|
||||||
|
// In that case, grant the configure. Xterm wants this to map properly, for example.
|
||||||
|
|
||||||
if floating_or_override_redirect {
|
if should_configure {
|
||||||
let mut geo = window.geometry();
|
let mut geo = window.geometry();
|
||||||
|
|
||||||
if let Some(x) = x {
|
if let Some(x) = x {
|
||||||
|
@ -211,6 +224,8 @@ impl XwmHandler for State {
|
||||||
geo.size.h = h as i32;
|
geo.size.h = h as i32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::info!(?geo, "configure_request");
|
||||||
|
|
||||||
if let Err(err) = window.configure(geo) {
|
if let Err(err) = window.configure(geo) {
|
||||||
error!("Failed to configure x11 win: {err}");
|
error!("Failed to configure x11 win: {err}");
|
||||||
}
|
}
|
||||||
|
@ -248,7 +263,7 @@ impl XwmHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_maximized(&window, true);
|
self.set_window_maximized_and_layout(&window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
|
@ -259,7 +274,7 @@ impl XwmHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_maximized(&window, false);
|
self.set_window_maximized_and_layout(&window, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
|
@ -270,7 +285,7 @@ impl XwmHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_fullscreen(&window, true);
|
self.set_window_fullscreen_and_layout(&window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
|
||||||
|
@ -281,7 +296,7 @@ impl XwmHandler for State {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_window_fullscreen(&window, true);
|
self.set_window_fullscreen_and_layout(&window, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize_request(
|
fn resize_request(
|
||||||
|
|
|
@ -17,10 +17,7 @@ use tracing::warn;
|
||||||
use crate::{
|
use crate::{
|
||||||
output::OutputName,
|
output::OutputName,
|
||||||
state::{Pinnacle, State, WithState},
|
state::{Pinnacle, State, WithState},
|
||||||
window::{
|
window::{window_state::FullscreenOrMaximized, WindowElement},
|
||||||
window_state::{FloatingOrTiled, FullscreenOrMaximized},
|
|
||||||
WindowElement,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::transaction::LayoutTransaction;
|
use self::transaction::LayoutTransaction;
|
||||||
|
@ -44,6 +41,13 @@ impl Pinnacle {
|
||||||
});
|
});
|
||||||
|
|
||||||
for win in to_unmap {
|
for win in to_unmap {
|
||||||
|
if win.with_state(|state| {
|
||||||
|
state.floating_or_tiled.is_floating() && state.fullscreen_or_maximized.is_neither()
|
||||||
|
}) {
|
||||||
|
if let Some(loc) = self.space.element_location(&win) {
|
||||||
|
win.with_state_mut(|state| state.floating_loc = Some(loc.to_f64()));
|
||||||
|
}
|
||||||
|
}
|
||||||
self.space.unmap_elem(&win);
|
self.space.unmap_elem(&win);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,33 +75,33 @@ impl Pinnacle {
|
||||||
|
|
||||||
for (win, geo) in zipped.by_ref() {
|
for (win, geo) in zipped.by_ref() {
|
||||||
win.change_geometry(geo.loc.to_f64(), geo.size);
|
win.change_geometry(geo.loc.to_f64(), geo.size);
|
||||||
|
self.space.map_element(win, geo.loc, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (remaining_wins, _remaining_geos) = zipped.unzip::<_, _, Vec<_>, Vec<_>>();
|
let (remaining_wins, _remaining_geos) = zipped.unzip::<_, _, Vec<_>, Vec<_>>();
|
||||||
|
|
||||||
for win in remaining_wins {
|
for win in remaining_wins {
|
||||||
assert!(win.with_state(|state| state.floating_or_tiled.is_floating()));
|
assert!(win.with_state(|state| state.floating_or_tiled.is_floating()));
|
||||||
win.toggle_floating();
|
self.set_window_floating(&win, true);
|
||||||
|
if let Some(toplevel) = win.toplevel() {
|
||||||
|
toplevel.send_pending_configure();
|
||||||
|
}
|
||||||
|
// TODO: will prolly need to map here
|
||||||
}
|
}
|
||||||
|
|
||||||
for window in windows_on_foc_tags.iter() {
|
for window in windows_on_foc_tags.iter() {
|
||||||
match window.with_state(|state| state.fullscreen_or_maximized) {
|
match window.with_state(|state| state.fullscreen_or_maximized) {
|
||||||
FullscreenOrMaximized::Fullscreen => {
|
FullscreenOrMaximized::Fullscreen => {
|
||||||
window.change_geometry(output_geo.loc.to_f64(), output_geo.size);
|
window.change_geometry(output_geo.loc.to_f64(), output_geo.size);
|
||||||
|
self.space
|
||||||
|
.map_element(window.clone(), output_geo.loc, false);
|
||||||
}
|
}
|
||||||
FullscreenOrMaximized::Maximized => {
|
FullscreenOrMaximized::Maximized => {
|
||||||
window.change_geometry(
|
let loc = output_geo.loc + non_exclusive_geo.loc;
|
||||||
(output_geo.loc + non_exclusive_geo.loc).to_f64(),
|
window.change_geometry(loc.to_f64(), non_exclusive_geo.size);
|
||||||
non_exclusive_geo.size,
|
self.space.map_element(window.clone(), loc, false);
|
||||||
);
|
|
||||||
}
|
|
||||||
FullscreenOrMaximized::Neither => {
|
|
||||||
if let FloatingOrTiled::Floating { loc, size } =
|
|
||||||
window.with_state(|state| state.floating_or_tiled)
|
|
||||||
{
|
|
||||||
window.change_geometry(loc, size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
FullscreenOrMaximized::Neither => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,10 +114,17 @@ impl Pinnacle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: get rid of target_loc
|
let floating_loc = win
|
||||||
let loc = win.with_state_mut(|state| state.target_loc.take());
|
.with_state(|state| {
|
||||||
if let Some(loc) = loc {
|
let should_map = state.floating_or_tiled.is_floating()
|
||||||
self.space.map_element(win.clone(), loc, false);
|
&& state.fullscreen_or_maximized.is_neither();
|
||||||
|
|
||||||
|
should_map.then_some(state.floating_loc)
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
if let Some(loc) = floating_loc {
|
||||||
|
self.space
|
||||||
|
.map_element(win.clone(), loc.to_i32_round(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ use crate::{
|
||||||
render::util::snapshot::OutputSnapshots,
|
render::util::snapshot::OutputSnapshots,
|
||||||
state::{Pinnacle, State, WithState},
|
state::{Pinnacle, State, WithState},
|
||||||
tag::Tag,
|
tag::Tag,
|
||||||
window::window_state::FloatingOrTiled,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A unique identifier for an output.
|
/// A unique identifier for an output.
|
||||||
|
@ -223,21 +222,17 @@ impl Pinnacle {
|
||||||
|
|
||||||
let output_loc = output.current_location();
|
let output_loc = output.current_location();
|
||||||
|
|
||||||
// FIXME: get everything out of this with_state
|
let mut loc = self.space.element_location(&win).unwrap_or(output_loc);
|
||||||
win.with_state_mut(|state| {
|
|
||||||
let FloatingOrTiled::Floating { loc, size: _ } = &mut state.floating_or_tiled
|
|
||||||
else {
|
|
||||||
unreachable!()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut loc_relative_to_output = *loc - output_loc.to_f64();
|
// FIXME: space maps in i32
|
||||||
loc_relative_to_output = loc_relative_to_output.upscale(pos_multiplier);
|
let mut loc_relative_to_output = loc - output_loc;
|
||||||
|
loc_relative_to_output = loc_relative_to_output
|
||||||
|
.to_f64()
|
||||||
|
.upscale(pos_multiplier)
|
||||||
|
.to_i32_round();
|
||||||
|
|
||||||
*loc = loc_relative_to_output + output_loc.to_f64();
|
loc = loc_relative_to_output + output_loc;
|
||||||
// FIXME: f64 -> i32
|
self.space.map_element(win.clone(), loc, false);
|
||||||
self.space
|
|
||||||
.map_element(win.clone(), loc.to_i32_round(), false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,10 +73,10 @@ impl WindowElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.with_state_mut(|state| {
|
// self.with_state_mut(|state| {
|
||||||
// FIXME: f64 -> i32, also remove target loc
|
// // FIXME: f64 -> i32, also remove target loc
|
||||||
state.target_loc = Some(new_loc.to_i32_round());
|
// state.target_loc = Some(new_loc.to_i32_round());
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this window's class (app id in Wayland but hey old habits die hard).
|
/// Get this window's class (app id in Wayland but hey old habits die hard).
|
||||||
|
|
|
@ -11,7 +11,7 @@ use smithay::{
|
||||||
use crate::{
|
use crate::{
|
||||||
handlers::decoration::KdeDecorationObject,
|
handlers::decoration::KdeDecorationObject,
|
||||||
state::{Pinnacle, WithState},
|
state::{Pinnacle, WithState},
|
||||||
window::window_state,
|
window::window_state::FloatingOrTiled,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::WindowElement;
|
use super::WindowElement;
|
||||||
|
@ -164,13 +164,6 @@ pub struct WindowRule {
|
||||||
pub decoration_mode: Option<DecorationMode>,
|
pub decoration_mode: Option<DecorationMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: just skip serializing fields on the other FloatingOrTiled
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub enum FloatingOrTiled {
|
|
||||||
Floating,
|
|
||||||
Tiled,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pinnacle {
|
impl Pinnacle {
|
||||||
pub fn apply_window_rules(&mut self, window: &WindowElement) {
|
pub fn apply_window_rules(&mut self, window: &WindowElement) {
|
||||||
tracing::debug!("Applying window rules");
|
tracing::debug!("Applying window rules");
|
||||||
|
@ -207,23 +200,14 @@ impl Pinnacle {
|
||||||
window.with_state_mut(|state| state.tags.clone_from(&tags));
|
window.with_state_mut(|state| state.tags.clone_from(&tags));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(floating_or_tiled) = floating_or_tiled {
|
|
||||||
match floating_or_tiled {
|
|
||||||
FloatingOrTiled::Floating => {
|
|
||||||
if window.with_state(|state| state.floating_or_tiled.is_tiled()) {
|
|
||||||
window.toggle_floating();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FloatingOrTiled::Tiled => {
|
|
||||||
if window.with_state(|state| state.floating_or_tiled.is_floating()) {
|
|
||||||
window.toggle_floating();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(fs_or_max) = fullscreen_or_maximized {
|
if let Some(fs_or_max) = fullscreen_or_maximized {
|
||||||
window.with_state_mut(|state| state.fullscreen_or_maximized = *fs_or_max);
|
match fs_or_max {
|
||||||
|
FullscreenOrMaximized::Neither => (), // TODO: is this branch needed?
|
||||||
|
FullscreenOrMaximized::Fullscreen => {
|
||||||
|
self.set_window_fullscreen(window, true)
|
||||||
|
}
|
||||||
|
FullscreenOrMaximized::Maximized => self.set_window_maximized(window, true),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((w, h)) = size {
|
if let Some((w, h)) = size {
|
||||||
|
@ -231,53 +215,20 @@ impl Pinnacle {
|
||||||
window_size.w = u32::from(*w) as i32;
|
window_size.w = u32::from(*w) as i32;
|
||||||
window_size.h = u32::from(*h) as i32;
|
window_size.h = u32::from(*h) as i32;
|
||||||
|
|
||||||
match window.with_state(|state| state.floating_or_tiled) {
|
window.with_state_mut(|state| {
|
||||||
window_state::FloatingOrTiled::Floating { loc, mut size } => {
|
state.floating_size = Some(window_size);
|
||||||
size = (u32::from(*w) as i32, u32::from(*h) as i32).into();
|
});
|
||||||
window.with_state_mut(|state| {
|
|
||||||
state.floating_or_tiled =
|
|
||||||
window_state::FloatingOrTiled::Floating { loc, size }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
window_state::FloatingOrTiled::Tiled(mut rect) => {
|
|
||||||
if let Some((_, size)) = rect.as_mut() {
|
|
||||||
*size = (u32::from(*w) as i32, u32::from(*h) as i32).into();
|
|
||||||
}
|
|
||||||
window.with_state_mut(|state| {
|
|
||||||
state.floating_or_tiled = window_state::FloatingOrTiled::Tiled(rect)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: make this f64
|
||||||
if let Some(location) = location {
|
if let Some(location) = location {
|
||||||
match window.with_state(|state| state.floating_or_tiled) {
|
window.with_state_mut(|state| {
|
||||||
window_state::FloatingOrTiled::Floating { mut loc, size } => {
|
state.floating_loc = Some(Point::from(*location).to_f64());
|
||||||
// FIXME: make window rule f64
|
});
|
||||||
loc = Point::from(*location).to_f64();
|
}
|
||||||
window.with_state_mut(|state| {
|
|
||||||
state.floating_or_tiled =
|
|
||||||
window_state::FloatingOrTiled::Floating { loc, size }
|
|
||||||
});
|
|
||||||
// FIXME: space maps as i32
|
|
||||||
self.space
|
|
||||||
.map_element(window.clone(), loc.to_i32_round(), false);
|
|
||||||
}
|
|
||||||
window_state::FloatingOrTiled::Tiled(rect) => {
|
|
||||||
// If the window is tiled, don't set the size. Instead, set
|
|
||||||
// what the size will be when it gets set to floating.
|
|
||||||
let rect = rect.unwrap_or_else(|| {
|
|
||||||
let size = window.geometry().size;
|
|
||||||
// FIXME: i32 -> f64
|
|
||||||
(Point::from(*location).to_f64(), size)
|
|
||||||
});
|
|
||||||
|
|
||||||
window.with_state_mut(|state| {
|
if let Some(floating_or_tiled) = floating_or_tiled {
|
||||||
state.floating_or_tiled =
|
window.with_state_mut(|state| state.floating_or_tiled = *floating_or_tiled);
|
||||||
window_state::FloatingOrTiled::Tiled(Some(rect))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(decoration_mode) = decoration_mode {
|
if let Some(decoration_mode) = decoration_mode {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use smithay::{
|
||||||
utils::{Logical, Point, Serial, Size},
|
utils::{Logical, Point, Serial, Size},
|
||||||
wayland::compositor::HookId,
|
wayland::compositor::HookId,
|
||||||
};
|
};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
layout::transaction::LayoutSnapshot,
|
layout::transaction::LayoutSnapshot,
|
||||||
|
@ -61,133 +62,11 @@ pub struct WindowElementState {
|
||||||
pub snapshot: Option<LayoutSnapshot>,
|
pub snapshot: Option<LayoutSnapshot>,
|
||||||
pub snapshot_hook_id: Option<HookId>,
|
pub snapshot_hook_id: Option<HookId>,
|
||||||
pub decoration_mode: Option<DecorationMode>,
|
pub decoration_mode: Option<DecorationMode>,
|
||||||
|
pub floating_loc: Option<Point<f64, Logical>>,
|
||||||
|
pub floating_size: Option<Size<i32, Logical>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowElement {
|
impl WindowElement {
|
||||||
/// RefCell Safety: This method uses a [`RefCell`] on this window.
|
|
||||||
pub fn toggle_floating(&self) {
|
|
||||||
match self.with_state(|state| state.floating_or_tiled) {
|
|
||||||
FloatingOrTiled::Floating { loc, size } => {
|
|
||||||
self.with_state_mut(|state| {
|
|
||||||
state.floating_or_tiled = FloatingOrTiled::Tiled(Some((loc, size)))
|
|
||||||
});
|
|
||||||
self.set_tiled_states();
|
|
||||||
}
|
|
||||||
FloatingOrTiled::Tiled(prev_rect) => {
|
|
||||||
// FIXME: is using window geometry here right?
|
|
||||||
let (prev_loc, prev_size) = prev_rect.unwrap_or_else(|| {
|
|
||||||
let geo = self.geometry();
|
|
||||||
(geo.loc.to_f64(), geo.size)
|
|
||||||
});
|
|
||||||
|
|
||||||
self.with_state_mut(|state| {
|
|
||||||
state.floating_or_tiled = FloatingOrTiled::Floating {
|
|
||||||
loc: prev_loc,
|
|
||||||
size: prev_size,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: maybe move this into update_windows
|
|
||||||
self.change_geometry(prev_loc, prev_size);
|
|
||||||
self.set_floating_states();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RefCell Safety: This method uses a [`RefCell`] on this window.
|
|
||||||
pub fn toggle_fullscreen(&self) {
|
|
||||||
match self.with_state(|state| state.fullscreen_or_maximized) {
|
|
||||||
FullscreenOrMaximized::Neither | FullscreenOrMaximized::Maximized => {
|
|
||||||
self.with_state_mut(|state| {
|
|
||||||
state.fullscreen_or_maximized = FullscreenOrMaximized::Fullscreen;
|
|
||||||
});
|
|
||||||
|
|
||||||
match self.underlying_surface() {
|
|
||||||
WindowSurface::Wayland(toplevel) => {
|
|
||||||
toplevel.with_pending_state(|state| {
|
|
||||||
state.states.unset(xdg_toplevel::State::Maximized);
|
|
||||||
state.states.set(xdg_toplevel::State::Fullscreen);
|
|
||||||
state.states.set(xdg_toplevel::State::TiledTop);
|
|
||||||
state.states.set(xdg_toplevel::State::TiledLeft);
|
|
||||||
state.states.set(xdg_toplevel::State::TiledBottom);
|
|
||||||
state.states.set(xdg_toplevel::State::TiledRight);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
WindowSurface::X11(surface) => {
|
|
||||||
if !surface.is_override_redirect() {
|
|
||||||
surface
|
|
||||||
.set_maximized(false)
|
|
||||||
.expect("failed to set x11 win to maximized");
|
|
||||||
surface
|
|
||||||
.set_fullscreen(true)
|
|
||||||
.expect("failed to set x11 win to not fullscreen");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FullscreenOrMaximized::Fullscreen => {
|
|
||||||
self.with_state_mut(|state| {
|
|
||||||
state.fullscreen_or_maximized = FullscreenOrMaximized::Neither;
|
|
||||||
});
|
|
||||||
|
|
||||||
match self.with_state(|state| state.floating_or_tiled) {
|
|
||||||
FloatingOrTiled::Floating { loc, size } => {
|
|
||||||
self.change_geometry(loc, size);
|
|
||||||
self.set_floating_states();
|
|
||||||
}
|
|
||||||
FloatingOrTiled::Tiled(_) => self.set_tiled_states(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// RefCell Safety: This method uses a [`RefCell`] on this window.
|
|
||||||
pub fn toggle_maximized(&self) {
|
|
||||||
match self.with_state(|state| state.fullscreen_or_maximized) {
|
|
||||||
FullscreenOrMaximized::Neither | FullscreenOrMaximized::Fullscreen => {
|
|
||||||
self.with_state_mut(|state| {
|
|
||||||
state.fullscreen_or_maximized = FullscreenOrMaximized::Maximized;
|
|
||||||
});
|
|
||||||
|
|
||||||
match self.underlying_surface() {
|
|
||||||
WindowSurface::Wayland(toplevel) => {
|
|
||||||
toplevel.with_pending_state(|state| {
|
|
||||||
state.states.set(xdg_toplevel::State::Maximized);
|
|
||||||
state.states.unset(xdg_toplevel::State::Fullscreen);
|
|
||||||
state.states.set(xdg_toplevel::State::TiledTop);
|
|
||||||
state.states.set(xdg_toplevel::State::TiledLeft);
|
|
||||||
state.states.set(xdg_toplevel::State::TiledBottom);
|
|
||||||
state.states.set(xdg_toplevel::State::TiledRight);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
WindowSurface::X11(surface) => {
|
|
||||||
if !surface.is_override_redirect() {
|
|
||||||
surface
|
|
||||||
.set_maximized(true)
|
|
||||||
.expect("failed to set x11 win to maximized");
|
|
||||||
surface
|
|
||||||
.set_fullscreen(false)
|
|
||||||
.expect("failed to set x11 win to not fullscreen");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FullscreenOrMaximized::Maximized => {
|
|
||||||
self.with_state_mut(|state| {
|
|
||||||
state.fullscreen_or_maximized = FullscreenOrMaximized::Neither;
|
|
||||||
});
|
|
||||||
|
|
||||||
match self.with_state(|state| state.floating_or_tiled) {
|
|
||||||
FloatingOrTiled::Floating { loc, size } => {
|
|
||||||
self.change_geometry(loc, size);
|
|
||||||
self.set_floating_states();
|
|
||||||
}
|
|
||||||
FloatingOrTiled::Tiled(_) => self.set_tiled_states(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unsets maximized and fullscreen states for both wayland and xwayland windows
|
/// Unsets maximized and fullscreen states for both wayland and xwayland windows
|
||||||
/// and unsets tiled states for wayland windows.
|
/// and unsets tiled states for wayland windows.
|
||||||
fn set_floating_states(&self) {
|
fn set_floating_states(&self) {
|
||||||
|
@ -243,19 +122,181 @@ impl WindowElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Pinnacle {
|
||||||
|
pub fn set_window_floating(&self, window: &WindowElement, floating: bool) {
|
||||||
|
// If the window is fullscreen or maximized, simply mark it as floating or tiled
|
||||||
|
// and don't set floating or tiled states to prevent stuff like decorations
|
||||||
|
// appearing in fullscreen mode.
|
||||||
|
if window.with_state(|state| !state.fullscreen_or_maximized.is_neither()) {
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.floating_or_tiled = match floating {
|
||||||
|
true => FloatingOrTiled::Floating,
|
||||||
|
false => FloatingOrTiled::Tiled,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if floating {
|
||||||
|
let size = window
|
||||||
|
.with_state(|state| state.floating_size)
|
||||||
|
.unwrap_or_else(|| window.geometry().size);
|
||||||
|
let loc = window
|
||||||
|
.with_state(|state| state.floating_loc)
|
||||||
|
.or_else(|| self.space.element_location(window).map(|loc| loc.to_f64()))
|
||||||
|
.or_else(|| {
|
||||||
|
self.focused_output().map(|op| {
|
||||||
|
let op_geo = self
|
||||||
|
.space
|
||||||
|
.output_geometry(op)
|
||||||
|
.expect("focused output wasn't mapped");
|
||||||
|
|
||||||
|
let x = op_geo.loc.x + op_geo.size.w / 2 - (size.w / 2);
|
||||||
|
let y = op_geo.loc.y + op_geo.size.h / 2 - (size.h / 2);
|
||||||
|
|
||||||
|
(x as f64, y as f64).into()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.floating_size = Some(size);
|
||||||
|
state.floating_loc = Some(loc);
|
||||||
|
state.floating_or_tiled = FloatingOrTiled::Floating;
|
||||||
|
});
|
||||||
|
|
||||||
|
window.change_geometry(loc, size);
|
||||||
|
window.set_floating_states();
|
||||||
|
} else {
|
||||||
|
let geo = self.space.element_geometry(window);
|
||||||
|
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
if let Some(geo) = geo {
|
||||||
|
state.floating_size.replace(geo.size);
|
||||||
|
state.floating_loc.replace(geo.loc.to_f64()); // FIXME: i32 -> f64
|
||||||
|
}
|
||||||
|
state.floating_or_tiled = FloatingOrTiled::Tiled;
|
||||||
|
});
|
||||||
|
window.set_tiled_states();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_window_maximized(&self, window: &WindowElement, maximized: bool) {
|
||||||
|
if maximized {
|
||||||
|
// We only want to update the stored floating geometry when exiting floating mode.
|
||||||
|
if window.with_state(|state| {
|
||||||
|
state.floating_or_tiled.is_floating() && state.fullscreen_or_maximized.is_neither()
|
||||||
|
}) {
|
||||||
|
let geo = self.space.element_geometry(window);
|
||||||
|
|
||||||
|
if let Some(geo) = geo {
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.floating_size.replace(geo.size);
|
||||||
|
state.floating_loc.replace(geo.loc.to_f64()); // FIXME: i32 -> f64
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.fullscreen_or_maximized = FullscreenOrMaximized::Maximized;
|
||||||
|
});
|
||||||
|
|
||||||
|
match window.underlying_surface() {
|
||||||
|
WindowSurface::Wayland(toplevel) => {
|
||||||
|
toplevel.with_pending_state(|state| {
|
||||||
|
state.states.set(xdg_toplevel::State::Maximized);
|
||||||
|
state.states.unset(xdg_toplevel::State::Fullscreen);
|
||||||
|
state.states.set(xdg_toplevel::State::TiledTop);
|
||||||
|
state.states.set(xdg_toplevel::State::TiledLeft);
|
||||||
|
state.states.set(xdg_toplevel::State::TiledBottom);
|
||||||
|
state.states.set(xdg_toplevel::State::TiledRight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
WindowSurface::X11(surface) => {
|
||||||
|
if !surface.is_override_redirect() {
|
||||||
|
if let Err(err) = surface.set_maximized(true) {
|
||||||
|
warn!("Failed to set xwayland window to maximized: {err}");
|
||||||
|
}
|
||||||
|
if let Err(err) = surface.set_fullscreen(false) {
|
||||||
|
warn!("Failed to unset xwayland window fullscreen: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.fullscreen_or_maximized = FullscreenOrMaximized::Neither;
|
||||||
|
});
|
||||||
|
|
||||||
|
match window.with_state(|state| state.floating_or_tiled) {
|
||||||
|
FloatingOrTiled::Floating => self.set_window_floating(window, true),
|
||||||
|
FloatingOrTiled::Tiled => window.set_tiled_states(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_window_fullscreen(&self, window: &WindowElement, fullscreen: bool) {
|
||||||
|
if fullscreen {
|
||||||
|
// We only want to update the stored floating geometry when exiting floating mode.
|
||||||
|
if window.with_state(|state| {
|
||||||
|
state.floating_or_tiled.is_floating() && state.fullscreen_or_maximized.is_neither()
|
||||||
|
}) {
|
||||||
|
let geo = self.space.element_geometry(window);
|
||||||
|
|
||||||
|
if let Some(geo) = geo {
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.floating_size.replace(geo.size);
|
||||||
|
state.floating_loc.replace(geo.loc.to_f64()); // FIXME: i32 -> f64
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.fullscreen_or_maximized = FullscreenOrMaximized::Fullscreen;
|
||||||
|
});
|
||||||
|
|
||||||
|
match window.underlying_surface() {
|
||||||
|
WindowSurface::Wayland(toplevel) => {
|
||||||
|
toplevel.with_pending_state(|state| {
|
||||||
|
state.states.unset(xdg_toplevel::State::Maximized);
|
||||||
|
state.states.set(xdg_toplevel::State::Fullscreen);
|
||||||
|
state.states.set(xdg_toplevel::State::TiledTop);
|
||||||
|
state.states.set(xdg_toplevel::State::TiledLeft);
|
||||||
|
state.states.set(xdg_toplevel::State::TiledBottom);
|
||||||
|
state.states.set(xdg_toplevel::State::TiledRight);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
WindowSurface::X11(surface) => {
|
||||||
|
if !surface.is_override_redirect() {
|
||||||
|
if let Err(err) = surface.set_maximized(false) {
|
||||||
|
warn!("Failed to unset xwayland window maximized: {err}");
|
||||||
|
}
|
||||||
|
if let Err(err) = surface.set_fullscreen(true) {
|
||||||
|
warn!("Failed to set xwayland window to fullscreen: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.fullscreen_or_maximized = FullscreenOrMaximized::Neither;
|
||||||
|
});
|
||||||
|
|
||||||
|
match window.with_state(|state| state.floating_or_tiled) {
|
||||||
|
FloatingOrTiled::Floating => self.set_window_floating(window, true),
|
||||||
|
FloatingOrTiled::Tiled => window.set_tiled_states(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether a window is floating or tiled
|
/// Whether a window is floating or tiled
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum FloatingOrTiled {
|
pub enum FloatingOrTiled {
|
||||||
/// The window is floating with the specified geometry.
|
/// The window is floating.
|
||||||
Floating {
|
Floating,
|
||||||
loc: Point<f64, Logical>,
|
|
||||||
size: Size<i32, Logical>,
|
|
||||||
},
|
|
||||||
/// The window is tiled.
|
/// The window is tiled.
|
||||||
///
|
Tiled,
|
||||||
/// The previous geometry it had when it was floating is stored here.
|
|
||||||
/// This is so when it becomes floating again, it returns to this geometry.
|
|
||||||
Tiled(Option<(Point<f64, Logical>, Size<i32, Logical>)>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FloatingOrTiled {
|
impl FloatingOrTiled {
|
||||||
|
@ -264,7 +305,7 @@ impl FloatingOrTiled {
|
||||||
/// [`Floating`]: FloatingOrTiled::Floating
|
/// [`Floating`]: FloatingOrTiled::Floating
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_floating(&self) -> bool {
|
pub fn is_floating(&self) -> bool {
|
||||||
matches!(self, Self::Floating { .. })
|
matches!(self, Self::Floating)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the floating or tiled is [`Tiled`].
|
/// Returns `true` if the floating or tiled is [`Tiled`].
|
||||||
|
@ -272,7 +313,7 @@ impl FloatingOrTiled {
|
||||||
/// [`Tiled`]: FloatingOrTiled::Tiled
|
/// [`Tiled`]: FloatingOrTiled::Tiled
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_tiled(&self) -> bool {
|
pub fn is_tiled(&self) -> bool {
|
||||||
matches!(self, Self::Tiled(..))
|
matches!(self, Self::Tiled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,9 +354,10 @@ impl WindowElementState {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: WindowId::next(),
|
id: WindowId::next(),
|
||||||
// loc_request_state: LocationRequestState::Idle,
|
|
||||||
tags: vec![],
|
tags: vec![],
|
||||||
floating_or_tiled: FloatingOrTiled::Tiled(None),
|
floating_or_tiled: FloatingOrTiled::Tiled,
|
||||||
|
floating_loc: None,
|
||||||
|
floating_size: None,
|
||||||
fullscreen_or_maximized: FullscreenOrMaximized::Neither,
|
fullscreen_or_maximized: FullscreenOrMaximized::Neither,
|
||||||
target_loc: None,
|
target_loc: None,
|
||||||
minimized: false,
|
minimized: false,
|
||||||
|
|
|
@ -3,7 +3,6 @@ use std::{path::PathBuf, sync::Arc, time::Duration};
|
||||||
use pinnacle::{
|
use pinnacle::{
|
||||||
state::{ClientState, State, WithState},
|
state::{ClientState, State, WithState},
|
||||||
tag::TagId,
|
tag::TagId,
|
||||||
window::window_state::FloatingOrTiled,
|
|
||||||
};
|
};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::input::{ButtonState, DeviceCapability, InputEvent},
|
backend::input::{ButtonState, DeviceCapability, InputEvent},
|
||||||
|
@ -119,29 +118,17 @@ fn handle_event(event: WlcsEvent, state: &mut State) {
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
if let Some(window) = window {
|
if let Some(window) = window {
|
||||||
|
window.with_state_mut(|state| {
|
||||||
|
state.floating_loc = Some(location.to_f64());
|
||||||
|
});
|
||||||
|
|
||||||
|
state.pinnacle.set_window_floating(&window, true);
|
||||||
|
|
||||||
state
|
state
|
||||||
.pinnacle
|
.pinnacle
|
||||||
.space
|
.space
|
||||||
.map_element(window.clone(), location, false);
|
.map_element(window.clone(), location, false);
|
||||||
|
|
||||||
let size = state
|
|
||||||
.pinnacle
|
|
||||||
.space
|
|
||||||
.element_geometry(&window)
|
|
||||||
.expect("window to be positioned was not mapped")
|
|
||||||
.size;
|
|
||||||
|
|
||||||
if window.with_state(|state| state.floating_or_tiled.is_tiled()) {
|
|
||||||
window.toggle_floating();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.with_state_mut(|state| {
|
|
||||||
state.floating_or_tiled = FloatingOrTiled::Floating {
|
|
||||||
loc: location.to_f64(),
|
|
||||||
size,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
for output in state.pinnacle.space.outputs_for_element(&window) {
|
for output in state.pinnacle.space.outputs_for_element(&window) {
|
||||||
state.schedule_render(&output);
|
state.schedule_render(&output);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue