From 89b5f3f87c8022f2d32c422c3516fca2f1538d69 Mon Sep 17 00:00:00 2001 From: tcmal Date: Tue, 6 Aug 2024 14:39:55 +0100 Subject: Add tags/workspaces --- src/clients/mod.rs | 70 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 23 deletions(-) (limited to 'src/clients/mod.rs') 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 } -- cgit v1.2.3