mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-30 20:34:49 +01:00
Rework fullscreen tracking again
Lots of stuff needs cleaning up
This commit is contained in:
parent
947ccdf464
commit
dbde8545c3
14 changed files with 389 additions and 335 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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[] }?
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
|
119
src/state.rs
119
src/state.rs
|
@ -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
|
||||
|
|
119
src/window.rs
119
src/window.rs
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue