diff --git a/api/lua/test_config.lua b/api/lua/test_config.lua index bb359e9..b6ff096 100644 --- a/api/lua/test_config.lua +++ b/api/lua/test_config.lua @@ -76,63 +76,63 @@ require("pinnacle").setup(function(pinnacle) end print("----------------------") - - local op = output.get_focused() --[[@as Output]] - print("res: " .. (op:res() and (op:res().w .. ", " .. op:res().h) or "nil")) - print("loc: " .. (op:loc() and (op:loc().x .. ", " .. op:loc().y) or "nil")) - print("rr: " .. (op:refresh_rate() or "nil")) - print("make: " .. (op:make() or "nil")) - print("model: " .. (op:model() or "nil")) - print("focused: " .. (tostring(op:focused()))) - - print("----------------------") - - local wins = window.get_by_class("Alacritty") - for _, win in pairs(wins) do - print("loc: " .. (win:loc() and win:loc().x or "nil") .. ", " .. (win:loc() and win:loc().y or "nil")) - print("size: " .. (win:size() and win:size().w or "nil") .. ", " .. (win:size() and win:size().h or "nil")) - print("class: " .. (win:class() or "nil")) - print("title: " .. (win:title() or "nil")) - print("float: " .. tostring(win:floating())) - end - - print("----------------------") - - local wins = window.get_by_title("~/p/pinnacle") - for _, win in pairs(wins) do - print("loc: " .. (win:loc() and win:loc().x or "nil") .. ", " .. (win:loc() and win:loc().y or "nil")) - print("size: " .. (win:size() and win:size().w or "nil") .. ", " .. (win:size() and win:size().h or "nil")) - print("class: " .. (win:class() or "nil")) - print("title: " .. (win:title() or "nil")) - print("float: " .. tostring(win:floating())) - end - - print("----------------------") - - local tags = tag.get_on_output(output.get_focused() --[[@as Output]]) - for _, tg in pairs(tags) do - print(tg:name()) - print((tg:output() and tg:output():name()) or "nil output") - print(tg:active()) - end - - print("----------------------") - - local tags = tag.get_by_name("2") - for _, tg in pairs(tags) do - print(tg:name()) - print((tg:output() and tg:output():name()) or "nil output") - print(tg:active()) - end - - print("----------------------") - - local tags = tag.get_all() - for _, tg in pairs(tags) do - print(tg:name()) - print((tg:output() and tg:output():name()) or "nil output") - print(tg:active()) - end + -- + -- local op = output.get_focused() --[[@as Output]] + -- print("res: " .. (op:res() and (op:res().w .. ", " .. op:res().h) or "nil")) + -- print("loc: " .. (op:loc() and (op:loc().x .. ", " .. op:loc().y) or "nil")) + -- print("rr: " .. (op:refresh_rate() or "nil")) + -- print("make: " .. (op:make() or "nil")) + -- print("model: " .. (op:model() or "nil")) + -- print("focused: " .. (tostring(op:focused()))) + -- + -- print("----------------------") + -- + -- local wins = window.get_by_class("Alacritty") + -- for _, win in pairs(wins) do + -- print("loc: " .. (win:loc() and win:loc().x or "nil") .. ", " .. (win:loc() and win:loc().y or "nil")) + -- print("size: " .. (win:size() and win:size().w or "nil") .. ", " .. (win:size() and win:size().h or "nil")) + -- print("class: " .. (win:class() or "nil")) + -- print("title: " .. (win:title() or "nil")) + -- print("float: " .. tostring(win:floating())) + -- end + -- + -- print("----------------------") + -- + -- local wins = window.get_by_title("~/p/pinnacle") + -- for _, win in pairs(wins) do + -- print("loc: " .. (win:loc() and win:loc().x or "nil") .. ", " .. (win:loc() and win:loc().y or "nil")) + -- print("size: " .. (win:size() and win:size().w or "nil") .. ", " .. (win:size() and win:size().h or "nil")) + -- print("class: " .. (win:class() or "nil")) + -- print("title: " .. (win:title() or "nil")) + -- print("float: " .. tostring(win:floating())) + -- end + -- + -- print("----------------------") + -- + -- local tags = tag.get_on_output(output.get_focused() --[[@as Output]]) + -- for _, tg in pairs(tags) do + -- print(tg:name()) + -- print((tg:output() and tg:output():name()) or "nil output") + -- print(tg:active()) + -- end + -- + -- print("----------------------") + -- + -- local tags = tag.get_by_name("2") + -- for _, tg in pairs(tags) do + -- print(tg:name()) + -- print((tg:output() and tg:output():name()) or "nil output") + -- print(tg:active()) + -- end + -- + -- print("----------------------") + -- + -- local tags = tag.get_all() + -- for _, tg in pairs(tags) do + -- print(tg:name()) + -- print((tg:output() and tg:output():name()) or "nil output") + -- print(tg:active()) + -- end end) -- Tags --------------------------------------------------------------------------- @@ -211,146 +211,67 @@ require("pinnacle").setup(function(pinnacle) end) input.keybind({ mod_key }, keys.KEY_1, function() - for _, t in pairs(tag.get_by_name("1")) do - if t:output() and t:output():focused() then - t:switch_to() - end - end + tag.switch_to("1") end) input.keybind({ mod_key }, keys.KEY_2, function() - for _, t in pairs(tag.get_by_name("2")) do - if t:output() and t:output():focused() then - t:switch_to() - end - end + tag.switch_to("2") end) input.keybind({ mod_key }, keys.KEY_3, function() - for _, t in pairs(tag.get_by_name("3")) do - if t:output() and t:output():focused() then - t:switch_to() - end - end + tag.switch_to("3") end) input.keybind({ mod_key }, keys.KEY_4, function() - for _, t in pairs(tag.get_by_name("4")) do - if t:output() and t:output():focused() then - t:switch_to() - end - end + tag.switch_to("4") end) input.keybind({ mod_key }, keys.KEY_5, function() - for _, t in pairs(tag.get_by_name("5")) do - if t:output() and t:output():focused() then - t:switch_to() - end - end + tag.switch_to("5") end) input.keybind({ mod_key, "Shift" }, keys.KEY_1, function() - for _, t in pairs(tag.get_by_name("1")) do - if t:output() and t:output():focused() then - t:toggle() - end - end + tag.toggle("1") end) input.keybind({ mod_key, "Shift" }, keys.KEY_2, function() - for _, t in pairs(tag.get_by_name("2")) do - if t:output() and t:output():focused() then - t:toggle() - end - end + tag.toggle("2") end) input.keybind({ mod_key, "Shift" }, keys.KEY_3, function() - for _, t in pairs(tag.get_by_name("3")) do - if t:output() and t:output():focused() then - t:toggle() - end - end + tag.toggle("3") end) input.keybind({ mod_key, "Shift" }, keys.KEY_4, function() - for _, t in pairs(tag.get_by_name("4")) do - if t:output() and t:output():focused() then - t:toggle() - end - end + tag.toggle("4") end) input.keybind({ mod_key, "Shift" }, keys.KEY_5, function() - for _, t in pairs(tag.get_by_name("5")) do - if t:output() and t:output():focused() then - t:toggle() - end - end + tag.toggle("5") end) + -- I check for nil this way because I don't want stylua to take up like 80 lines on `if win ~= nil` input.keybind({ mod_key, "Alt" }, keys.KEY_1, function() - for _, t in pairs(tag.get_by_name("1")) do - if t:output() and t:output():focused() then - window.get_focused():move_to_tag(t) - end - end + local _ = window.get_focused() and window:get_focused():move_to_tag("1") end) input.keybind({ mod_key, "Alt" }, keys.KEY_2, function() - for _, t in pairs(tag.get_by_name("2")) do - if t:output() and t:output():focused() then - window.get_focused():move_to_tag(t) - end - end + local _ = window.get_focused() and window:get_focused():move_to_tag("2") end) input.keybind({ mod_key, "Alt" }, keys.KEY_3, function() - for _, t in pairs(tag.get_by_name("3")) do - if t:output() and t:output():focused() then - window.get_focused():move_to_tag(t) - end - end + local _ = window.get_focused() and window:get_focused():move_to_tag("3") end) input.keybind({ mod_key, "Alt" }, keys.KEY_4, function() - for _, t in pairs(tag.get_by_name("4")) do - if t:output() and t:output():focused() then - window.get_focused():move_to_tag(t) - end - end + local _ = window.get_focused() and window:get_focused():move_to_tag("4") end) input.keybind({ mod_key, "Alt" }, keys.KEY_5, function() - for _, t in pairs(tag.get_by_name("5")) do - if t:output() and t:output():focused() then - window.get_focused():move_to_tag(t) - end - end + local _ = window.get_focused() and window:get_focused():move_to_tag("5") end) input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_1, function() - for _, t in pairs(tag.get_by_name("1")) do - if t:output() and t:output():focused() then - window.get_focused():toggle_tag(t) - end - end + local _ = window.get_focused() and window.get_focused():toggle_tag("1") end) input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_2, function() - for _, t in pairs(tag.get_by_name("2")) do - if t:output() and t:output():focused() then - window.get_focused():toggle_tag(t) - end - end + local _ = window.get_focused() and window.get_focused():toggle_tag("2") end) input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_3, function() - for _, t in pairs(tag.get_by_name("3")) do - if t:output() and t:output():focused() then - window.get_focused():toggle_tag(t) - end - end + local _ = window.get_focused() and window.get_focused():toggle_tag("3") end) input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_4, function() - for _, t in pairs(tag.get_by_name("4")) do - if t:output() and t:output():focused() then - window.get_focused():toggle_tag(t) - end - end + local _ = window.get_focused() and window.get_focused():toggle_tag("4") end) input.keybind({ mod_key, "Shift", "Alt" }, keys.KEY_5, function() - for _, t in pairs(tag.get_by_name("5")) do - if t:output() and t:output():focused() then - window.get_focused():toggle_tag(t) - end - end + local _ = window.get_focused() and window.get_focused():toggle_tag("5") end) end) diff --git a/src/handlers/xwayland.rs b/src/handlers/xwayland.rs index 3704ba7..9474935 100644 --- a/src/handlers/xwayland.rs +++ b/src/handlers/xwayland.rs @@ -8,9 +8,12 @@ use smithay::{ desktop::space::SpaceElement, input::pointer::Focus, reexports::wayland_server::Resource, - utils::{Rectangle, SERIAL_COUNTER}, + utils::{Logical, Rectangle, SERIAL_COUNTER}, wayland::compositor::{self, CompositorHandler}, - xwayland::{xwm::XwmId, X11Wm, XwmHandler}, + xwayland::{ + xwm::{Reorder, XwmId}, + X11Surface, X11Wm, XwmHandler, + }, }; use crate::{ @@ -25,11 +28,11 @@ impl XwmHandler for CalloopData { self.state.xwm.as_mut().expect("xwm not in state") } - fn new_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {} + fn new_window(&mut self, xwm: XwmId, window: X11Surface) {} - fn new_override_redirect_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {} + fn new_override_redirect_window(&mut self, xwm: XwmId, window: X11Surface) {} - fn map_window_request(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) { + fn map_window_request(&mut self, xwm: XwmId, window: X11Surface) { tracing::debug!("new x11 window from map_window_request"); window.set_mapped(true).expect("failed to map x11 window"); let window = WindowElement::X11(window); @@ -149,17 +152,14 @@ impl XwmHandler for CalloopData { } } - fn mapped_override_redirect_window( - &mut self, - xwm: XwmId, - window: smithay::xwayland::X11Surface, - ) { + fn mapped_override_redirect_window(&mut self, xwm: XwmId, window: X11Surface) { let loc = window.geometry().loc; let window = WindowElement::X11(window); self.state.space.map_element(window, loc, true); } - fn unmapped_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) { + fn unmapped_window(&mut self, xwm: XwmId, window: X11Surface) { + tracing::debug!("unmapped x11 window"); let win = self .state .space @@ -168,23 +168,50 @@ impl XwmHandler for CalloopData { .cloned(); if let Some(win) = win { self.state.space.unmap_elem(&win); + // self.state.windows.retain(|elem| &win != elem); + if win.with_state(|state| state.floating.is_tiled()) { + if let Some(output) = win.output(&self.state) { + self.state.re_layout(&output); + } + } } if !window.is_override_redirect() { window.set_mapped(false).expect("failed to unmap x11 win"); } } - fn destroyed_window(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface) {} + fn destroyed_window(&mut self, xwm: XwmId, window: X11Surface) { + let win = self + .state + .windows + .iter() + .find(|elem| { + matches!(elem, + WindowElement::X11(surface) if surface.wl_surface() == window.wl_surface()) + }) + .cloned(); + tracing::debug!("{win:?}"); + if let Some(win) = win { + tracing::debug!("removing x11 window from windows"); + self.state.windows.retain(|elem| win.wl_surface() != elem.wl_surface()); + if win.with_state(|state| state.floating.is_tiled()) { + if let Some(output) = win.output(&self.state) { + self.state.re_layout(&output); + } + } + } + tracing::debug!("destroyed x11 window"); + } fn configure_request( &mut self, xwm: XwmId, - window: smithay::xwayland::X11Surface, + window: X11Surface, x: Option, y: Option, w: Option, h: Option, - reorder: Option, + reorder: Option, ) { let mut geo = window.geometry(); if let Some(w) = w { @@ -201,8 +228,8 @@ impl XwmHandler for CalloopData { fn configure_notify( &mut self, xwm: XwmId, - window: smithay::xwayland::X11Surface, - geometry: smithay::utils::Rectangle, + window: X11Surface, + geometry: Rectangle, above: Option, ) { let Some(win) = self @@ -229,7 +256,7 @@ impl XwmHandler for CalloopData { fn resize_request( &mut self, xwm: XwmId, - window: smithay::xwayland::X11Surface, + window: X11Surface, button: u32, resize_edge: smithay::xwayland::xwm::ResizeEdge, ) { @@ -283,8 +310,8 @@ impl XwmHandler for CalloopData { } } - fn move_request(&mut self, xwm: XwmId, window: smithay::xwayland::X11Surface, button: u32) { - todo!() + fn move_request(&mut self, xwm: XwmId, window: X11Surface, button: u32) { + // TODO: } // TODO: allow_selection_access diff --git a/src/state.rs b/src/state.rs index 39c3eaf..33aaa1f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -47,7 +47,7 @@ use smithay::{ Display, }, }, - utils::{Clock, Logical, Monotonic, Point, Size}, + utils::{Clock, IsAlive, Logical, Monotonic, Point, Size}, wayland::{ compositor::{self, CompositorClientState, CompositorState}, data_device::DataDeviceState, @@ -305,20 +305,24 @@ impl State { .as_ref() .and_then(|win| self.space.element_location(win)) .map(|loc| (loc.x, loc.y)); - let (class, title) = window.as_ref().and_then(|win| win.wl_surface()).map_or( - (None, None), - |wl_surf| { - compositor::with_states(&wl_surf, |states| { - let lock = states - .data_map - .get::() - .expect("XdgToplevelSurfaceData wasn't in surface's data map") - .lock() - .expect("failed to acquire lock"); - (lock.app_id.clone(), lock.title.clone()) - }) - }, - ); + let (class, title) = window.as_ref().map_or((None, None), |win| match &win { + WindowElement::Wayland(_) => { + if let Some(wl_surf) = win.wl_surface() { + compositor::with_states(&wl_surf, |states| { + let lock = states + .data_map + .get::() + .expect("XdgToplevelSurfaceData wasn't in surface's data map") + .lock() + .expect("failed to acquire lock"); + (lock.app_id.clone(), lock.title.clone()) + }) + } else { + (None, None) + } + } + WindowElement::X11(surface) => (Some(surface.class()), Some(surface.title())), + }); let floating = window .as_ref() .map(|win| win.with_state(|state| state.floating.is_floating())); @@ -619,6 +623,11 @@ impl State { .any(|tag| tag.output(self).is_some_and(|op| &op == output)) }) }) + .filter(|win| match win { + WindowElement::Wayland(win) => !win.with_state(|state| state.minimized), + WindowElement::X11(surf) => !surf.is_minimized(), + }) + .filter(|win| win.alive()) .cloned() .collect::>(); let (render, do_not_render) = output.with_state(|state| { diff --git a/src/window.rs b/src/window.rs index a3a6335..a8dc34a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -40,7 +40,7 @@ use crate::{ state::{State, WithState}, }; -use self::window_state::{Float, WindowResizeState, WindowState}; +use self::window_state::{Float, WindowElementState, WindowResizeState}; pub mod window_state; @@ -202,6 +202,13 @@ impl WindowElement { } } } + + /// Get the output this window is on. + /// + /// This method gets the first tag the window has and returns its output. + pub fn output(&self, state: &State) -> Option { + self.with_state(|st| st.tags.first().and_then(|tag| tag.output(state))) + } } impl IsAlive for WindowElement { @@ -401,7 +408,7 @@ impl SpaceElement for WindowElement { } impl WithState for WindowElement { - type State = WindowState; + type State = WindowElementState; fn with_state(&self, mut func: F) -> T where diff --git a/src/window/window_state.rs b/src/window/window_state.rs index e9b733b..3f68b93 100644 --- a/src/window/window_state.rs +++ b/src/window/window_state.rs @@ -1,11 +1,15 @@ // SPDX-License-Identifier: GPL-3.0-or-later use std::{ + cell::RefCell, fmt, sync::atomic::{AtomicU32, Ordering}, }; -use smithay::utils::{Logical, Point, Serial, Size}; +use smithay::{ + desktop::Window, + utils::{Logical, Point, Serial, Size}, +}; use crate::{ backend::Backend, @@ -35,7 +39,32 @@ impl WindowId { } } +#[derive(Debug, Default)] pub struct WindowState { + pub minimized: bool, +} + +impl WithState for Window { + type State = WindowState; + + fn with_state(&self, mut func: F) -> T + where + F: FnMut(&mut Self::State) -> T, + { + self.user_data() + .insert_if_missing(RefCell::::default); + + let state = self + .user_data() + .get::>() + .expect("RefCell not in data map"); + + func(&mut state.borrow_mut()) + } +} + +#[derive(Debug)] +pub struct WindowElementState { /// The id of this window. pub id: WindowId, /// Whether the window is floating or tiled. @@ -95,6 +124,7 @@ impl fmt::Debug for WindowResizeState { } } +#[derive(Debug)] pub enum Float { /// The previous location and size of the window when it was floating, if any. Tiled(Option<(Point, Size)>), @@ -119,14 +149,14 @@ impl Float { } } -impl WindowState { +impl WindowElementState { #[allow(dead_code)] pub fn new() -> Self { Default::default() } } -impl Default for WindowState { +impl Default for WindowElementState { fn default() -> Self { Self { // INFO: I think this will assign the id on use of the state, not on window spawn.