summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-06-10 21:47:47 +0100
committertcmal <me@aria.rip>2024-06-10 21:47:47 +0100
commit9db364a195cb5c0e8fb82e52e607d373ddb0e13c (patch)
tree7c962af61460e0a5800ef6fb1f122a1e3ead2c63 /src
parent124e6878b1de561f4bc3fccea768203821c88469 (diff)
set ewmh hints
Diffstat (limited to 'src')
-rw-r--r--src/atoms.rs41
-rw-r--r--src/clients.rs39
-rw-r--r--src/focus.rs12
-rw-r--r--src/main.rs117
4 files changed, 150 insertions, 59 deletions
diff --git a/src/atoms.rs b/src/atoms.rs
index 5db75da..2d23c81 100644
--- a/src/atoms.rs
+++ b/src/atoms.rs
@@ -1,27 +1,18 @@
-use crate::error::*;
-use xcb::Connection;
-
-#[derive(Debug)]
-pub struct InternedAtoms {}
-
-impl InternedAtoms {
- pub fn new_with(_conn: &Connection) -> Result<Self> {
- // TODO: intern atoms
-
- // utf8string = XInternAtom(dpy, "UTF8_STRING", False);
- // wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
- // wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
- // wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
- // wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
- // netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
- // netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
- // netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
- // netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
- // netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
- // netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
- // netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
- // netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
- // netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
- Ok(Self {})
+xcb::atoms_struct! {
+ #[derive(Copy, Clone, Debug)]
+ pub struct Atoms {
+ pub wm_protocols => b"WM_PROTOCOLS",
+ pub wm_delete => b"WM_DELETE_WINDOW",
+ pub wm_state => b"WM_STATE",
+ pub wm_take_focus => b"WM_TAKE_FOCUS",
+ pub net_active_window => b"_NET_ACTIVE_WINDOW",
+ pub net_supported => b"_NET_SUPPORTED",
+ pub net_wm_name => b"_NET_WM_NAME",
+ pub net_wm_state => b"_NET_WM_STATE",
+ pub net_wm_check => b"_NET_SUPPORTING_WM_CHECK",
+ pub net_wm_fullscreen => b"_NET_WM_STATE_FULLSCREEN",
+ pub net_wm_window_type => b"_NET_WM_WINDOW_TYPE",
+ pub net_wm_window_type_dialog => b"_NET_WM_WINDOW_TYPE_DIALOG",
+ pub net_client_list => b"_NET_CLIENT_LIST",
}
}
diff --git a/src/clients.rs b/src/clients.rs
index f04fc36..2892c9d 100644
--- a/src/clients.rs
+++ b/src/clients.rs
@@ -3,9 +3,9 @@ use std::cmp::min;
use crate::{config::BORDER_WIDTH, error::*, WM};
use xcb::{
x::{
- ChangeWindowAttributes, ConfigWindow, ConfigureNotifyEvent, ConfigureRequestEvent,
- ConfigureWindow, Cw, DestroyNotifyEvent, Drawable, EventMask, GetGeometry,
- GetWindowAttributes, MapRequestEvent, MapWindow, SendEvent, SendEventDest,
+ self, ChangeProperty, ChangeWindowAttributes, ConfigWindow, ConfigureNotifyEvent,
+ ConfigureRequestEvent, ConfigureWindow, Cw, DestroyNotifyEvent, Drawable, EventMask,
+ GetGeometry, GetWindowAttributes, MapRequestEvent, MapWindow, SendEvent, SendEventDest,
UnmapNotifyEvent, UnmapWindow, Window,
},
xinerama::{self, ScreenInfo},
@@ -138,7 +138,13 @@ impl WM<'_> {
pub(crate) fn handle_unmap_notify(&mut self, e: UnmapNotifyEvent) -> Result<()> {
if self.clients.find_client_mut(e.window()).is_some() {
if e.is_from_send_event() {
- // TODO: set client state to withdrawn
+ self.conn.send_request(&ChangeProperty {
+ mode: xcb::x::PropMode::Replace,
+ window: e.window(),
+ property: self.atoms.wm_state,
+ r#type: self.atoms.wm_state,
+ data: &[0_u8, 0_u8],
+ });
} else {
self.clients.remove_client(e.window());
self.clients.rearrange(self.conn);
@@ -205,13 +211,32 @@ impl WM<'_> {
| EventMask::STRUCTURE_NOTIFY,
);
// TODO: grabbuttons
- // TODO: add to NetClientList
- // TODO: XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
- // TODO: setclientstate(c, NormalState);
+ // set ewmh
+ self.conn.send_request(&ChangeProperty {
+ mode: xcb::x::PropMode::Append,
+ window: self.root,
+ property: self.atoms.net_client_list,
+ r#type: x::ATOM_WINDOW,
+ data: &[window],
+ });
+ self.set_client_withdrawn(window, false);
+
+ // TODO: XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */
self.refocus(mon, 0);
self.clients.rearrange_monitor(conn, mon);
}
+
+ /// Set the given window as withdrawn / not withdrawn.
+ fn set_client_withdrawn(&self, window: Window, withdrawn: bool) {
+ self.conn.send_request(&ChangeProperty {
+ mode: xcb::x::PropMode::Replace,
+ window,
+ property: self.atoms.wm_state,
+ r#type: self.atoms.wm_state,
+ data: &[!withdrawn as u8, 0_u8],
+ });
+ }
}
/// Holds state related to the window manager's clients
diff --git a/src/focus.rs b/src/focus.rs
index bee148d..b26381e 100644
--- a/src/focus.rs
+++ b/src/focus.rs
@@ -1,6 +1,6 @@
use xcb::x::{
- self, EnterNotifyEvent, FocusInEvent, InputFocus, NotifyDetail, NotifyMode, SetInputFocus,
- Window,
+ self, DeleteProperty, EnterNotifyEvent, FocusInEvent, InputFocus, NotifyDetail, NotifyMode,
+ SetInputFocus, Window,
};
use crate::{error::*, WM};
@@ -46,11 +46,15 @@ impl WM<'_> {
focus: window,
time: x::CURRENT_TIME,
});
- // TODO: XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
+ self.conn.send_request(&DeleteProperty {
+ window,
+ property: self.atoms.net_active_window,
+ });
}
}
/// Refocus on the client with the given co-ordinates, setting X11 properties as required.
+ /// If the given index is invalid, focus on the root instead.
/// This function sends multiple requests without checking them, so `conn.flush()` should be called after.
pub fn refocus(&mut self, mon: usize, i: usize) {
self.unfocus();
@@ -61,6 +65,8 @@ impl WM<'_> {
// TODO: set input focus
// TODO: set active window
// TODO: send wmtakefocus event
+ } else {
+ // TODO: focus on root
}
}
diff --git a/src/main.rs b/src/main.rs
index 6ac9dce..25b4bb9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,13 +1,16 @@
//! A lightweight X11 window manager, inspired by dwm.
-use atoms::InternedAtoms;
+use atoms::Atoms;
use clients::ClientState;
use colours::Colours;
use cursors::Cursors;
use error::*;
use keys::KeyboardInfo;
use xcb::{
- x::{self, ChangeWindowAttributes, PropertyNotifyEvent, Window},
+ x::{
+ self, ChangeProperty, ChangeWindowAttributes, CreateWindow, DeleteProperty,
+ PropertyNotifyEvent, Window, WindowClass,
+ },
Connection, Event, Extension,
};
@@ -44,6 +47,10 @@ struct WM<'a> {
/// The root window
root: Window,
+ /// A window used to prove we're actually EWMH compliant.
+ /// See [the EWMH spec](https://specifications.freedesktop.org/wm-spec/wm-spec-1.3.html#idm46187912047344)
+ check_window: Window,
+
/// WM client state
clients: ClientState,
@@ -54,7 +61,7 @@ struct WM<'a> {
cursors: Cursors,
/// Cached atoms
- atoms: InternedAtoms,
+ atoms: Atoms,
/// Cached keyboard layout information
keyboard_state: KeyboardInfo,
@@ -83,10 +90,11 @@ impl WM<'_> {
Ok(WM {
colours: Colours::new_with(conn, screen.default_colormap())?,
- atoms: InternedAtoms::new_with(conn)?,
+ atoms: Atoms::intern_all(conn)?,
cursors: Cursors::new_with(conn)?,
keyboard_state: KeyboardInfo::new_with(conn)?,
clients: Default::default(),
+ check_window: conn.generate_id(),
conn,
screen_num,
root: screen.root(),
@@ -95,29 +103,90 @@ impl WM<'_> {
/// Set the correct properties on the root window
fn setup_root(&mut self) -> Result<()> {
- // TODO: Set EHWM properties on root window
-
- self.conn
- .check_request(self.conn.send_request_checked(&ChangeWindowAttributes {
- window: self.root,
- value_list: &[
- x::Cw::EventMask(
- x::EventMask::SUBSTRUCTURE_REDIRECT
- | x::EventMask::SUBSTRUCTURE_NOTIFY
- | x::EventMask::BUTTON_PRESS
- | x::EventMask::ENTER_WINDOW
- | x::EventMask::FOCUS_CHANGE
- | x::EventMask::STRUCTURE_NOTIFY
- | x::EventMask::PROPERTY_CHANGE,
- ),
- x::Cw::Cursor(self.cursors.normal()),
- ],
- }))?;
+ // Check window
+ self.conn.send_request(&CreateWindow {
+ wid: self.check_window,
+ parent: self.root,
+ depth: 0,
+ x: 0,
+ y: 0,
+ width: 0,
+ height: 0,
+ border_width: 0,
+ class: WindowClass::InputOutput,
+ visual: 0,
+ value_list: &[],
+ });
+
+ self.conn.send_request(&ChangeProperty {
+ mode: x::PropMode::Replace,
+ window: self.root,
+ property: self.atoms.net_wm_check,
+ r#type: x::ATOM_WINDOW,
+ data: &[self.check_window],
+ });
+
+ self.conn.send_request(&ChangeProperty {
+ mode: x::PropMode::Replace,
+ window: self.check_window,
+ property: self.atoms.net_wm_check,
+ r#type: x::ATOM_WINDOW,
+ data: &[self.check_window],
+ });
+
+ self.conn.send_request(&ChangeProperty {
+ mode: x::PropMode::Replace,
+ window: self.check_window,
+ property: self.atoms.net_wm_name,
+ r#type: x::ATOM_STRING,
+ data: b"blow",
+ });
+
+ // Supported flag
+ self.conn.send_request(&ChangeProperty {
+ mode: x::PropMode::Replace,
+ window: self.root,
+ property: self.atoms.net_supported,
+ r#type: x::ATOM_ATOM,
+ data: &[
+ self.atoms.net_active_window,
+ self.atoms.net_wm_name,
+ self.atoms.net_wm_state,
+ self.atoms.net_wm_check,
+ self.atoms.net_wm_fullscreen,
+ self.atoms.net_wm_window_type,
+ self.atoms.net_wm_window_type_dialog,
+ self.atoms.net_client_list,
+ ],
+ });
+
+ // Cleanup state
+ self.conn.send_request(&DeleteProperty {
+ window: self.root,
+ property: self.atoms.net_client_list,
+ });
+
+ // Get the right events
+ self.conn.send_request(&ChangeWindowAttributes {
+ window: self.root,
+ value_list: &[
+ x::Cw::EventMask(
+ x::EventMask::SUBSTRUCTURE_REDIRECT
+ | x::EventMask::SUBSTRUCTURE_NOTIFY
+ | x::EventMask::BUTTON_PRESS
+ | x::EventMask::ENTER_WINDOW
+ | x::EventMask::FOCUS_CHANGE
+ | x::EventMask::STRUCTURE_NOTIFY
+ | x::EventMask::PROPERTY_CHANGE,
+ ),
+ x::Cw::Cursor(self.cursors.normal()),
+ ],
+ });
self.grab_keys()?;
- // TODO: reset focus
- self.unfocus();
+ self.refocus(usize::MAX, usize::MAX);
+ self.conn.flush()?;
Ok(())
}