mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-29 20:34:46 +01:00
Add fullscreen, maximize to api
This commit is contained in:
parent
275d968e77
commit
0ead02921f
11 changed files with 181 additions and 128 deletions
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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| {
|
||||
|
|
16
src/focus.rs
16
src/focus.rs
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
16
src/state.rs
16
src/state.rs
|
@ -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);
|
||||
});
|
||||
|
|
165
src/window.rs
165
src/window.rs
|
@ -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<_>>()
|
||||
});
|
||||
|
||||
|
|
|
@ -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`].
|
||||
|
|
Loading…
Add table
Reference in a new issue