summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/clients/client.rs65
-rw-r--r--src/clients/mod.rs19
-rw-r--r--src/main.rs49
-rw-r--r--todos.org6
4 files changed, 109 insertions, 30 deletions
diff --git a/src/clients/client.rs b/src/clients/client.rs
index c9fa645..43d4c54 100644
--- a/src/clients/client.rs
+++ b/src/clients/client.rs
@@ -1,7 +1,8 @@
use xcb::{
x::{
- ChangeProperty, ChangeWindowAttributes, ConfigWindow, ConfigureNotifyEvent,
- ConfigureWindow, Cw, EventMask, MapWindow, SendEvent, SendEventDest, StackMode, Window,
+ self, ChangeProperty, ChangeWindowAttributes, ConfigWindow, ConfigureNotifyEvent,
+ ConfigureWindow, Cw, EventMask, MapWindow, PropMode, SendEvent, SendEventDest, StackMode,
+ Window,
},
VoidCookieChecked, Xid,
};
@@ -125,25 +126,40 @@ impl Client {
matches!(self.layout_mode, LayoutMode::Tiled)
}
+ /// Whether the client is fullscreen
+ pub const fn fullscreen(&self) -> bool {
+ matches!(self.layout_mode, LayoutMode::Fullscreen)
+ }
+
/// Set the window as tiled
- pub fn set_tiled(&mut self) {
+ pub fn set_tiled(&mut self, conn: &Connection<'_>) {
+ let dirty = !matches!(self.layout_mode, LayoutMode::Tiled);
self.layout_mode = LayoutMode::Tiled;
+ if dirty {
+ self.apply_net_wm_state(conn)
+ }
}
/// Set the window as floating
pub fn set_floating(&mut self, conn: &Connection<'_>) {
- if !matches!(self.layout_mode, LayoutMode::Floating) {
+ let dirty = !matches!(self.layout_mode, LayoutMode::Floating);
+ self.layout_mode = LayoutMode::Floating;
+
+ if dirty {
conn.send_request(&ConfigureWindow {
window: self.window,
value_list: &[ConfigWindow::StackMode(StackMode::Above)],
});
+ self.apply_net_wm_state(conn);
}
- self.layout_mode = LayoutMode::Floating;
}
/// Set the window as fullscreen
pub fn set_fullscreen(&mut self, conn: &Connection<'_>, mon_geom: &MonitorGeometry) {
- if !matches!(self.layout_mode, LayoutMode::Fullscreen) {
+ let dirty = !matches!(self.layout_mode, LayoutMode::Fullscreen);
+ self.layout_mode = LayoutMode::Fullscreen;
+
+ if dirty {
conn.send_request(&ConfigureWindow {
window: self.window,
value_list: &[ConfigWindow::StackMode(StackMode::Above)],
@@ -157,8 +173,22 @@ impl Client {
mon_geom.height,
self.border_width,
);
+ self.apply_net_wm_state(conn);
}
- self.layout_mode = LayoutMode::Fullscreen;
+ }
+
+ fn apply_net_wm_state(&self, conn: &Connection<'_>) {
+ conn.send_request(&ChangeProperty {
+ mode: PropMode::Replace,
+ window: self.window,
+ property: conn.atoms.net_wm_state,
+ r#type: x::ATOM_ATOM,
+ data: &[match self.layout_mode {
+ LayoutMode::Tiled => 0_u32,
+ LayoutMode::Floating => 0_u32,
+ LayoutMode::Fullscreen => conn.atoms.net_wm_fullscreen.resource_id(),
+ }],
+ });
}
/// Ensure this client is currently mapped / visible
@@ -188,8 +218,8 @@ impl Client {
})
}
- /// Sync the non-geometry related properties with EWMH hints
- pub fn sync_properties(&mut self, conn: &Connection<'_>, focused: bool) {
+ /// Sync the non-geometry related hints with EWMH hints
+ pub fn sync_hints(&mut self, conn: &Connection<'_>, focused: bool) {
let Some(mut hints) = hints::Ewm::get(conn, self.window) else {
return;
};
@@ -204,6 +234,15 @@ impl Client {
self.never_focus = hints.input().is_some_and(|i| !i);
}
+ /// Apply any geometry-related EWM hints on the window
+ pub fn apply_geometry_hints(&mut self, conn: &Connection<'_>, mon_geom: &MonitorGeometry) {
+ if hints::is_fullscreen(conn, self.window) {
+ self.set_fullscreen(conn, mon_geom);
+ } else if hints::is_dialog(conn, self.window) {
+ self.set_floating(conn);
+ }
+ }
+
/// Set the given window as withdrawn / not withdrawn.
pub fn set_withdrawn(&self, conn: &Connection<'_>, withdrawn: bool) -> VoidCookieChecked {
conn.send_request_checked(&ChangeProperty {
@@ -215,14 +254,6 @@ impl Client {
})
}
- pub fn update_window_type(&mut self, conn: &Connection<'_>, mon_geom: &MonitorGeometry) {
- if hints::is_fullscreen(conn, self.window) {
- self.set_fullscreen(conn, mon_geom);
- } else if hints::is_dialog(conn, self.window) {
- self.set_floating(conn);
- }
- }
-
pub fn set_urgent(&mut self, urgent: bool) {
self.urgent = urgent;
}
diff --git a/src/clients/mod.rs b/src/clients/mod.rs
index 053b4de..597a336 100644
--- a/src/clients/mod.rs
+++ b/src/clients/mod.rs
@@ -210,13 +210,15 @@ impl ClientState {
BORDER_WIDTH,
);
c.set_border(conn, conn.colours.border_normal());
+
if floating {
c.set_floating(conn);
}
- c.update_window_type(conn, &mon_geom);
- c.ensure_mapped(conn);
- c.sync_properties(conn, true);
+ c.sync_hints(conn, true);
+ c.apply_geometry_hints(conn, &mon_geom);
+
+ c.ensure_mapped(conn);
c.set_event_mask(
conn,
@@ -266,7 +268,7 @@ impl ClientState {
self.unfocus(conn);
if let Some(new) = self.set_focused(mon, i) {
new.set_border(conn, conn.colours.border_focused());
- new.sync_properties(conn, true);
+ new.sync_hints(conn, true);
if !new.never_focus() {
conn.send_request(&SetInputFocus {
revert_to: InputFocus::PointerRoot,
@@ -332,7 +334,7 @@ impl ClientState {
if c.tiled() {
c.set_floating(conn);
} else {
- c.set_tiled();
+ c.set_tiled(conn);
}
self.rearrange_monitor(conn, mon);
@@ -345,7 +347,7 @@ impl ClientState {
if c.tiled() {
c.set_fullscreen(conn, &mon_info.screen_info);
} else {
- c.set_tiled();
+ c.set_tiled(conn);
}
self.rearrange_monitor(conn, mon);
@@ -482,6 +484,11 @@ impl ClientState {
const fn focused_mon(&self) -> usize {
self.focused.0
}
+
+ /// Get the geometry of the monitor with the given index
+ pub fn mon_geometry(&self, mon: usize) -> MonitorGeometry {
+ self.mons[mon].screen_info
+ }
}
impl Default for ClientState {
diff --git a/src/main.rs b/src/main.rs
index 7c6c7bd..122f735 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -29,8 +29,8 @@ use nix::{
unistd::Pid,
};
use xcb::{
- x::{self, PropertyNotifyEvent},
- Connection as RawConnection, Event, Extension,
+ x::{self, ClientMessageEvent, PropertyNotifyEvent},
+ Connection as RawConnection, Event, Extension, Xid,
};
pub mod clients;
@@ -101,12 +101,13 @@ impl<'a> WM<'a> {
Event::X(x::Event::MapRequest(e)) => self.handle_map_request(&e)?,
Event::X(x::Event::UnmapNotify(e)) => self.handle_unmap_notify(&e),
- // // See focus.rs
+ // See focus.rs
Event::X(x::Event::EnterNotify(e)) => self.handle_enter_notify(&e),
Event::X(x::Event::FocusIn(e)) => self.handle_focus_in(&e),
- // // See below
+ // See below
Event::X(x::Event::PropertyNotify(e)) => self.handle_property_notify(&e),
+ Event::X(x::Event::ClientMessage(e)) => self.handle_client_message(&e),
_ => {}
}
}
@@ -127,10 +128,48 @@ impl<'a> WM<'a> {
if x::ATOM_WM_HINTS == e.atom() {
let focused = self.clients.is_focused(e.window());
if let Some(c) = self.clients.find_client_mut(e.window()) {
- c.sync_properties(&self.conn, focused);
+ c.sync_hints(&self.conn, focused);
}
}
}
+
+ /// Handle some common client requests set out by the EWMH spec
+ fn handle_client_message(&mut self, e: &ClientMessageEvent) {
+ let Some((mon, pos)) = self.clients.find_client_pos(e.window()) else {
+ return;
+ };
+
+ if e.format() != 32 {
+ return;
+ }
+
+ if dbg!(e.r#type()) == dbg!(self.conn.atoms.net_wm_state) {
+ let x::ClientMessageData::Data32(data) = e.data() else {
+ unreachable!();
+ };
+
+ if !(data[1] == self.conn.atoms.net_wm_fullscreen.resource_id()
+ || data[2] == self.conn.atoms.net_wm_fullscreen.resource_id())
+ {
+ return;
+ }
+
+ let mon_geom = self.clients.mon_geometry(mon);
+ let c = self.clients.client_mut(mon, pos).unwrap();
+ let fullscreen = match data[0] {
+ 1 => true,
+ 2 => !c.fullscreen(),
+ _ => false,
+ };
+
+ if fullscreen {
+ c.set_fullscreen(&self.conn, &mon_geom);
+ } else {
+ c.set_tiled(&self.conn);
+ }
+ self.clients.rearrange(&self.conn);
+ }
+ }
}
/// Cleanup this process' children and set some flags.
diff --git a/todos.org b/todos.org
index 7bb97ab..ad46a35 100644
--- a/todos.org
+++ b/todos.org
@@ -1,5 +1,3 @@
-* Set fullscreen based on hint
-
* Fix focus changing bug
* Deal with configurerequest better
@@ -27,3 +25,7 @@
* Don't use Vecs?
* Figure out better configuration method?
+
+* Look at refactoring out kind of scuffed client access patterns
+
+* Read rest of EWMH spec and see what is missing