summaryrefslogtreecommitdiff
path: root/src/clients/client.rs
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-06-21 19:13:12 +0100
committertcmal <me@aria.rip>2024-06-21 19:13:12 +0100
commit475253b7bcfd03a932c4b7efd969b3d2bf155035 (patch)
tree44789ce271d14c89cbff777e75f1e9ab6e1a5e64 /src/clients/client.rs
parentc3e98e34ed7d42ef4271339de88f7131e7647442 (diff)
refactor connection-related stuff out, make things a bit cleaner
Diffstat (limited to 'src/clients/client.rs')
-rw-r--r--src/clients/client.rs174
1 files changed, 174 insertions, 0 deletions
diff --git a/src/clients/client.rs b/src/clients/client.rs
new file mode 100644
index 0000000..a7b1a8d
--- /dev/null
+++ b/src/clients/client.rs
@@ -0,0 +1,174 @@
+use xcb::{
+ x::{
+ ChangeProperty, ChangeWindowAttributes, ConfigWindow, ConfigureNotifyEvent,
+ ConfigureWindow, Cw, EventMask, MapWindow, SendEvent, SendEventDest, Window,
+ },
+ Xid,
+};
+
+use crate::{config::BORDER_WIDTH, conn_info::Connection};
+
+use super::hints;
+
+/// Information about a single client / window
+#[derive(Debug)]
+pub struct Client {
+ /// The corresponding X11 window
+ window: Window,
+
+ x: i16,
+ y: i16,
+ width: u16,
+ height: u16,
+ border_width: u16,
+ mapped: bool,
+
+ urgent: bool,
+ never_focus: bool,
+}
+
+impl Client {
+ pub const fn new(window: Window) -> Self {
+ Self {
+ window,
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0,
+ border_width: BORDER_WIDTH,
+ mapped: false,
+ urgent: false,
+ never_focus: false,
+ }
+ }
+
+ /// Send a configure configure notify event with the current geometry.
+ /// This function does not check for success, so `conn.flush()` should be called after.
+ pub fn configure_notify(&self, conn: &Connection<'_>) {
+ conn.send_request(&SendEvent {
+ destination: SendEventDest::Window(self.window),
+ event_mask: EventMask::STRUCTURE_NOTIFY,
+ event: &ConfigureNotifyEvent::new(
+ self.window,
+ self.window,
+ Window::none(),
+ self.x,
+ self.y,
+ self.width,
+ self.height,
+ self.border_width,
+ false,
+ ),
+ propagate: false,
+ });
+ }
+
+ /// Set this client's geometry, also updating the X11 window if needed.
+ /// This function does not check for success, so `conn.flush()` should be called after.
+ pub fn set_geom(
+ &mut self,
+ conn: &Connection<'_>,
+ x: i16,
+ y: i16,
+ width: u16,
+ height: u16,
+ border_width: u16,
+ ) {
+ if (x, y, width, height, border_width)
+ == (self.x, self.y, self.width, self.height, self.border_width)
+ {
+ return;
+ }
+
+ self.x = x;
+ self.y = y;
+ self.width = width;
+ self.height = height;
+ self.border_width = border_width;
+
+ conn.send_request(&ConfigureWindow {
+ window: self.window,
+ value_list: &[
+ ConfigWindow::X(self.x.into()),
+ ConfigWindow::Y(self.y.into()),
+ ConfigWindow::Width(self.width.into()),
+ ConfigWindow::Height(self.height.into()),
+ ConfigWindow::BorderWidth(self.border_width.into()),
+ ],
+ });
+ }
+
+ /// Set the border colour of the X11 window to the given value (see `crate::colours::Colours`)
+ /// This function does not check for success, so `conn.flush()` should be called after.
+ pub fn set_border(&self, conn: &Connection<'_>, colour: u32) {
+ conn.send_request(&ChangeWindowAttributes {
+ window: self.window(),
+ value_list: &[Cw::BorderPixel(colour)],
+ });
+ }
+
+ /// Ensure this client is currently mapped / visible
+ /// This function does not check for success, so `conn.flush()` should be called after.
+ pub fn ensure_mapped(&mut self, conn: &Connection<'_>) {
+ if !self.mapped {
+ conn.send_request(&MapWindow {
+ window: self.window,
+ });
+ self.mapped = true;
+ }
+ }
+
+ /// Get the associated window
+ pub const fn window(&self) -> Window {
+ self.window
+ }
+
+ /// Set the event mask for this window
+ /// This function does not check for success, so `conn.flush()` should be called after.
+ pub fn set_event_mask(&self, conn: &Connection<'_>, event_mask: EventMask) {
+ conn.send_request(&ChangeWindowAttributes {
+ window: self.window(),
+ value_list: &[Cw::EventMask(event_mask)],
+ });
+ }
+
+ /// Sync the non-geometry related properties with EWMH hints
+ /// This function does not check for success, so `conn.flush()` should be called after.
+ pub fn sync_properties(&mut self, conn: &Connection<'_>, focused: bool) {
+ let Some(mut hints) = hints::Xwm::get(conn, self.window) else {
+ return;
+ };
+
+ if focused && hints.is_urgent() {
+ hints.set_urgent(false);
+ hints.apply(conn, self.window);
+ } else {
+ self.urgent = hints.is_urgent();
+ }
+
+ self.never_focus = hints.input().is_some_and(|i| !i);
+ }
+
+ /// Set the given window as withdrawn / not withdrawn.
+ pub fn set_withdrawn(&self, conn: &Connection<'_>, withdrawn: bool) {
+ conn.send_request(&ChangeProperty {
+ mode: xcb::x::PropMode::Replace,
+ window: self.window,
+ property: conn.atoms.wm_state,
+ r#type: conn.atoms.wm_state,
+ data: &[u8::from(!withdrawn), 0_u8],
+ });
+ }
+
+ pub fn set_urgent(&mut self, urgent: bool) {
+ self.urgent = urgent;
+ }
+
+ pub const fn border_width(&self) -> u16 {
+ self.border_width
+ }
+
+ pub const fn never_focus(&self) -> bool {
+ self.never_focus
+ }
+}