summaryrefslogtreecommitdiff
path: root/src/clients
diff options
context:
space:
mode:
Diffstat (limited to 'src/clients')
-rw-r--r--src/clients/client.rs19
-rw-r--r--src/clients/mod.rs70
-rw-r--r--src/clients/monitors.rs39
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),
}
}
}