diff options
author | tcmal <me@aria.rip> | 2024-08-06 14:39:55 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-13 17:07:19 +0100 |
commit | 89b5f3f87c8022f2d32c422c3516fca2f1538d69 (patch) | |
tree | 231457b673cafbfdaea32a8f6a9d8c0aba08a7d3 /src/clients | |
parent | 5359062bf58a5ff57dfa48492db57f9340ecfbee (diff) |
Add tags/workspaces
Diffstat (limited to 'src/clients')
-rw-r--r-- | src/clients/client.rs | 19 | ||||
-rw-r--r-- | src/clients/mod.rs | 70 | ||||
-rw-r--r-- | src/clients/monitors.rs | 39 |
3 files changed, 99 insertions, 29 deletions
diff --git a/src/clients/client.rs b/src/clients/client.rs index 6ab664b..c8fca6c 100644 --- a/src/clients/client.rs +++ b/src/clients/client.rs @@ -2,14 +2,14 @@ use xcb::{ x::{ self, ChangeProperty, ChangeWindowAttributes, ConfigWindow, ConfigWindowMask, ConfigureNotifyEvent, ConfigureWindow, Cw, EventMask, MapWindow, PropMode, SendEvent, - SendEventDest, StackMode, Window, + SendEventDest, StackMode, UnmapWindow, Window, }, VoidCookieChecked, Xid, }; use crate::{config::BORDER_WIDTH, conn_info::Connection}; -use super::{hints, MonitorGeometry}; +use super::{hints, MonitorGeometry, Tag}; /// Information about a single client / window #[derive(Debug)] @@ -28,6 +28,8 @@ pub struct Client { urgent: bool, never_focus: bool, layout_mode: LayoutMode, + + pub tag: Tag, } /// How this window's geometry is determined, @@ -44,7 +46,7 @@ pub enum LayoutMode { } impl Client { - pub const fn new(window: Window) -> Self { + pub const fn new(window: Window, tag: Tag) -> Self { Self { window, x: 0, @@ -56,6 +58,7 @@ impl Client { urgent: false, never_focus: false, layout_mode: LayoutMode::Tiled, + tag, } } @@ -226,6 +229,16 @@ impl Client { }); } + /// Ensure this client is currently unmapped + pub fn ensure_unmapped(&mut self, conn: &Connection<'_>) { + if self.mapped { + conn.send_request(&UnmapWindow { + window: self.window, + }); + self.mapped = false; + } + } + /// Ensure this client is currently mapped / visible pub fn ensure_mapped(&mut self, conn: &Connection<'_>) { if !self.mapped { diff --git a/src/clients/mod.rs b/src/clients/mod.rs index 7fdb739..1abab20 100644 --- a/src/clients/mod.rs +++ b/src/clients/mod.rs @@ -5,6 +5,7 @@ use std::cmp::{min, Ordering}; use crate::{ config::BORDER_WIDTH, conn_info::Connection, + debug, error::{Error, Result}, WM, }; @@ -15,7 +16,7 @@ use xcb::{ EventMask, GetGeometry, GetWindowAttributes, InputFocus, MapRequestEvent, PropMode, SetInputFocus, UnmapNotifyEvent, Window, }, - xinerama, BaseEvent, Extension, + xinerama, Extension, }; pub use client::*; @@ -33,6 +34,9 @@ mod monitors; mod tile; +/// The tag a client has, similar to a workspace in most WMs. +pub type Tag = u8; + impl WM<'_> { /// Perform configure requests if we're happy with them, or they're for an unmanaged window. pub(crate) fn handle_configure_request(&mut self, e: &ConfigureRequestEvent) -> Result<()> { @@ -83,7 +87,7 @@ impl WM<'_> { /// Removing destroyed windows from the client list and rearrange. pub(crate) fn handle_destroy_notify(&mut self, e: &DestroyNotifyEvent) { - self.clients.unmanage(&self.conn, e.window(), true); + self.clients.unmanage(&self.conn, e.window()); } /// Map a window, starting to manage it if needed. @@ -108,16 +112,12 @@ impl WM<'_> { Ok(()) } - /// When a window is unmapped, either stop managing it or update its state. + /// When a window is unmapped, update its state. pub(crate) fn handle_unmap_notify(&mut self, e: &UnmapNotifyEvent) { if let Some(c) = self.clients.find_client_mut(e.window()) { - if e.is_from_send_event() { - let cookie = c.set_withdrawn(&self.conn, true); - // The above may fail if the window has already been destroyed - just discard the error here. - let _ = self.conn.check_request(cookie); - } else { - self.clients.unmanage(&self.conn, e.window(), false); - } + let cookie = c.set_withdrawn(&self.conn, true); + // The above may fail if the window has already been destroyed - just discard the error here. + let _ = self.conn.check_request(cookie); } } } @@ -204,7 +204,8 @@ impl ClientState { // We're about to invalidate focus position self.unfocus(conn); // TODO: inserting at index 0 is why dwm uses linked lists, maybe this can be improved - self.mons[mon].clients.insert(0, Client::new(window)); + let tag = self.mons[mon].focused_tag.create_tag(); + self.mons[mon].clients.insert(0, Client::new(window, tag)); let mon_geom @ MonitorGeometry { width: mon_width, @@ -257,21 +258,13 @@ impl ClientState { self.rearrange_monitor(conn, mon); } - /// Stop managing the given window, and also unset attributes unless `already_destroyed` is true. - pub fn unmanage(&mut self, conn: &Connection<'_>, window: Window, already_destroyed: bool) { + /// Stop managing the given window + pub fn unmanage(&mut self, conn: &Connection<'_>, window: Window) { let Some((mon, i)) = self.find_client_pos(window) else { return; }; - let c = self.mons[mon].clients.remove(i); - - if !already_destroyed { - c.set_event_mask(conn, EventMask::NO_EVENT); - // TODO: Ungrab button - let cookie = c.set_withdrawn(conn, true); + self.mons[mon].clients.remove(i); - // If any of the above requests fail, it's just a race condition and the window is already destroyed, so discard the error here. - let _ = conn.check_request(cookie); - } self.rearrange(conn); } @@ -366,6 +359,37 @@ impl ClientState { self.rearrange_monitor(conn, mon); } + /// Set the focused tag for the given monitor + pub fn set_mon_tag_focus(&mut self, conn: &Connection<'_>, mon: usize, tag_focus: TagFocus) { + // Hide windows from currently focused tag filter + let curr_focus = self.mons[mon].focused_tag; + self.mons[mon] + .clients + .iter_mut() + .filter(|c| curr_focus.matches(c.tag)) + .for_each(|c| c.ensure_unmapped(conn)); + + debug!("setting tag focus to {:?} on mon {}", tag_focus, mon); + self.mons[mon].focused_tag = tag_focus; + self.unfocus(conn); + self.rearrange_monitor(conn, mon); + } + + /// Set the tag for the given client + pub fn set_client_tag(&mut self, conn: &Connection<'_>, (mon, pos): (usize, usize), tag: Tag) { + if let Some(c) = self.client_mut(mon, pos) { + if c.tag == tag { + return; + } + + debug!("moving client with window {:?} to tag {}", c.window(), tag); + c.tag = tag; + c.ensure_unmapped(conn); + self.unfocus(conn); + self.rearrange_monitor(conn, mon); + } + } + /// Get the amount of monitors this state is currently aware of pub fn monitor_count(&self) -> usize { self.mons.len() @@ -498,7 +522,7 @@ impl ClientState { } /// Get the currently focused monitor - const fn focused_mon(&self) -> usize { + pub const fn focused_mon(&self) -> usize { self.focused.0 } diff --git a/src/clients/monitors.rs b/src/clients/monitors.rs index 6790632..16348ea 100644 --- a/src/clients/monitors.rs +++ b/src/clients/monitors.rs @@ -1,6 +1,6 @@ use xcb::xinerama::ScreenInfo; -use super::Client; +use super::{Client, Tag}; /// Info stored for each monitor #[derive(Debug)] @@ -8,15 +8,47 @@ pub struct MonitorInfo { /// Clients attached to that monitor pub clients: Vec<Client>, + /// How clients should be filtered by tag + pub focused_tag: TagFocus, + /// The monitor's geometry pub screen_info: MonitorGeometry, } +/// How clients are currently being filtered by tag +#[derive(Debug, Clone, Copy)] +pub enum TagFocus { + /// Only show clients with the given tag + Tag(Tag), + + /// Show all clients + All, +} +impl TagFocus { + /// Check if a client with the given tag should be displayed when using this filter + pub const fn matches(&self, tag: Tag) -> bool { + match self { + Self::Tag(x) => *x == tag, + Self::All => true, + } + } + + /// Get the tag that new clients should be assigned when using this filter + pub const fn create_tag(&self) -> Tag { + match self { + Self::Tag(x) => *x, + Self::All => 1, + } + } +} + impl MonitorInfo { /// Iterate over all tiled clients, returning a mutable reference to each. pub fn clients_tiled_mut(&mut self) -> impl Iterator<Item = &mut Client> { - // TODO: tag filtering - self.clients.iter_mut().filter(|c| c.tiled()) + self.clients + .iter_mut() + .filter(|c| c.tiled()) + .filter(|c| self.focused_tag.matches(c.tag)) } } @@ -30,6 +62,7 @@ impl Default for MonitorInfo { width: 0, height: 0, }, + focused_tag: TagFocus::Tag(1), } } } |