Rework fullscreen tracking again

Lots of stuff needs cleaning up
This commit is contained in:
Ottatop 2023-08-14 13:54:50 -05:00
parent 947ccdf464
commit dbde8545c3
14 changed files with 389 additions and 335 deletions

View file

@ -53,11 +53,7 @@ require("pinnacle").setup(function(pinnacle)
input.keybind({ mod_key, "Alt" }, keys.space, function() input.keybind({ mod_key, "Alt" }, keys.space, function()
local win = window.get_focused() local win = window.get_focused()
if win ~= nil then if win ~= nil then
if win:status() == "Tiled" then win:toggle_floating()
win:set_status("Floating")
elseif win:status() == "Floating" then
win:set_status("Tiled")
end
end end
end) end)
@ -70,21 +66,14 @@ require("pinnacle").setup(function(pinnacle)
input.keybind({ mod_key }, keys.f, function() input.keybind({ mod_key }, keys.f, function()
local win = window.get_focused() local win = window.get_focused()
if win ~= nil then if win ~= nil then
win:set_status("Fullscreen") win:toggle_fullscreen()
end end
end) end)
input.keybind({ mod_key }, keys.m, function() input.keybind({ mod_key }, keys.m, function()
local win = window.get_focused() local win = window.get_focused()
if win ~= nil then if win ~= nil then
win:set_status("Maximized") win:toggle_maximized()
end
end)
input.keybind({ mod_key }, keys.t, function()
local win = window.get_focused()
if win ~= nil then
win:set_status("Tiled")
end end
end) end)

View file

@ -10,7 +10,9 @@
---@field SetWindowSize { window_id: WindowId, width: integer?, height: integer? }? ---@field SetWindowSize { window_id: WindowId, width: integer?, height: integer? }?
---@field MoveWindowToTag { window_id: WindowId, tag_id: TagId }? ---@field MoveWindowToTag { window_id: WindowId, tag_id: TagId }?
---@field ToggleTagOnWindow { window_id: WindowId, tag_id: TagId }? ---@field ToggleTagOnWindow { window_id: WindowId, tag_id: TagId }?
---@field SetStatus { window_id: WindowId, status: StatusName }? ---@field ToggleFloating { window_id: WindowId }?
---@field ToggleFullscreen { window_id: WindowId }?
---@field ToggleMaximized { window_id: WindowId }?
-- --
---@field Spawn { command: string[], callback_id: integer? }? ---@field Spawn { command: string[], callback_id: integer? }?
---@field Request Request? ---@field Request Request?
@ -26,9 +28,8 @@
---@alias Msg _Msg | "Quit" ---@alias Msg _Msg | "Quit"
---@alias StatusName ---@alias FullscreenOrMaximized
---| "Floating" ---| "Neither"
---| "Tiled"
---| "Fullscreen" ---| "Fullscreen"
---| "Maximized" ---| "Maximized"
@ -62,7 +63,7 @@
--Windows --Windows
---@field Window { window_id: WindowId|nil }? ---@field Window { window_id: WindowId|nil }?
---@field Windows { window_ids: WindowId[] }? ---@field Windows { window_ids: WindowId[] }?
---@field WindowProps { size: integer[]?, loc: integer[]?, class: string?, title: string?, status: StatusName?, focused: boolean? }? ---@field WindowProps { size: integer[]?, loc: integer[]?, class: string?, title: string?, focused: boolean?, floating: boolean?, fullscreen_or_maximized: FullscreenOrMaximized? }?
--Outputs --Outputs
---@field Output { output_name: OutputName? }? ---@field Output { output_name: OutputName? }?
---@field Outputs { output_names: OutputName[] }? ---@field Outputs { output_names: OutputName[] }?

View file

@ -90,12 +90,6 @@ function window:close()
window_module.close(self) window_module.close(self)
end end
---Set this window's status. Yes, this isn't a great name.
---@param status StatusName
function window:set_status(status)
window_module.set_status(self, status)
end
---Get this window's size. ---Get this window's size.
--- ---
---### Example ---### Example
@ -158,10 +152,48 @@ function window:title()
end end
---Get this window's status. ---Get this window's status.
---@return StatusName|nil ---@return boolean|nil
---@see WindowModule.status — The corresponding module function ---@see WindowModule.status — The corresponding module function
function window:status() function window:floating()
return window_module.status(self) return window_module.floating(self)
end
---Get this window's status.
---@return boolean|nil
---@see WindowModule.status — The corresponding module function
function window:fullscreen()
return window_module.fullscreen(self)
end
---Get this window's status.
---@return boolean|nil
---@see WindowModule.status — The corresponding module function
function window:maximized()
return window_module.maximized(self)
end
function window:toggle_floating()
SendMsg({
ToggleFloating = {
window_id = self:id(),
},
})
end
function window:toggle_fullscreen()
SendMsg({
ToggleFullscreen = {
window_id = self:id(),
},
})
end
function window:toggle_maximized()
SendMsg({
ToggleMaximized = {
window_id = self:id(),
},
})
end end
---Get whether or not this window is focused. ---Get whether or not this window is focused.
@ -361,18 +393,6 @@ function window_module.close(win)
}) })
end end
---Set `win`'s status. Yes, this is not a great name for the function.
---@param win Window
---@param status StatusName
function window_module.set_status(win, status)
SendMsg({
SetStatus = {
window_id = win:id(),
status = status,
},
})
end
---Get the specified window's size. ---Get the specified window's size.
--- ---
---### Example ---### Example
@ -482,18 +502,48 @@ function window_module.title(win)
return title return title
end end
---Get this window's status. ---Get this window's floating status.
---@param win Window ---@param win Window
---@return StatusName|nil ---@return boolean|nil
---@see Window.status — The corresponding object method ---@see Window.status — The corresponding object method
function window_module.status(win) function window_module.floating(win)
local response = Request({ local response = Request({
GetWindowProps = { GetWindowProps = {
window_id = win:id(), window_id = win:id(),
}, },
}) })
local status = response.RequestResponse.response.WindowProps.status local floating = response.RequestResponse.response.WindowProps.floating
return status return floating
end
---Get this window's fullscreen status.
---@param win Window
---@return boolean|nil
---@see Window.status — The corresponding object method
function window_module.fullscreen(win)
local response = Request({
GetWindowProps = {
window_id = win:id(),
},
})
local fom =
response.RequestResponse.response.WindowProps.fullscreen_or_maximized
return fom == "Fullscreen"
end
---Get this window's maximized status.
---@param win Window
---@return boolean|nil
---@see Window.status — The corresponding object method
function window_module.maximized(win)
local response = Request({
GetWindowProps = {
window_id = win:id(),
},
})
local fom =
response.RequestResponse.response.WindowProps.fullscreen_or_maximized
return fom == "Maximized"
end end
---Get whether or not this window is focused. ---Get whether or not this window is focused.

View file

@ -7,7 +7,7 @@ use crate::{
layout::Layout, layout::Layout,
output::OutputName, output::OutputName,
tag::TagId, tag::TagId,
window::window_state::{StatusName, WindowId}, window::window_state::{FullscreenOrMaximized, WindowId},
}; };
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
@ -44,9 +44,14 @@ pub enum Msg {
window_id: WindowId, window_id: WindowId,
tag_id: TagId, tag_id: TagId,
}, },
SetStatus { ToggleFloating {
window_id: WindowId,
},
ToggleFullscreen {
window_id: WindowId,
},
ToggleMaximized {
window_id: WindowId, window_id: WindowId,
status: StatusName,
}, },
// Tag management // Tag management
@ -207,7 +212,8 @@ pub enum RequestResponse {
class: Option<String>, class: Option<String>,
title: Option<String>, title: Option<String>,
focused: Option<bool>, focused: Option<bool>,
status: Option<StatusName>, floating: Option<bool>,
fullscreen_or_maximized: Option<FullscreenOrMaximized>,
}, },
Output { Output {
output_name: Option<String>, output_name: Option<String>,

View file

@ -20,7 +20,7 @@ use crate::{
backend::Backend, backend::Backend,
state::{State, WithState}, state::{State, WithState},
window::{ window::{
window_state::{LocationRequestState, Status}, window_state::{FloatingOrTiled, LocationRequestState},
WindowElement, WindowElement,
}, },
}; };
@ -60,7 +60,9 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
// tracing::info!("window geo is: {:?}", self.window.geometry()); // tracing::info!("window geo is: {:?}", self.window.geometry());
// tracing::info!("loc is: {:?}", data.space.element_location(&self.window)); // tracing::info!("loc is: {:?}", data.space.element_location(&self.window));
let tiled = self.window.with_state(|state| state.status.is_tiled()); let tiled = self
.window
.with_state(|state| state.floating_or_tiled.is_tiled());
if tiled { if tiled {
// 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
@ -85,7 +87,8 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
return; return;
} }
let is_floating = window_under.with_state(|state| state.status.is_floating()); let is_floating =
window_under.with_state(|state| state.floating_or_tiled.is_floating());
if is_floating { if is_floating {
return; return;
@ -113,10 +116,12 @@ impl<B: Backend> PointerGrab<State<B>> for MoveSurfaceGrab<State<B>> {
.size; .size;
self.window.with_state(|state| { self.window.with_state(|state| {
if state.status.is_floating() { if state.floating_or_tiled.is_floating() {
state.status = Status::Floating(Rectangle::from_loc_and_size(new_loc, size)); state.floating_or_tiled =
FloatingOrTiled::Floating(Rectangle::from_loc_and_size(new_loc, size));
} }
}); });
if let WindowElement::X11(surface) = &self.window { if let WindowElement::X11(surface) = &self.window {
let geo = surface.geometry(); let geo = surface.geometry();
let new_geo = Rectangle::from_loc_and_size(new_loc, geo.size); let new_geo = Rectangle::from_loc_and_size(new_loc, geo.size);

View file

@ -18,7 +18,7 @@ use smithay::{
use crate::{ use crate::{
backend::Backend, backend::Backend,
state::{State, WithState}, state::{State, WithState},
window::{window_state::Status, WindowElement}, window::{window_state::FloatingOrTiled, WindowElement},
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -332,8 +332,9 @@ pub fn handle_commit<B: Backend>(state: &mut State<B>, surface: &WlSurface) -> O
.size; .size;
window.with_state(|state| { window.with_state(|state| {
if state.status.is_floating() { if state.floating_or_tiled.is_floating() {
state.status = Status::Floating(Rectangle::from_loc_and_size(window_loc, size)); state.floating_or_tiled =
FloatingOrTiled::Floating(Rectangle::from_loc_and_size(window_loc, size));
} }
}); });
@ -365,7 +366,8 @@ pub fn resize_request_client<B: Backend>(
return; return;
}; };
if window.with_state(|state| state.status.is_tiled()) { // TODO: check for fullscreen/maximized (probably shouldn't matter)
if window.with_state(|state| state.floating_or_tiled.is_tiled()) {
return; return;
} }
@ -412,7 +414,7 @@ pub fn resize_request_server<B: Backend>(
return; return;
}; };
if window.with_state(|state| state.status.is_tiled()) { if window.with_state(|state| state.floating_or_tiled.is_tiled()) {
return; return;
} }

View file

@ -30,10 +30,7 @@ use crate::{
backend::Backend, backend::Backend,
focus::FocusTarget, focus::FocusTarget,
state::{State, WithState}, state::{State, WithState},
window::{ window::{window_state::LocationRequestState, WindowElement, BLOCKER_COUNTER},
window_state::{LocationRequestState, StatusName},
WindowElement, BLOCKER_COUNTER,
},
}; };
impl<B: Backend> XdgShellHandler for State<B> { impl<B: Backend> XdgShellHandler for State<B> {
@ -42,9 +39,14 @@ impl<B: Backend> XdgShellHandler for State<B> {
} }
fn new_toplevel(&mut self, surface: ToplevelSurface) { fn new_toplevel(&mut self, surface: ToplevelSurface) {
let window = WindowElement::Wayland(Window::new(surface.clone())); surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
window.set_status(StatusName::Tiled); let window = WindowElement::Wayland(Window::new(surface.clone()));
window.with_state(|state| { window.with_state(|state| {
state.tags = match ( state.tags = match (
@ -323,7 +325,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
return; return;
}; };
window.set_status(StatusName::Fullscreen); if !window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
window.toggle_fullscreen();
}
} }
surface.send_configure(); surface.send_configure();
@ -351,8 +355,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
return; return;
}; };
// TODO: remember the last status instead of tiled if window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
window.set_status(StatusName::Tiled); window.toggle_fullscreen();
}
} }
fn maximize_request(&mut self, surface: ToplevelSurface) { fn maximize_request(&mut self, surface: ToplevelSurface) {
@ -360,8 +365,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
return; return;
}; };
window.set_status(StatusName::Maximized); if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.toggle_maximized();
}
// TODO: might need to update_windows here // TODO: might need to update_windows here
} }
@ -370,8 +376,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
return; return;
}; };
// TODO: remember last status if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.set_status(StatusName::Tiled); window.toggle_maximized();
}
} }
// fn minimize_request(&mut self, surface: ToplevelSurface) { // fn minimize_request(&mut self, surface: ToplevelSurface) {

