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

View file

@ -10,7 +10,9 @@
---@field SetWindowSize { window_id: WindowId, width: integer?, height: integer? }?
---@field MoveWindowToTag { 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 Request Request?
@ -26,9 +28,8 @@
---@alias Msg _Msg | "Quit"
---@alias StatusName
---| "Floating"
---| "Tiled"
---@alias FullscreenOrMaximized
---| "Neither"
---| "Fullscreen"
---| "Maximized"
@ -62,7 +63,7 @@
--Windows
---@field Window { window_id: WindowId|nil }?
---@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
---@field Output { output_name: OutputName? }?
---@field Outputs { output_names: OutputName[] }?

View file

@ -90,12 +90,6 @@ function window:close()
window_module.close(self)
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.
---
---### Example
@ -158,10 +152,48 @@ function window:title()
end
---Get this window's status.
---@return StatusName|nil
---@return boolean|nil
---@see WindowModule.status — The corresponding module function
function window:status()
return window_module.status(self)
function window:floating()
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
---Get whether or not this window is focused.
@ -361,18 +393,6 @@ function window_module.close(win)
})
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.
---
---### Example
@ -482,18 +502,48 @@ function window_module.title(win)
return title
end
---Get this window's status.
---Get this window's floating status.
---@param win Window
---@return StatusName|nil
---@return boolean|nil
---@see Window.status — The corresponding object method
function window_module.status(win)
function window_module.floating(win)
local response = Request({
GetWindowProps = {
window_id = win:id(),
},
})
local status = response.RequestResponse.response.WindowProps.status
return status
local floating = response.RequestResponse.response.WindowProps.floating
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
---Get whether or not this window is focused.

View file

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

View file

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

View file

@ -18,7 +18,7 @@ use smithay::{
use crate::{
backend::Backend,
state::{State, WithState},
window::{window_state::Status, WindowElement},
window::{window_state::FloatingOrTiled, WindowElement},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -332,8 +332,9 @@ pub fn handle_commit<B: Backend>(state: &mut State<B>, surface: &WlSurface) -> O
.size;
window.with_state(|state| {
if state.status.is_floating() {
state.status = Status::Floating(Rectangle::from_loc_and_size(window_loc, size));
if state.floating_or_tiled.is_floating() {
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;
};
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;
}
@ -412,7 +414,7 @@ pub fn resize_request_server<B: Backend>(
return;
};
if window.with_state(|state| state.status.is_tiled()) {
if window.with_state(|state| state.floating_or_tiled.is_tiled()) {
return;
}

View file

@ -30,10 +30,7 @@ use crate::{
backend::Backend,
focus::FocusTarget,
state::{State, WithState},
window::{
window_state::{LocationRequestState, StatusName},
WindowElement, BLOCKER_COUNTER,
},
window::{window_state::LocationRequestState, WindowElement, BLOCKER_COUNTER},
};
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) {
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| {
state.tags = match (
@ -323,7 +325,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
return;
};
window.set_status(StatusName::Fullscreen);
if !window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
window.toggle_fullscreen();
}
}
surface.send_configure();
@ -351,8 +355,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
return;
};
// TODO: remember the last status instead of tiled
window.set_status(StatusName::Tiled);
if window.with_state(|state| state.fullscreen_or_maximized.is_fullscreen()) {
window.toggle_fullscreen();
}
}
fn maximize_request(&mut self, surface: ToplevelSurface) {
@ -360,8 +365,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
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
}
@ -370,8 +376,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
return;
};
// TODO: remember last status
window.set_status(StatusName::Tiled);
if window.with_state(|state| state.fullscreen_or_maximized.is_maximized()) {
window.toggle_maximized();
}
}
// fn minimize_request(&mut self, surface: ToplevelSurface) {

View file

