Add fullscreen, maximize to api

This commit is contained in:
Ottatop 2023-08-11 18:48:51 -05:00
parent 275d968e77
commit 0ead02921f
11 changed files with 181 additions and 128 deletions

View file

@ -11,6 +11,8 @@
---@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 Spawn { command: string[], callback_id: integer? }? ---@field Spawn { command: string[], callback_id: integer? }?
---@field Request Request? ---@field Request Request?
--Tags --Tags
@ -25,6 +27,12 @@
---@alias Msg _Msg | "Quit" ---@alias Msg _Msg | "Quit"
---@alias StatusName
---| "Floating"
---| "Tiled"
---| "Fullscreen"
---| "Maximized"
-------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------
---@class __Request ---@class __Request

View file

@ -64,6 +64,27 @@ require("pinnacle").setup(function(pinnacle)
process.spawn("nautilus") process.spawn("nautilus")
end) end)
input.keybind({ mod_key }, keys.f, function()
local win = window.get_focused()
if win ~= nil then
win:set_status("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")
end
end)
-- Just testing stuff -- Just testing stuff
input.keybind({ mod_key }, keys.h, function() input.keybind({ mod_key }, keys.h, function()
local dp2 = output.get_by_name("DP-2") local dp2 = output.get_by_name("DP-2")

View file

@ -101,6 +101,12 @@ function window:toggle_floating()
window_module.toggle_floating(self) window_module.toggle_floating(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
@ -241,7 +247,8 @@ end
---Get all windows. ---Get all windows.
---@return Window[] ---@return Window[]
function window_module.get_all() function window_module.get_all()
local window_ids = Request("GetWindows").RequestResponse.response.Windows.window_ids local window_ids =
Request("GetWindows").RequestResponse.response.Windows.window_ids
---@type Window[] ---@type Window[]
local windows = {} local windows = {}
for _, window_id in pairs(window_ids) do for _, window_id in pairs(window_ids) do
@ -383,6 +390,18 @@ function window_module.toggle_floating(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

View file

@ -3,7 +3,12 @@
// The MessagePack format for these is a one-element map where the element's key is the enum name and its // The MessagePack format for these is a one-element map where the element's key is the enum name and its
// value is a map of the enum's values // value is a map of the enum's values
use crate::{layout::Layout, output::OutputName, tag::TagId, window::window_state::WindowId}; use crate::{
layout::Layout,
output::OutputName,
tag::TagId,
window::window_state::{StatusName, WindowId},
};
#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)] #[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy)]
pub struct CallbackId(pub u32); pub struct CallbackId(pub u32);
@ -42,6 +47,10 @@ pub enum Msg {
window_id: WindowId, window_id: WindowId,
tag_id: TagId, tag_id: TagId,
}, },
SetStatus {
window_id: WindowId,
status: StatusName,
},
// Tag management // Tag management
ToggleTag { ToggleTag {

View file

@ -37,7 +37,8 @@ pub fn post_repaint(
dmabuf_feedback: Option<SurfaceDmabufFeedback<'_>>, dmabuf_feedback: Option<SurfaceDmabufFeedback<'_>>,
time: Duration, time: Duration,
) { ) {
let throttle = Some(Duration::from_secs(1)); // let throttle = Some(Duration::from_secs(1));
let throttle = Some(Duration::ZERO);
space.elements().for_each(|window| { space.elements().for_each(|window| {
window.with_surfaces(|surface, states_inner| { window.with_surfaces(|surface, states_inner| {

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
use smithay::{ use smithay::{
desktop::{LayerSurface, PopupKind}, desktop::{LayerSurface, PopupKind, Space},
input::{ input::{
keyboard::KeyboardTarget, keyboard::KeyboardTarget,
pointer::{MotionEvent, PointerTarget}, pointer::{MotionEvent, PointerTarget},
@ -17,7 +17,7 @@ use crate::{backend::Backend, state::State, window::WindowElement};
#[derive(Default)] #[derive(Default)]
pub struct FocusState { pub struct FocusState {
focus_stack: Vec<WindowElement>, pub focus_stack: Vec<WindowElement>,
pub focused_output: Option<Output>, pub focused_output: Option<Output>,
} }
@ -43,6 +43,18 @@ impl FocusState {
self.focus_stack.retain(|win| win != &window); self.focus_stack.retain(|win| win != &window);
self.focus_stack.push(window); self.focus_stack.push(window);
} }
/// Fix focus layering for all windows in the `focus_stack`.
///
/// This will call `space.map_element` on all windows from front
/// to back to correct their z locations.
pub fn fix_up_focus(&self, space: &mut Space<WindowElement>) {
for win in self.focus_stack.iter() {
if let Some(loc) = space.element_location(win) {
space.map_element(win.clone(), loc, false);
}
}
}
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]

View file

@ -122,13 +122,13 @@ impl<B: Backend> CompositorHandler for State<B> {
window.with_state(|state| { window.with_state(|state| {
if let LocationRequestState::Acknowledged(new_pos) = state.loc_request_state { if let LocationRequestState::Acknowledged(new_pos) = state.loc_request_state {
state.loc_request_state = LocationRequestState::Idle; state.loc_request_state = LocationRequestState::Idle;
if window.is_x11() {
tracing::warn!("did something with X11 window here");
}
self.space.map_element(window.clone(), new_pos, false); self.space.map_element(window.clone(), new_pos, false);
} }
}); });
} }
// correct focus layering
self.focus_state.fix_up_focus(&mut self.space);
} }
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState { fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {

View file

@ -30,7 +30,10 @@ use crate::{
backend::Backend, backend::Backend,
focus::FocusTarget, focus::FocusTarget,
state::{State, WithState}, state::{State, WithState},
window::{window_state::LocationRequestState, WindowBlocker, WindowElement, BLOCKER_COUNTER}, window::{
window_state::{LocationRequestState, StatusName},
WindowElement, BLOCKER_COUNTER,
},
}; };
impl<B: Backend> XdgShellHandler for State<B> { impl<B: Backend> XdgShellHandler for State<B> {
@ -39,17 +42,9 @@ 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)); let window = WindowElement::Wayland(Window::new(surface.clone()));
{ window.set_status(StatusName::Tiled);
let WindowElement::Wayland(window) = &window else { unreachable!() };
window.toplevel().with_pending_state(|tl_state| {
tl_state.states.set(xdg_toplevel::State::TiledTop);
tl_state.states.set(xdg_toplevel::State::TiledBottom);
tl_state.states.set(xdg_toplevel::State::TiledLeft);
tl_state.states.set(xdg_toplevel::State::TiledRight);
});
}
window.with_state(|state| { window.with_state(|state| {
state.tags = match ( state.tags = match (
@ -92,6 +87,8 @@ impl<B: Backend> XdgShellHandler for State<B> {
.cloned() .cloned()
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// note to self: don't reorder this
// TODO: fix it so that reordering this doesn't break stuff
self.windows.push(window.clone()); self.windows.push(window.clone());
// self.space.map_element(window.clone(), (0, 0), true); // self.space.map_element(window.clone(), (0, 0), true);
if let Some(focused_output) = self.focus_state.focused_output.clone() { if let Some(focused_output) = self.focus_state.focused_output.clone() {
@ -103,7 +100,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
); );
for win in windows_on_output.iter() { for win in windows_on_output.iter() {
if let Some(surf) = win.wl_surface() { if let Some(surf) = win.wl_surface() {
compositor::add_blocker(&surf, WindowBlocker); compositor::add_blocker(&surf, crate::window::WindowBlocker);
} }
} }
let clone = window.clone(); let clone = window.clone();
@ -146,17 +143,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
.is_some_and(|surf| &surf != surface.wl_surface()) .is_some_and(|surf| &surf != surface.wl_surface())
}); });
if let Some(focused_output) = self.focus_state.focused_output.as_ref().cloned() { if let Some(focused_output) = self.focus_state.focused_output.as_ref().cloned() {
focused_output.with_state(|state| { self.update_windows(&focused_output);
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,
&focused_output,
);
}
});
} }
// let mut windows: Vec<Window> = self.space.elements().cloned().collect(); // let mut windows: Vec<Window> = self.space.elements().cloned().collect();

View file

@ -206,6 +206,18 @@ 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 } => {
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);
if let Some(output) = outputs.into_iter().next() {
self.update_windows(&output);
}
}
// Tags ----------------------------------------
Msg::ToggleTag { tag_id } => { Msg::ToggleTag { tag_id } => {
tracing::debug!("ToggleTag"); tracing::debug!("ToggleTag");
if let Some(tag) = tag_id.tag(self) { if let Some(tag) = tag_id.tag(self) {
@ -735,6 +747,10 @@ pub fn schedule_on_commit<F, B: Backend>(
for window in windows.iter().filter(|win| win.alive()) { for window in windows.iter().filter(|win| win.alive()) {
if window.with_state(|state| !matches!(state.loc_request_state, LocationRequestState::Idle)) if window.with_state(|state| !matches!(state.loc_request_state, LocationRequestState::Idle))
{ {
tracing::debug!(
"window state is {:?}",
window.with_state(|state| state.loc_request_state.clone())
);
data.state.loop_handle.insert_idle(|data| { data.state.loop_handle.insert_idle(|data| {
schedule_on_commit(data, windows, on_commit); schedule_on_commit(data, windows, on_commit);
}); });

View file

@ -40,7 +40,7 @@ use crate::{
state::{State, WithState}, state::{State, WithState},
}; };
use self::window_state::{LocationRequestState, Status, WindowElementState}; use self::window_state::{LocationRequestState, Status, StatusName, WindowElementState};
pub mod window_state; pub mod window_state;
@ -269,25 +269,19 @@ impl WindowElement {
matches!(self, Self::X11(..)) matches!(self, Self::X11(..))
} }
/// Set this window to floating. pub fn set_status(&self, status: StatusName) {
/// let prev_status = self.with_state(|state| state.status);
/// This will change the size of the window only. let geo = match prev_status {
/// Call `State.update_windows` to perform mapping. Status::Floating(rect)
pub fn set_floating(&self) { | Status::Tiled(Some(rect))
let status = self.with_state(|state| state.status); | Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
match status { match status {
Status::Floating(_) => (), StatusName::Floating => {
Status::Tiled(rect) | Status::Fullscreen(rect) | Status::Maximized(rect) => {
if let Some(rect) = rect {
self.change_geometry(rect);
self.with_state(|state| state.status = Status::Floating(rect));
} else {
// TODO: is this the same as from the space? prolly not
let geo = self.geometry();
self.with_state(|state| state.status = Status::Floating(geo)); self.with_state(|state| state.status = Status::Floating(geo));
}
}
}
if let WindowElement::Wayland(window) = self { if let WindowElement::Wayland(window) = self {
window.toplevel().with_pending_state(|state| { window.toplevel().with_pending_state(|state| {
@ -300,16 +294,7 @@ impl WindowElement {
}); });
} }
} }
StatusName::Tiled => {
/// Call compute_tiled_windows after this
pub fn set_tiled(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Tiled(Some(geo))); self.with_state(|state| state.status = Status::Tiled(Some(geo)));
if let WindowElement::Wayland(window) = self { if let WindowElement::Wayland(window) = self {
@ -323,16 +308,7 @@ impl WindowElement {
}); });
} }
} }
StatusName::Fullscreen => {
pub fn set_fullscreen(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Fullscreen(Some(geo))); self.with_state(|state| state.status = Status::Fullscreen(Some(geo)));
if let WindowElement::Wayland(window) = self { if let WindowElement::Wayland(window) = self {
@ -346,16 +322,7 @@ impl WindowElement {
}); });
} }
} }
StatusName::Maximized => {
pub fn set_maximized(&self) {
let geo = match self.with_state(|state| state.status) {
Status::Floating(rect)
| Status::Tiled(Some(rect))
| Status::Fullscreen(Some(rect))
| Status::Maximized(Some(rect)) => rect,
_ => self.geometry(),
};
self.with_state(|state| state.status = Status::Maximized(Some(geo))); self.with_state(|state| state.status = Status::Maximized(Some(geo)));
if let WindowElement::Wayland(window) = self { if let WindowElement::Wayland(window) = self {
@ -370,6 +337,8 @@ impl WindowElement {
} }
} }
} }
}
}
impl IsAlive for WindowElement { impl IsAlive for WindowElement {
fn alive(&self) -> bool { fn alive(&self) -> bool {
@ -604,10 +573,11 @@ impl<B: Backend> State<B> {
/// Toggle a window's floating status. /// Toggle a window's floating status.
pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement) { pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement) {
if window.with_state(|state| state.status.is_floating()) { match window.with_state(|state| state.status) {
window.set_tiled(); Status::Floating(_) => window.set_status(StatusName::Tiled),
} else { Status::Tiled(_) => window.set_status(StatusName::Fullscreen),
window.set_floating(); Status::Fullscreen(_) => window.set_status(StatusName::Maximized),
Status::Maximized(_) => window.set_status(StatusName::Floating),
} }
// TODO: don't use the focused output, use the one the window is on // TODO: don't use the focused output, use the one the window is on
@ -637,6 +607,7 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement)
false false
}) })
}) })
.filter(|win| win != window)
.collect::<Vec<_>>() .collect::<Vec<_>>()
}); });

View file

@ -122,6 +122,15 @@ pub enum Status {
Fullscreen(Option<Rectangle<i32, Logical>>), Fullscreen(Option<Rectangle<i32, Logical>>),
Maximized(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 Status {
/// Returns `true` if the float is [`Tiled`]. /// Returns `true` if the float is [`Tiled`].