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 MoveWindowToTag { 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 Request Request?
--Tags
@ -25,6 +27,12 @@
---@alias Msg _Msg | "Quit"
---@alias StatusName
---| "Floating"
---| "Tiled"
---| "Fullscreen"
---| "Maximized"
--------------------------------------------------------------------------------------------
---@class __Request

View file

@ -64,6 +64,27 @@ require("pinnacle").setup(function(pinnacle)
process.spawn("nautilus")
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
input.keybind({ mod_key }, keys.h, function()
local dp2 = output.get_by_name("DP-2")

View file

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

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
// 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)]
pub struct CallbackId(pub u32);
@ -42,6 +47,10 @@ pub enum Msg {
window_id: WindowId,
tag_id: TagId,
},
SetStatus {
window_id: WindowId,
status: StatusName,
},
// Tag management
ToggleTag {

View file

@ -37,7 +37,8 @@ pub fn post_repaint(
dmabuf_feedback: Option<SurfaceDmabufFeedback<'_>>,
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| {
window.with_surfaces(|surface, states_inner| {

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use smithay::{
desktop::{LayerSurface, PopupKind},
desktop::{LayerSurface, PopupKind, Space},
input::{
keyboard::KeyboardTarget,
pointer::{MotionEvent, PointerTarget},
@ -17,7 +17,7 @@ use crate::{backend::Backend, state::State, window::WindowElement};
#[derive(Default)]
pub struct FocusState {
focus_stack: Vec<WindowElement>,
pub focus_stack: Vec<WindowElement>,
pub focused_output: Option<Output>,
}
@ -43,6 +43,18 @@ impl FocusState {
self.focus_stack.retain(|win| win != &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)]

View file

@ -122,13 +122,13 @@ impl<B: Backend> CompositorHandler for State<B> {
window.with_state(|state| {
if let LocationRequestState::Acknowledged(new_pos) = state.loc_request_state {
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);
}
});
}
// correct focus layering
self.focus_state.fix_up_focus(&mut self.space);
}
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {

View file

@ -30,7 +30,10 @@ use crate::{
backend::Backend,
focus::FocusTarget,
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> {
@ -39,17 +42,9 @@ impl<B: Backend> XdgShellHandler for State<B> {
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
let window = WindowElement::Wayland(Window::new(surface));
let window = WindowElement::Wayland(Window::new(surface.clone()));
{
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.set_status(StatusName::Tiled);
window.with_state(|state| {
state.tags = match (
@ -92,6 +87,8 @@ impl<B: Backend> XdgShellHandler for State<B> {
.cloned()
.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.space.map_element(window.clone(), (0, 0), true);
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() {
if let Some(surf) = win.wl_surface() {
compositor::add_blocker(&surf, WindowBlocker);
compositor::add_blocker(&surf, crate::window::WindowBlocker);
}
}
let clone = window.clone();
@ -146,17 +143,7 @@ impl<B: Backend> XdgShellHandler for State<B> {
.is_some_and(|surf| &surf != surface.wl_surface())
});
if let Some(focused_output) = self.focus_state.focused_output.as_ref().cloned() {
focused_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,
&focused_output,
);
}
});
self.update_windows(&focused_output);
}
// 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.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 } => {
tracing::debug!("ToggleTag");
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()) {
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| {
schedule_on_commit(data, windows, on_commit);
});

View file

@ -40,7 +40,7 @@ use crate::{
state::{State, WithState},
};
use self::window_state::{LocationRequestState, Status, WindowElementState};
use self::window_state::{LocationRequestState, Status, StatusName, WindowElementState};
pub mod window_state;
@ -269,104 +269,73 @@ impl WindowElement {
matches!(self, Self::X11(..))
}
/// Set this window to floating.
///
/// This will change the size of the window only.
/// Call `State.update_windows` to perform mapping.
pub fn set_floating(&self) {
let status = self.with_state(|state| state.status);
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 {
Status::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));
StatusName::Floating => {
self.with_state(|state| state.status = Status::Floating(geo));
if let WindowElement::Wayland(window) = self {
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);
});
}
}
}
StatusName::Tiled => {
self.with_state(|state| state.status = Status::Tiled(Some(geo)));
if let WindowElement::Wayland(window) = self {
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);
});
}
}
if let WindowElement::Wayland(window) = self {
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);
});
}
}
StatusName::Fullscreen => {
self.with_state(|state| state.status = Status::Fullscreen(Some(geo)));
/// 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)));
if let WindowElement::Wayland(window) = self {
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);
});
}
}
StatusName::Maximized => {
self.with_state(|state| state.status = Status::Maximized(Some(geo)));
if let WindowElement::Wayland(window) = self {
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);
});
}
}
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)));
if let WindowElement::Wayland(window) = self {
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);
});
}
}
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)));
if let WindowElement::Wayland(window) = self {
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);
});
if let WindowElement::Wayland(window) = self {
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);
});
}
}
}
}
}
@ -604,10 +573,11 @@ impl<B: Backend> State<B> {
/// Toggle a window's floating status.
pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement) {
if window.with_state(|state| state.status.is_floating()) {
window.set_tiled();
} else {
window.set_floating();
match window.with_state(|state| state.status) {
Status::Floating(_) => window.set_status(StatusName::Tiled),
Status::Tiled(_) => window.set_status(StatusName::Fullscreen),
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
@ -637,6 +607,7 @@ pub fn toggle_floating<B: Backend>(state: &mut State<B>, window: &WindowElement)
false
})
})
.filter(|win| win != window)
.collect::<Vec<_>>()
});

View file

@ -122,6 +122,15 @@ pub enum Status {
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`].