@ -28,10 +28,7 @@ use crate::{
backend::Backend,
focus::FocusTarget,
state::{CalloopData, WithState},
window::{
window_state::{Status, StatusName},
WindowBlocker, WindowElement, BLOCKER_COUNTER,
},
window::{window_state::FloatingOrTiled, WindowBlocker, WindowElement, BLOCKER_COUNTER},
};
impl<B: Backend> XwmHandler for CalloopData<B> {
@ -131,7 +128,7 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
if should_float(surface) {
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
.windows
.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) {
self.state.update_windows(&output);
// self.state.re_layout(&output);
}
if let Some(output) = win.output(&self.state) {
self.state.update_windows(&output);
}
}
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 {
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) {
@ -351,8 +349,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else {
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) {
@ -363,9 +363,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else {
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) {
@ -375,8 +376,10 @@ impl<B: Backend> XwmHandler for CalloopData<B> {
let Some(window) = (|| self.state.window_for_surface(&window.wl_surface()?))() else {
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(

View file

@ -75,7 +75,8 @@ impl<B: Backend> State<B> {
let top_fullscreen_window = self.focus_state.focus_stack.iter().rev().find(|win| {
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},
tag::Tag,
window::{
window_state::{LocationRequestState, Status},
window_state::{FullscreenOrMaximized, LocationRequestState},
WindowElement,
},
};
@ -70,7 +70,11 @@ impl<B: Backend> State<B> {
let tiled_windows = windows_on_foc_tags
.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()
.collect::<Vec<_>>();
@ -78,11 +82,11 @@ impl<B: Backend> State<B> {
let output_geo = self.space.output_geometry(output).expect("no output geo");
for window in windows_on_foc_tags.iter() {
match window.with_state(|state| state.status) {
Status::Fullscreen(_) => {
match window.with_state(|state| state.fullscreen_or_maximized) {
FullscreenOrMaximized::Fullscreen => {
window.change_geometry(output_geo);
}
Status::Maximized(_) => {
FullscreenOrMaximized::Maximized => {
let map = layer_map_for_output(output);
let geo = if map.layers().peekable().peek().is_none() {
// 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);
}
_ => (),
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> {
windows
.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| {
window.with_state(|state| {
for tag in state.tags.iter() {

View file

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

View file

@ -20,10 +20,7 @@ use crate::{
focus::FocusState,
grab::resize_grab::ResizeSurfaceState,
tag::Tag,
window::{
window_state::{LocationRequestState, Status, StatusName},
WindowElement,
},
window::{window_state::LocationRequestState, WindowElement},
};
use calloop::futures::Scheduler;
use futures_lite::AsyncBufReadExt;
@ -201,15 +198,26 @@ impl<B: Backend> State<B> {
self.update_windows(&output);
// self.re_layout(&output);
}
Msg::SetStatus { window_id, status } => {
Msg::ToggleFloating { window_id } => {
let Some(window) = window_id.window(self) else { return };
window.set_status(status);
let outputs = self.space.outputs_for_element(&window);
self.focus_state.set_focus(window);
window.toggle_floating();
if let Some(output) = outputs.into_iter().next() {
self.update_windows(&output);
}
let Some(output) = window.output(self) else { return };
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 ----------------------------------------
@ -369,19 +377,17 @@ impl<B: Backend> State<B> {
}
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| {
self.focus_state
.current_focus() // TODO: actual focus
.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(
&mut stream,
&OutgoingMsg::RequestResponse {
@ -392,7 +398,8 @@ impl<B: Backend> State<B> {
class,
title,
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

View file

@ -20,10 +20,7 @@ use smithay::{
},
output::Output,
reexports::{
wayland_protocols::{
wp::presentation_time::server::wp_presentation_feedback,
xdg::shell::server::xdg_toplevel,
},
wayland_protocols::wp::presentation_time::server::wp_presentation_feedback,
wayland_server::protocol::wl_surface::WlSurface,
},
utils::{user_data::UserDataMap, IsAlive, Logical, Point, Rectangle, Serial, Size},
@ -40,7 +37,7 @@ use crate::{
state::{State, WithState},
};
use self::window_state::{LocationRequestState, Status, StatusName, WindowElementState};
use self::window_state::{LocationRequestState, WindowElementState};
pub mod window_state;
@ -184,6 +181,8 @@ impl WindowElement {
/// configures to Wayland windows.
///
/// Xwayland windows will still receive a configure.
///
/// This method uses a [`RefCell`].
// TODO: ^ does that make things flicker?
pub fn change_geometry(&self, new_geo: Rectangle<i32, Logical>) {
match self {
@ -268,116 +267,6 @@ impl WindowElement {
pub fn is_x11(&self) -> bool {
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 {

View file

@ -6,7 +6,8 @@ use std::{
};
use smithay::{
desktop::Window,
desktop::{space::SpaceElement, Window},
reexports::wayland_protocols::xdg::shell::server::xdg_toplevel,
utils::{Logical, Point, Rectangle, Serial},
};
@ -66,12 +67,12 @@ impl WithState for Window {
pub struct WindowElementState {
/// The id of this window.
pub id: WindowId,
/// Whether the window is floating, tiled, fullscreen, or maximized.
pub status: Status,
/// The window's resize state. See [WindowResizeState] for more.
pub loc_request_state: LocationRequestState,
/// What tags the window is currently on.
pub tags: Vec<Tag>,
pub floating_or_tiled: FloatingOrTiled,
pub fullscreen_or_maximized: FullscreenOrMaximized,
}
/// The state of a window's resize operation.
@ -114,55 +115,215 @@ pub enum LocationRequestState {
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)]
pub enum Status {
pub enum FloatingOrTiled {
Floating(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 {
/// Returns `true` if the float is [`Tiled`].
impl FloatingOrTiled {
/// 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]
pub fn is_tiled(&self) -> bool {
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]
pub fn is_floating(&self) -> bool {
matches!(self, Self::Floating(_))
pub fn is_neither(&self) -> bool {
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]
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]
pub fn is_maximized(&self) -> bool {
matches!(self, Self::Maximized(..))
matches!(self, Self::Maximized)
}
}
@ -178,9 +339,10 @@ impl Default for WindowElementState {
Self {
// INFO: I think this will assign the id on use of the state, not on window spawn.
id: WindowId::next(),
status: Status::Tiled(None),
loc_request_state: LocationRequestState::Idle,
tags: vec![],
floating_or_tiled: FloatingOrTiled::Tiled(None),
fullscreen_or_maximized: FullscreenOrMaximized::Neither,
}
}
}