diff --git a/pinnacle_api_lua/client.lua b/pinnacle_api_lua/client.lua index 8cb758e..024ec4f 100644 --- a/pinnacle_api_lua/client.lua +++ b/pinnacle_api_lua/client.lua @@ -10,4 +10,14 @@ function M.close_window(client_id) }) end +---Toggle a window's floating status. +---@param client_id integer? The id of the window you want to toggle, or nil to toggle the currently focused window, if any. +function M.toggle_floating(client_id) + SendMsg({ + ToggleFloating = { + client_id = client_id or "nil", + }, + }) +end + return M diff --git a/pinnacle_api_lua/input.lua b/pinnacle_api_lua/input.lua index 5a6f3e6..0687172 100644 --- a/pinnacle_api_lua/input.lua +++ b/pinnacle_api_lua/input.lua @@ -3,7 +3,7 @@ local M = {} ---Set a keybind. If called on an already existing keybind, it gets replaced. ---@param key Keys The key for the keybind. NOTE: uppercase and lowercase characters are considered different. ---@param modifiers Modifiers[] Which modifiers need to be pressed for the keybind to trigger. ----@param action function What to run. +---@param action fun() What to run. function M.keybind(modifiers, key, action) table.insert(CallbackTable, action) SendMsg({ diff --git a/src/api/msg.rs b/src/api/msg.rs index 24bb767..ba3966b 100644 --- a/src/api/msg.rs +++ b/src/api/msg.rs @@ -14,6 +14,9 @@ pub enum Msg { CloseWindow { client_id: Option, }, + ToggleFloating { + client_id: Option, + }, } #[derive(Debug, serde::Serialize, serde::Deserialize)] diff --git a/src/focus.rs b/src/focus.rs index e17be0c..5c5817d 100644 --- a/src/focus.rs +++ b/src/focus.rs @@ -1,9 +1,4 @@ -use smithay::{ - desktop::Window, - utils::{IsAlive, Serial}, -}; - -use crate::{backend::Backend, state::State}; +use smithay::{desktop::Window, utils::IsAlive}; #[derive(Default)] pub struct FocusState { @@ -30,19 +25,3 @@ impl FocusState { self.focus_stack.push(window); } } - -impl State { - pub fn set_focus(&mut self, window: Window, serial: Serial) { - // INFO: this is inserted into the loop because foot didn't like it when you set the focus - // |` immediately after creating the toplevel - // TODO: figure out why - self.loop_handle.insert_idle(move |data| { - data.state.focus_state.set_focus(window.clone()); - data.state.seat.get_keyboard().unwrap().set_focus( - &mut data.state, - Some(window.toplevel().wl_surface().clone()), - serial, - ); - }); - } -} diff --git a/src/handlers.rs b/src/handlers.rs index e8fabfb..bb515d8 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -177,7 +177,13 @@ impl SeatHandler for State { self.cursor_status = image; } - fn focus_changed(&mut self, _seat: &Seat, _focused: Option<&Self::KeyboardFocus>) {} + fn focus_changed(&mut self, _seat: &Seat, focused: Option<&Self::KeyboardFocus>) { + if let Some(wl_surface) = focused { + if let Some(window) = self.window_for_surface(wl_surface) { + self.focus_state.set_focus(window); + } + } + } } delegate_seat!(@ State); @@ -195,8 +201,15 @@ impl XdgShellHandler for State { fn new_toplevel(&mut self, surface: ToplevelSurface) { let window = Window::new(surface); + self.space.map_element(window.clone(), (0, 0), true); - self.set_focus(window, SERIAL_COUNTER.next_serial()); + self.loop_handle.insert_idle(move |data| { + data.state.seat.get_keyboard().unwrap().set_focus( + &mut data.state, + Some(window.toplevel().wl_surface().clone()), + SERIAL_COUNTER.next_serial(), + ) + }); let windows: Vec = self.space.elements().cloned().collect(); Layout::master_stack(self, windows, crate::layout::Direction::Left); diff --git a/src/input.rs b/src/input.rs index 95ff8ff..f9706a4 100644 --- a/src/input.rs +++ b/src/input.rs @@ -131,7 +131,7 @@ impl State { // Move window to top of stack. self.space.raise_element(&window, true); - self.set_focus(window, serial); + keyboard.set_focus(self, Some(window.toplevel().wl_surface().clone()), serial); self.space.elements().for_each(|window| { window.toplevel().send_configure(); diff --git a/src/layout/automatic.rs b/src/layout/automatic.rs index 2906d8e..af320a7 100644 --- a/src/layout/automatic.rs +++ b/src/layout/automatic.rs @@ -1,14 +1,24 @@ use smithay::desktop::Window; -use crate::{backend::Backend, state::State}; +use crate::{ + backend::Backend, + state::State, + window::window_state::{Float, WindowState}, +}; use super::{Direction, Layout}; impl Layout { - pub fn master_stack(state: &mut State, windows: Vec, side: Direction) { + pub fn master_stack( + state: &mut State, + mut windows: Vec, + side: Direction, + ) { + windows.retain(|win| { + WindowState::with_state(win, |state| matches!(state.floating, Float::Tiled(_))) + }); match side { Direction::Left => { - // println!("MasterStack layout_windows"); let window_count = windows.len(); if window_count == 0 { return; diff --git a/src/state.rs b/src/state.rs index 04c7261..732de6e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -127,17 +127,16 @@ impl State { Msg::SetMousebind { button } => todo!(), Msg::CloseWindow { client_id } => { tracing::info!("CloseWindow {:?}", client_id); - if let Some(window) = data - .state - .seat - .get_keyboard() - .unwrap() - .current_focus() - .and_then(|wl_surface| data.state.window_for_surface(&wl_surface)) - { + if let Some(window) = data.state.focus_state.current_focus() { window.toplevel().send_close(); } } + Msg::ToggleFloating { client_id } => { + // TODO: add client_ids + if let Some(window) = data.state.focus_state.current_focus() { + crate::window::toggle_floating(&mut data.state, &window); + } + } }; } Event::Closed => todo!(), diff --git a/src/window.rs b/src/window.rs index 2e1cdb4..68c5bbe 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,8 +1,11 @@ use std::cell::RefCell; -use smithay::{reexports::wayland_server::protocol::wl_surface::WlSurface, wayland::compositor}; +use smithay::{ + desktop::Window, reexports::wayland_server::protocol::wl_surface::WlSurface, + wayland::compositor, +}; -use crate::{backend::Backend, state::State}; +use crate::{backend::Backend, layout::Layout, state::State}; use self::window_state::{Float, WindowState}; @@ -23,32 +26,35 @@ pub trait SurfaceState: Default + 'static { } } -pub fn toggle_floating(state: &mut State, wl_surface: &WlSurface) { - WindowState::with_state(wl_surface, |window_state| { - let window = state.window_for_surface(wl_surface).unwrap(); +pub fn toggle_floating(state: &mut State, window: &Window) { + tracing::info!("toggling floating"); + WindowState::with_state(window, |window_state| { match window_state.floating { - Float::NotFloating(prev_loc_and_size) => { + Float::Tiled(prev_loc_and_size) => { if let Some((prev_loc, prev_size)) = prev_loc_and_size { + tracing::info!("changing size and loc"); window.toplevel().with_pending_state(|state| { state.size = Some(prev_size); }); window.toplevel().send_pending_configure(); - state.space.map_element(window, prev_loc, false); // TODO: should it activate? + state.space.map_element(window.clone(), prev_loc, false); // TODO: should it activate? } - window_state.floating = Float::Floating + window_state.floating = Float::Floating; } Float::Floating => { - // TODO: recompute all non-floating window positions - - window_state.floating = Float::NotFloating(Some(( - state.space.element_location(&window).unwrap(), // We get the location this way + window_state.floating = Float::Tiled(Some(( + state.space.element_location(window).unwrap(), // We get the location this way // because window.geometry().loc doesn't seem to be the actual location window.geometry().size, ))); } } - }) + }); + + let windows = state.space.elements().cloned().collect::>(); + Layout::master_stack(state, windows, crate::layout::Direction::Left); + state.space.raise_element(window, true); } diff --git a/src/window/window_state.rs b/src/window/window_state.rs index 6bf6461..7f1cafd 100644 --- a/src/window/window_state.rs +++ b/src/window/window_state.rs @@ -1,30 +1,48 @@ -use smithay::utils::{Logical, Point, Size}; +use std::{borrow::BorrowMut, cell::RefCell}; -use super::SurfaceState; +use smithay::{ + desktop::Window, + utils::{Logical, Point, Size}, +}; pub struct WindowState { pub floating: Float, } pub enum Float { - NotFloating(Option<(Point, Size)>), /// An [Option] of a tuple of the previous location and previous size of the window + Tiled(Option<(Point, Size)>), Floating, } impl WindowState { pub fn new() -> Self { - Self { - floating: Float::NotFloating(None), // TODO: get this from a config file instead of - // | hardcoding - } + Default::default() + } + + /// Access a [Window]'s state + pub fn with_state(window: &Window, mut func: F) -> T + where + F: FnMut(&mut Self) -> T, + { + window + .user_data() + .insert_if_missing(RefCell::::default); + + let mut state = window + .user_data() + .get::>() + .unwrap() + .borrow_mut(); + func(&mut state) } } impl Default for WindowState { fn default() -> Self { - Self::new() // TODO: maybe actual defaults + Self { + // TODO: get this from a config file instead of hardcoding + floating: Float::Tiled(None), + } } } - -impl SurfaceState for WindowState {}