diff options
author | tcmal <me@aria.rip> | 2024-06-10 21:47:47 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-06-10 21:47:47 +0100 |
commit | 9db364a195cb5c0e8fb82e52e607d373ddb0e13c (patch) | |
tree | 7c962af61460e0a5800ef6fb1f122a1e3ead2c63 | |
parent | 124e6878b1de561f4bc3fccea768203821c88469 (diff) |
set ewmh hints
-rw-r--r-- | src/atoms.rs | 41 | ||||
-rw-r--r-- | src/clients.rs | 39 | ||||
-rw-r--r-- | src/focus.rs | 12 | ||||
-rw-r--r-- | src/main.rs | 117 |
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(()) } |