summaryrefslogtreecommitdiff
path: root/src/clients/mod.rs
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-06 14:39:55 +0100
committertcmal <me@aria.rip>2024-08-13 17:07:19 +0100
commit89b5f3f87c8022f2d32c422c3516fca2f1538d69 (patch)
tree231457b673cafbfdaea32a8f6a9d8c0aba08a7d3 /src/clients/mod.rs
parent5359062bf58a5ff57dfa48492db57f9340ecfbee (diff)
Add tags/workspaces
Diffstat (limited to 'src/clients/mod.rs')
-rw-r--r--src/clients/mod.rs70
1 files changed, 47 insertions, 23 deletions
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
}