View file

@ -28,10 +28,7 @@ use crate::{
backend::Backend, backend::Backend,
focus::FocusTarget, focus::FocusTarget,
state::{CalloopData, WithState}, state::{CalloopData, WithState},
window::{ window::{window_state::FloatingOrTiled, WindowBlocker, WindowElement, BLOCKER_COUNTER},
window_state::{Status, StatusName},
WindowBlocker, WindowElement, BLOCKER_COUNTER,
},
}; };
impl<B: Backend> XwmHandler for CalloopData<B> { impl<B: Backend> XwmHandler for CalloopData<B> {
@ -131,7 +128,7 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
if should_float(surface) { if should_float(surface) {
window.with_state(|state| { window.with_state(|state| {
state.status = Status::Floating(bbox); state.floating_or_tiled = FloatingOrTiled::Floating(bbox);
}); });
} }
@ -279,11 +276,9 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
self.state self.state
.windows .windows
.retain(|elem| win.wl_surface() != elem.wl_surface()); .retain(|elem| win.wl_surface() != elem.wl_surface());
if win.with_state(|state| state.status.is_tiled()) {
if let Some(output) = win.output(&self.state) { if let Some(output) = win.output(&self.state) {
self.state.update_windows(&output); self.state.update_windows(&output);
// self.state.re_layout(&output);
}
} }
} }
tracing::debug!("destroyed x11 window"); tracing::debug!("destroyed x11 window");
@ -341,7 +336,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else { let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else {
return; return;
}; };
window.set_status(StatusName::Maximized);
if !window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.toggle_maximized();
}
} }
fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) { fn unmaximize_request(&mut self, _xwm: XwmId, window: X11Surface) {
@ -351,8 +349,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else { let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else {
return; return;
}; };
// TODO: remember previous status
window.set_status(StatusName::Tiled); if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.toggle_maximized();
}
} }
fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { fn fullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
@ -363,9 +363,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else { let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else {
return; return;
}; };
window.set_status(StatusName::Fullscreen);
// TODO: do i need to update_windows here? if !window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
window.toggle_fullscreen();
}
} }
fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) { fn unfullscreen_request(&mut self, _xwm: XwmId, window: X11Surface) {
@ -375,8 +376,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else { let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else {
return; return;
}; };
// TODO: remember previous status
window.set_status(StatusName::Tiled); if window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
window.toggle_fullscreen();
}
} }
fn resize_request( fn resize_request(

View file

@ -75,7 +75,8 @@ impl<B: Backend> State<B> {
let top_fullscreen_window = self.focus_state.focus_stack.iter().rev().find(|win| { let top_fullscreen_window = self.focus_state.focus_stack.iter().rev().find(|win| {
win.with_state(|state| { win.with_state(|state| {
state.status.is_fullscreen() && state.tags.iter().any(|tag| tag.active()) state.fullscreen_or_maximized.is_fullscreen()
&& state.tags.iter().any(|tag| tag.active())
}) })
}); });

View file

@ -12,7 +12,7 @@ use crate::{
state::{State, WithState}, state::{State, WithState},
tag::Tag, tag::Tag,
window::{ window::{
window_state::{LocationRequestState, Status}, window_state::{FullscreenOrMaximized, LocationRequestState},
WindowElement, WindowElement,
}, },
}; };
@ -70,7 +70,11 @@ impl<B: Backend> State<B> {
let tiled_windows = windows_on_foc_tags let tiled_windows = windows_on_foc_tags
.iter() .iter()
.filter(|win| win.with_state(|state| state.status.is_tiled())) .filter(|win| {
win.with_state(|state| {
state.floating_or_tiled.is_tiled() && state.fullscreen_or_maximized.is_neither()
})
})
.cloned() .cloned()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -78,11 +82,11 @@ impl<B: Backend> State<B> {
let output_geo = self.space.output_geometry(output).expect("no output geo"); let output_geo = self.space.output_geometry(output).expect("no output geo");
for window in windows_on_foc_tags.iter() { for window in windows_on_foc_tags.iter() {
match window.with_state(|state| state.status) { match window.with_state(|state| state.fullscreen_or_maximized) {
Status::Fullscreen(_) => { FullscreenOrMaximized::Fullscreen => {
window.change_geometry(output_geo); window.change_geometry(output_geo);
} }
Status::Maximized(_) => { FullscreenOrMaximized::Maximized => {
let map = layer_map_for_output(output); let map = layer_map_for_output(output);
let geo = if map.layers().peekable().peek().is_none() { let geo = if map.layers().peekable().peek().is_none() {
// INFO: Sometimes the exclusive zone is some weird number that doesn't match the // INFO: Sometimes the exclusive zone is some weird number that doesn't match the
@ -96,7 +100,7 @@ impl<B: Backend> State<B> {
}; };
window.change_geometry(geo); window.change_geometry(geo);
} }
_ => (), FullscreenOrMaximized::Neither => (),
} }
} }
@ -474,7 +478,7 @@ fn corner(layout: &Layout, windows: Vec<WindowElement>, rect: Rectangle<i32, Log
fn filter_windows(windows: &[WindowElement], tags: Vec<Tag>) -> Vec<WindowElement> { fn filter_windows(windows: &[WindowElement], tags: Vec<Tag>) -> Vec<WindowElement> {
windows windows
.iter() .iter()
.filter(|window| window.with_state(|state| state.status.is_tiled())) .filter(|window| window.with_state(|state| state.floating_or_tiled.is_tiled()))
.filter(|window| { .filter(|window| {
window.with_state(|state| { window.with_state(|state| {
for tag in state.tags.iter() { for tag in state.tags.iter() {

View file

@ -181,7 +181,7 @@ where
true true
} }
}; };
state.status.is_fullscreen() state.fullscreen_or_maximized.is_fullscreen()
&& state.tags.iter().any(|tag| tag.active()) && state.tags.iter().any(|tag| tag.active())
&& is_wayland_actually_fullscreen && is_wayland_actually_fullscreen
}) })

View file

@ -20,10 +20,7 @@ use crate::{
focus::FocusState, focus::FocusState,
grab::resize_grab::ResizeSurfaceState, grab::resize_grab::ResizeSurfaceState,
tag::Tag, tag::Tag,
window::{ window::{window_state::LocationRequestState, WindowElement},
window_state::{LocationRequestState, Status, StatusName},
WindowElement,
},
}; };
use calloop::futures::Scheduler; use calloop::futures::Scheduler;
use futures_lite::AsyncBufReadExt; use futures_lite::AsyncBufReadExt;
@ -201,15 +198,26 @@ impl<B: Backend> State<B> {
self.update_windows(&output); self.update_windows(&output);
// self.re_layout(&output); // self.re_layout(&output);
} }
Msg::SetStatus { window_id, status } => { Msg::ToggleFloating { window_id } => {
let Some(window) = window_id.window(self) else { return }; let Some(window) = window_id.window(self) else { return };
window.set_status(status); window.toggle_floating();
let outputs = self.space.outputs_for_element(&window);
self.focus_state.set_focus(window);
if let Some(output) = outputs.into_iter().next() { let Some(output) = window.output(self) else { return };
self.update_windows(&output); self.update_windows(&output);
} }
Msg::ToggleFullscreen { window_id } => {
let Some(window) = window_id.window(self) else { return };
window.toggle_fullscreen();
let Some(output) = window.output(self) else { return };
self.update_windows(&output);
}
Msg::ToggleMaximized { window_id } => {
let Some(window) = window_id.window(self) else { return };
window.toggle_maximized();
let Some(output) = window.output(self) else { return };
self.update_windows(&output);
} }
// Tags ---------------------------------------- // Tags ----------------------------------------
@ -369,19 +377,17 @@ impl<B: Backend> State<B> {
} }
WindowElement::X11(surface) => (Some(surface.class()), Some(surface.title())), WindowElement::X11(surface) => (Some(surface.class()), Some(surface.title())),
}); });
let status = window.as_ref().map(|win| {
win.with_state(|state| match state.status {
Status::Floating(_) => StatusName::Floating,
Status::Tiled(_) => StatusName::Tiled,
Status::Fullscreen(_) => StatusName::Fullscreen,
Status::Maximized(_) => StatusName::Maximized,
})
});
let focused = window.as_ref().and_then(|win| { let focused = window.as_ref().and_then(|win| {
self.focus_state self.focus_state
.current_focus() // TODO: actual focus .current_focus() // TODO: actual focus
.map(|foc_win| win == &foc_win) .map(|foc_win| win == &foc_win)
}); });
let floating = window
.as_ref()
.map(|win| win.with_state(|state| state.floating_or_tiled.is_floating()));
let fullscreen_or_maximized = window
.as_ref()
.map(|win| win.with_state(|state| state.fullscreen_or_maximized));
crate::api::send_to_client( crate::api::send_to_client(
&mut stream, &mut stream,
&OutgoingMsg::RequestResponse { &OutgoingMsg::RequestResponse {
@ -392,7 +398,8 @@ impl<B: Backend> State<B> {
class, class,
title, title,
focused, focused,
status, floating,
fullscreen_or_maximized,
}, },
}, },
) )
@ -661,78 +668,6 @@ impl<B: Backend> State<B> {
} }
} }
} }
pub fn re_layout(&mut self, output: &Output) {
let windows = self
.windows
.iter()
.filter(|win| {
win.with_state(|state| {
state
.tags
.iter()
.any(|tag| tag.output(self).is_some_and(|op| &op == output))
})
})
.cloned()
.collect::<Vec<_>>();
let (render, do_not_render) = output.with_state(|state| {
let first_tag = state.focused_tags().next();
if let Some(first_tag) = first_tag {
first_tag.layout().layout(
self.windows.clone(),
state.focused_tags().cloned().collect(),
&mut self.space,
output,
);
}
windows.iter().cloned().partition::<Vec<_>, _>(|win| {
win.with_state(|win_state| {
win_state
.tags
.iter()
.any(|tag| state.focused_tags().any(|tg| tag == tg))
})
})
});
tracing::debug!(
"{} to render, {} to not render",
render.len(),
do_not_render.len()
);
let clone = render.clone();
self.loop_handle.insert_idle(|data| {
schedule_on_commit(data, clone.clone(), |dt| {
for win in do_not_render {
dt.state.space.unmap_elem(&win);
if let WindowElement::X11(surface) = win {
if !surface.is_override_redirect() {
surface.set_mapped(false).expect("failed to unmap x11 win");
}
}
}
for (win, rect) in
clone
.into_iter()
.filter_map(|win| match win.with_state(|state| state.status) {
Status::Floating(loc) => Some((win, loc)),
_ => None,
})
{
if let WindowElement::X11(surface) = &win {
if !surface.is_override_redirect() {
surface.set_mapped(true).expect("failed to map x11 win");
}
}
dt.state.space.map_element(win, rect.loc, false);
}
});
});
}
} }
/// Schedule something to be done when windows have finished committing and have become /// Schedule something to be done when windows have finished committing and have become

View file

@ -20,10 +20,7 @@ use smithay::{
}, },
output::Output, output::Output,
reexports::{ reexports::{
wayland_protocols::{ wayland_protocols::wp::presentation_time::server::wp_presentation_feedback,
wp::presentation_time::server::wp_presentation_feedback,
xdg::shell::server::xdg_toplevel,
},
wayland_server::protocol::wl_surface::WlSurface, 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, Size},
@ -40,7 +37,7 @@ use crate::{
state::{State, WithState}, state::{State, WithState},
}; };
use self::window_state::{LocationRequestState, Status, StatusName, WindowElementState}; use self::window_state::{LocationRequestState, WindowElementState};
pub mod window_state; pub mod window_state;
@ -184,6 +181,8 @@ impl WindowElement {
/// configures to Wayland windows. /// configures to Wayland windows.
/// ///
/// Xwayland windows will still receive a configure. /// Xwayland windows will still receive a configure.
///
/// This method uses a [`RefCell`].
// TODO: ^ does that make things flicker? // TODO: ^ does that make things flicker?
pub fn change_geometry(&self, new_geo: Rectangle<i32, Logical>) { pub fn change_geometry(&self, new_geo: Rectangle<i32, Logical>) {
match self { match self {
@ -268,116 +267,6 @@ impl WindowElement {
pub fn is_x11(&self) -> bool { pub fn is_x11(&self) -> bool {
matches!(self, Self::X11(..)) matches!(self, Self::X11(..))
} }
pub fn set_status(&self, status: StatusName) {
let prev_status = self.with_state(|state| state.status);
let geo = match prev_status {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
match status {
StatusName::Floating => {
self.with_state(|state| state.status = Status::Floating(geo));
match self {
WindowElement::Wayland(window) => {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.unset(xdg_toplevel::State::TiledTop);
state.states.unset(xdg_toplevel::State::TiledBottom);
state.states.unset(xdg_toplevel::State::TiledLeft);
state.states.unset(xdg_toplevel::State::TiledRight);
});
}
WindowElement::X11(surface) => {
surface
.set_fullscreen(false)
.expect("failed to set x11 win to not fullscreen");
surface
.set_maximized(false)
.expect("failed to set x11 win to not maximized");
}
}
}
StatusName::Tiled => {
self.with_state(|state| state.status = Status::Tiled(Some(geo)));
match self {
WindowElement::Wayland(window) => {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
WindowElement::X11(surface) => {
surface
.set_fullscreen(false)
.expect("failed to set x11 win to not fullscreen");
surface
.set_maximized(false)
.expect("failed to set x11 win to not maximized");
}
}
}
StatusName::Fullscreen => {
self.with_state(|state| state.status = Status::Fullscreen(Some(geo)));
match self {
WindowElement::Wayland(window) => {
window.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::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
WindowElement::X11(surface) => {
surface
.set_fullscreen(true)
.expect("failed to set x11 win to fullscreen");
surface
.set_maximized(false)
.expect("failed to set x11 win to not maximzied");
}
}
}
StatusName::Maximized => {
self.with_state(|state| state.status = Status::Maximized(Some(geo)));
match self {
WindowElement::Wayland(window) => {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.set(xdg_toplevel::State::Maximized);
state.states.set(xdg_toplevel::State::TiledTop);
state.states.set(xdg_toplevel::State::TiledBottom);
state.states.set(xdg_toplevel::State::TiledLeft);
state.states.set(xdg_toplevel::State::TiledRight);
});
}
WindowElement::X11(surface) => {
surface
.set_fullscreen(false)
.expect("failed to set x11 win to not fullscreen");
surface
.set_maximized(true)
.expect("failed to set x11 win to maximized");
}
}
}
}
}
} }
impl IsAlive for WindowElement { impl IsAlive for WindowElement {

View file

@ -6,7 +6,8 @@ use std::{
}; };
use smithay::{ use smithay::{
desktop::Window, desktop::{space::SpaceElement, Window},
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel,
utils::{Logical, Point, Rectangle, Serial}, utils::{Logical, Point, Rectangle, Serial},
}; };
@ -66,12 +67,12 @@ impl WithState for Window {
pub struct WindowElementState { pub struct WindowElementState {
/// The id of this window. /// The id of this window.
pub id: WindowId, pub id: WindowId,
/// Whether the window is floating, tiled, fullscreen, or maximized.
pub status: Status,
/// The window's resize state. See [WindowResizeState] for more. /// The window's resize state. See [WindowResizeState] for more.
pub loc_request_state: LocationRequestState, pub loc_request_state: LocationRequestState,
/// What tags the window is currently on. /// What tags the window is currently on.
pub tags: Vec<Tag>, pub tags: Vec<Tag>,
pub floating_or_tiled: FloatingOrTiled,
pub fullscreen_or_maximized: FullscreenOrMaximized,
} }
/// The state of a window's resize operation. /// The state of a window's resize operation.
@ -114,55 +115,215 @@ pub enum LocationRequestState {
Acknowledged(Point<i32, Logical>), Acknowledged(Point<i32, Logical>),
} }
/// Whether the window is floating, tiled, fullscreen, or maximized. impl WindowElement {
/// This method uses a [`RefCell`].
pub fn toggle_floating(&self) {
match self.with_state(|state| state.floating_or_tiled) {
FloatingOrTiled::Floating(current_rect) => {
self.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Tiled(Some(current_rect))
});
self.set_tiled_states();
}
FloatingOrTiled::Tiled(prev_rect) => {
let prev_rect = prev_rect.unwrap_or_else(|| self.geometry());
self.with_state(|state| {
state.floating_or_tiled = FloatingOrTiled::Floating(prev_rect);
});
// TODO: maybe move this into update_windows
self.change_geometry(prev_rect);
self.set_floating_states();
}
}
}
/// This method uses a [`RefCell`].
pub fn toggle_fullscreen(&self) {
self.with_state(|state| match state.fullscreen_or_maximized {
FullscreenOrMaximized::Neither | FullscreenOrMaximized::Maximized => {
state.fullscreen_or_maximized = FullscreenOrMaximized::Fullscreen;
match self {
WindowElement::Wayland(window) => {
window.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);
});
}
WindowElement::X11(surface) => {
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 => {
state.fullscreen_or_maximized = FullscreenOrMaximized::Neither;
match state.floating_or_tiled {
FloatingOrTiled::Floating(current_rect) => {
self.change_geometry(current_rect);
self.set_floating_states();
}
FloatingOrTiled::Tiled(_) => self.set_tiled_states(),
}
}
});
}
/// This method uses a [`RefCell`].
pub fn toggle_maximized(&self) {
self.with_state(|state| match state.fullscreen_or_maximized {
FullscreenOrMaximized::Neither | FullscreenOrMaximized::Fullscreen => {
state.fullscreen_or_maximized = FullscreenOrMaximized::Maximized;
match self {
WindowElement::Wayland(window) => {
window.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);
});
}
WindowElement::X11(surface) => {
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 => {
state.fullscreen_or_maximized = FullscreenOrMaximized::Neither;
match state.floating_or_tiled {
FloatingOrTiled::Floating(current_rect) => {
self.change_geometry(current_rect);
self.set_floating_states();
}
FloatingOrTiled::Tiled(_) => self.set_tiled_states(),
}
}
});
}
fn set_floating_states(&self) {
match self {
WindowElement::Wayland(window) => {
window.toplevel().with_pending_state(|state| {
state.states.unset(xdg_toplevel::State::Maximized);
state.states.unset(xdg_toplevel::State::Fullscreen);
state.states.unset(xdg_toplevel::State::TiledTop);
state.states.unset(xdg_toplevel::State::TiledLeft);
state.states.unset(xdg_toplevel::State::TiledBottom);
state.states.unset(xdg_toplevel::State::TiledRight);
});
}
WindowElement::X11(surface) => {
surface
.set_maximized(false)
.expect("failed to set x11 win to maximized");
surface
.set_fullscreen(false)
.expect("failed to set x11 win to not fullscreen");
}
}
}
fn set_tiled_states(&self) {
match self {
WindowElement::Wayland(window) => {
window.toplevel().with_pending_state(|state| {
state.states.unset(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);
});
}
WindowElement::X11(surface) => {
surface
.set_maximized(false)
.expect("failed to set x11 win to maximized");
surface
.set_fullscreen(false)
.expect("failed to set x11 win to not fullscreen");
}
}
}
}
// You know what they say, the two hardest things in computer science are
// cache invalidation and naming things (and off by one errors).
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum Status { pub enum FloatingOrTiled {
Floating(Rectangle<i32, Logical>), Floating(Rectangle<i32, Logical>),
Tiled(Option<Rectangle<i32, Logical>>), Tiled(Option<Rectangle<i32, Logical>>),
Fullscreen(Option<Rectangle<i32, Logical>>),
Maximized(Option<Rectangle<i32, Logical>>),
}
// TODO: couple these somehow
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum StatusName {
Floating,
Tiled,
Fullscreen,
Maximized,
} }
impl Status { impl FloatingOrTiled {
/// Returns `true` if the float is [`Tiled`]. /// Returns `true` if the floating or tiled is [`Floating`].
/// ///
/// [`Tiled`]: Float::Tiled /// [`Floating`]: FloatingOrTiled::Floating
#[must_use]
pub fn is_floating(&self) -> bool {
matches!(self, Self::Floating(..))
}
/// Returns `true` if the floating or tiled is [`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(..))
} }
}
/// Returns `true` if the float is [`Floating`]. #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum FullscreenOrMaximized {
Neither,
Fullscreen,
Maximized,
}
impl FullscreenOrMaximized {
/// Returns `true` if the fullscreen or maximized is [`Neither`].
/// ///
/// [`Floating`]: Float::Floating /// [`Neither`]: FullscreenOrMaximized::Neither
#[must_use] #[must_use]
pub fn is_floating(&self) -> bool { pub fn is_neither(&self) -> bool {
matches!(self, Self::Floating(_)) matches!(self, Self::Neither)
} }
/// Returns `true` if the status is [`Fullscreen`]. /// Returns `true` if the fullscreen or maximized is [`Fullscreen`].
/// ///
/// [`Fullscreen`]: Status::Fullscreen /// [`Fullscreen`]: FullscreenOrMaximized::Fullscreen
#[must_use] #[must_use]
pub fn is_fullscreen(&self) -> bool { pub fn is_fullscreen(&self) -> bool {
matches!(self, Self::Fullscreen(..)) matches!(self, Self::Fullscreen)
} }
/// Returns `true` if the status is [`Maximized`]. /// Returns `true` if the fullscreen or maximized is [`Maximized`].
/// ///
/// [`Maximized`]: Status::Maximized /// [`Maximized`]: FullscreenOrMaximized::Maximized
#[must_use] #[must_use]
pub fn is_maximized(&self) -> bool { pub fn is_maximized(&self) -> bool {
matches!(self, Self::Maximized(..)) matches!(self, Self::Maximized)
} }
} }
@ -178,9 +339,10 @@ impl Default for WindowElementState {
Self { Self {
// INFO: I think this will assign the id on use of the state, not on window spawn. // INFO: I think this will assign the id on use of the state, not on window spawn.
id: WindowId::next(), id: WindowId::next(),
status: Status::Tiled(None),
loc_request_state: LocationRequestState::Idle, loc_request_state: LocationRequestState::Idle,
tags: vec![], tags: vec![],
floating_or_tiled: FloatingOrTiled::Tiled(None),
fullscreen_or_maximized: FullscreenOrMaximized::Neither,
} }
} }
} }