From c826556e83b86df400c200f762e1bad3d1117183 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 8 May 2022 02:54:40 +0200 Subject: [PATCH] partial EWMH support now, window type, wmname, clientlist,wmcheck --- Cargo.toml | 3 +- src/backends/window_event.rs | 47 +++++-- src/backends/xlib/mod.rs | 260 ++++++++++++++++++++++++++--------- src/state.rs | 60 ++++---- 4 files changed, 257 insertions(+), 113 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d3d4112..2a9e69c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,5 @@ derivative = "2.2.0" serde = { version = "1.0", features = ["derive"] } toml = "0.5" num-traits = "0.2.14" -strum = {version = "0.24.0", features = ["derive"]} \ No newline at end of file +strum = {version = "0.24.0", features = ["derive"]} +bytemuck = "1.0.0" diff --git a/src/backends/window_event.rs b/src/backends/window_event.rs index 71b7dfd..2c7dfb4 100644 --- a/src/backends/window_event.rs +++ b/src/backends/window_event.rs @@ -1,10 +1,13 @@ #![allow(dead_code)] -use super::keycodes::{KeyOrButton, MouseButton, VirtualKeyCode}; +use super::{ + keycodes::{KeyOrButton, MouseButton, VirtualKeyCode}, + structs::WindowType, +}; use crate::util::{Point, Size}; use bitflags::bitflags; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum WindowEvent { KeyEvent(KeyEvent), ButtonEvent(ButtonEvent), @@ -18,6 +21,7 @@ pub enum WindowEvent { ConfigureEvent(ConfigureEvent), FullscreenEvent(FullscreenEvent), //1 { window: Window, event: 1 }, WindowNameEvent(WindowNameEvent), + WindowTypeChangedEvent(WindowTypeChangedEvent), } #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -107,7 +111,7 @@ impl Into for ModifierKey { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct KeyEvent { pub window: Window, pub state: KeyState, @@ -131,7 +135,7 @@ impl KeyEvent { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ButtonEvent { pub window: Window, pub state: KeyState, @@ -158,7 +162,7 @@ impl ButtonEvent { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MotionEvent { pub position: Point, pub window: Window, @@ -170,22 +174,22 @@ impl MotionEvent { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct MapEvent { pub window: Window, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UnmapEvent { pub window: Window, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EnterEvent { pub window: Window, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DestroyEvent { pub window: Window, } @@ -196,7 +200,7 @@ impl DestroyEvent { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CreateEvent { pub window: Window, pub position: Point, @@ -213,7 +217,7 @@ impl CreateEvent { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ConfigureEvent { pub window: Window, pub position: Point, @@ -230,7 +234,7 @@ impl ConfigureEvent { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum FullscreenState { On, Off, @@ -246,7 +250,7 @@ impl From for FullscreenState { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct FullscreenEvent { pub window: Window, pub state: FullscreenState, @@ -258,7 +262,7 @@ impl FullscreenEvent { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct WindowNameEvent { pub window: Window, pub name: String, @@ -270,6 +274,21 @@ impl WindowNameEvent { } } +#[derive(Debug, Clone)] +pub struct WindowTypeChangedEvent { + pub window: Window, + pub window_type: WindowType, +} + +impl WindowTypeChangedEvent { + pub fn new(window: Window, window_type: WindowType) -> Self { + Self { + window, + window_type, + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct KeyBind { pub key: VirtualKeyCode, diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index a0f2cdd..c3a2876 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -4,14 +4,14 @@ use std::{convert::TryFrom, ptr::NonNull, rc::Rc}; use thiserror::Error; -use x11::xlib::{self, Atom, Success, XEvent, XKeyEvent}; +use x11::xlib::{self, Atom, Success, Window, XEvent, XKeyEvent, XA_WINDOW}; use crate::backends::{ keycodes::KeyOrButton, xlib::keysym::mouse_button_to_xbutton, }; use self::{ - connection::XLibConnection, + connection::{PropMode, XLibConnection}, ewmh::{EWMHAtom, EWMHAtoms}, keysym::{ keysym_to_virtual_keycode, virtual_keycode_to_keysym, @@ -27,6 +27,7 @@ use super::{ ButtonEvent, ConfigureEvent, DestroyEvent, EnterEvent, FullscreenEvent, FullscreenState, KeyEvent, KeyOrMouseBind, KeyState, MapEvent, ModifierState, MotionEvent, UnmapEvent, WindowEvent, WindowNameEvent, + WindowTypeChangedEvent, }, WindowServerBackend, }; @@ -35,7 +36,7 @@ use crate::util::{Point, Size}; pub mod color; pub mod keysym; -pub type XLibWindowEvent = WindowEvent; +pub type XLibWindowEvent = WindowEvent; #[derive(Clone)] pub struct Display(Rc>); @@ -122,6 +123,7 @@ pub mod wmh { WmTakeFocus, WmState, WmTransientFor, + Utf8String, } #[derive(Debug, Clone)] @@ -185,6 +187,7 @@ pub mod wmh { ICCCMAtom::WmTakeFocus => "WM_TAKE_FOCUS", ICCCMAtom::WmState => "WM_STATE", ICCCMAtom::WmTransientFor => "WM_TRANSIENT_FOR", + ICCCMAtom::Utf8String => "UTF8_STRING", } } } @@ -472,12 +475,14 @@ pub mod ewmh { pub mod connection { use std::{ ffi::CString, + mem::size_of, os::raw::{c_char, c_long}, }; + use bytemuck::from_bytes; use x11::xlib::{self, Atom, Window}; - use super::Display; + use super::{xpointer::XPointer, Display}; pub struct XLibConnection { display: Display, @@ -539,6 +544,61 @@ pub mod connection { self.screen } + pub fn get_window_property( + &self, + window: Window, + atom: Atom, + atom_type: Atom, + ) -> Option> { + let mut format_returned = 0; + let mut items_returned = 0; + let mut bytes_after_return = 0; + let mut type_returned = 0; + + let (ptr, success) = + XPointer::::build_with_result(|ptr| unsafe { + xlib::XGetWindowProperty( + self.dpy(), + window, + atom, + 0, + 4096 / 4, + 0, + atom_type, + &mut type_returned, + &mut format_returned, + &mut items_returned, + &mut bytes_after_return, + ptr as *mut _ as *mut _, + ) == i32::from(xlib::Success) + }); + + success.then(|| ptr).flatten().map(|ptr| { + unsafe { + std::slice::from_raw_parts( + ptr.as_ptr(), + items_returned as usize * format_returned as usize, + ) + } + .to_vec() + }) + } + + pub fn get_property_long( + &self, + window: Window, + atom: Atom, + atom_type: Atom, + ) -> Option> { + self.get_window_property(window, atom, atom_type) + .map(|bytes| { + bytes + .chunks(size_of::()) + .map(|bytes| *from_bytes::(bytes)) + .collect::>() + }) + } + pub fn get_text_property( &self, window: Window, @@ -565,8 +625,15 @@ pub mod connection { } } - pub fn change_root_property_byte>( + pub fn delete_property(&self, window: Window, atom: Atom) { + unsafe { + xlib::XDeleteProperty(self.dpy(), window, atom); + } + } + + pub fn change_property_byte>( &self, + window: Window, atom: Atom, atom_type: Atom, mode: PropMode, @@ -575,7 +642,7 @@ pub mod connection { unsafe { xlib::XChangeProperty( self.dpy(), - self.root, + window, atom, atom_type, 8, @@ -586,6 +653,38 @@ pub mod connection { } } + pub fn change_root_property_byte>( + &self, + atom: Atom, + atom_type: Atom, + mode: PropMode, + data: T, + ) { + self.change_property_byte(self.root, atom, atom_type, mode, data) + } + + pub fn change_property_long>( + &self, + window: Window, + atom: Atom, + atom_type: Atom, + mode: PropMode, + data: T, + ) { + unsafe { + xlib::XChangeProperty( + self.dpy(), + window, + atom, + atom_type, + 32, + mode.into(), + data.as_ref().as_ptr().cast::(), + data.as_ref().len() as i32, + ); + } + } + pub fn change_root_property_long>( &self, atom: Atom, @@ -593,18 +692,7 @@ pub mod connection { mode: PropMode, data: T, ) { - unsafe { - xlib::XChangeProperty( - self.dpy(), - self.root, - atom, - atom_type, - 32, - mode.into(), - data.as_ref().as_ptr().cast::(), - data.as_ref().len() as i32, - ); - } + self.change_property_long(self.root, atom, atom_type, mode, data) } } } @@ -627,12 +715,12 @@ impl Display { pub struct XLib { connection: Rc, - //atoms: XLibAtoms, atoms: ICCCMAtoms, ewmh_atoms: EWMHAtoms, keybinds: Vec, active_border_color: Option, inactive_border_color: Option, + wm_window: Window, } impl XLib { @@ -648,6 +736,19 @@ impl XLib { keybinds: Vec::new(), active_border_color: None, inactive_border_color: None, + wm_window: unsafe { + xlib::XCreateSimpleWindow( + con.dpy(), + con.root(), + 0, + 0, + 1, + 1, + 0, + 0, + 0, + ) + }, } } @@ -680,6 +781,34 @@ impl XLib { xlib::XSync(self.dpy(), 0); self.ewmh_atoms.set_supported_atoms(self.connection.clone()); + self.connection.delete_property( + self.connection.root(), + self.ewmh_atoms[EWMHAtom::NetClientList], + ); + + self.connection.change_property_long( + self.wm_window, + self.ewmh_atoms[EWMHAtom::NetSupportingWmCheck], + XA_WINDOW, + PropMode::Replace, + &[self.wm_window as i64], + ); + + self.connection.change_property_long( + self.connection.root(), + self.ewmh_atoms[EWMHAtom::NetSupportingWmCheck], + XA_WINDOW, + PropMode::Replace, + &[self.wm_window as i64], + ); + + self.connection.change_property_byte( + self.wm_window, + self.ewmh_atoms[EWMHAtom::NetWmName], + self.atoms[ICCCMAtom::Utf8String], + PropMode::Replace, + "nirgendwm".as_bytes(), + ); } //#[deprecated = "use `self.connection.dpy()` instead"] @@ -803,28 +932,12 @@ impl XLib { atom if atom == self.ewmh_atoms[EWMHAtom::NetWmWindowType] => { - if self - .get_atom_property( + Some(XLibWindowEvent::WindowTypeChangedEvent( + WindowTypeChangedEvent::new( ev.window, - self.ewmh_atoms[EWMHAtom::NetWmState], - ) - .map(|atom| { - *atom - == self.ewmh_atoms - [EWMHAtom::NetWmStateFullscreen] - }) - .unwrap_or(false) - { - debug!("fullscreen event"); - Some(XLibWindowEvent::FullscreenEvent( - FullscreenEvent::new( - ev.window, - FullscreenState::On, - ), - )) - } else { - None - } + self.get_window_type(ev.window), + ), + )) } _ => None, } @@ -872,7 +985,7 @@ impl XLib { #[allow(dead_code)] fn get_window_attributes( &self, - window: xlib::Window, + window: Window, ) -> Option { let mut wa = unsafe { std::mem::MaybeUninit::::zeroed() @@ -890,7 +1003,7 @@ impl XLib { fn get_atom_property( &self, - window: xlib::Window, + window: Window, atom: xlib::Atom, ) -> Option> { let mut di = 0; @@ -919,11 +1032,7 @@ impl XLib { success.then(|| atom_out).flatten() } - fn check_for_protocol( - &self, - window: xlib::Window, - proto: xlib::Atom, - ) -> bool { + fn check_for_protocol(&self, window: Window, proto: xlib::Atom) -> bool { let mut protos: *mut xlib::Atom = std::ptr::null_mut(); let mut num_protos: i32 = 0; @@ -946,7 +1055,7 @@ impl XLib { return false; } - fn send_protocol(&self, window: xlib::Window, proto: Atom) -> bool { + fn send_protocol(&self, window: Window, proto: Atom) -> bool { if self.check_for_protocol(window, proto) { let mut data = xlib::ClientMessageData::default(); data.set_long(0, proto as i64); @@ -1032,11 +1141,7 @@ impl XLib { None } - fn grab_key_or_button( - &self, - binding: &KeyOrMouseBind, - window: xlib::Window, - ) { + fn grab_key_or_button(&self, binding: &KeyOrMouseBind, window: Window) { let modmask = binding.modifiers.as_modmask(self); let numlock_mask = self @@ -1090,11 +1195,7 @@ impl XLib { } #[allow(dead_code)] - fn ungrab_key_or_button( - &self, - binding: &KeyOrMouseBind, - window: xlib::Window, - ) { + fn ungrab_key_or_button(&self, binding: &KeyOrMouseBind, window: Window) { let modmask = binding.modifiers.as_modmask(self); let numlock_mask = self @@ -1135,7 +1236,7 @@ impl XLib { } } - fn grab_global_keybinds(&self, window: xlib::Window) { + fn grab_global_keybinds(&self, window: Window) { for binding in self.keybinds.iter() { self.grab_key_or_button(binding, window); } @@ -1197,7 +1298,7 @@ impl ModifierStateExt for ModifierState { } impl WindowServerBackend for XLib { - type Window = xlib::Window; + type Window = Window; fn build() -> Self { let xlib = Self::new(); @@ -1211,6 +1312,7 @@ impl WindowServerBackend for XLib { let ev = self.xevent_to_window_event(ev); if let Some(ev) = ev { + self.handle_event(ev.clone()); return ev; } } @@ -1236,15 +1338,35 @@ impl WindowServerBackend for XLib { } self.grab_global_keybinds(event.window); - } - WindowEvent::ConfigureEvent(event) => { - self.configure_window( - event.window, - Some(event.size), - Some(event.position), - None, + + // add window to client list + self.connection.change_root_property_long( + self.ewmh_atoms[EWMHAtom::NetClientList], + XA_WINDOW, + PropMode::Append, + &[event.window as i64], ); } + WindowEvent::DestroyEvent(event) => { + self.connection + .get_property_long( + self.connection.root(), + self.ewmh_atoms[EWMHAtom::NetClientList], + XA_WINDOW, + ) + .map(|mut clients| { + clients + .retain(|&window| window as Window != event.window); + + self.connection.change_property_long( + self.connection.root(), + self.ewmh_atoms[EWMHAtom::NetClientList], + XA_WINDOW, + PropMode::Replace, + &clients, + ); + }); + } _ => {} } } @@ -1612,6 +1734,10 @@ pub mod xpointer { let result = cb(&mut ptr); (NonNull::new(ptr as *mut T).map(|ptr| Self(ptr)), result) } + + pub fn as_ptr(&self) -> *const T { + self.0.as_ptr() as *const _ + } } impl AsRef for XPointer { diff --git a/src/state.rs b/src/state.rs index 2fd9fa4..142e2b2 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,7 +6,7 @@ use x11::xlib::{self, Window}; use crate::backends::structs::WindowType; use crate::backends::window_event::{ - FullscreenEvent, FullscreenState, WindowNameEvent, + FullscreenEvent, FullscreenState, WindowNameEvent, WindowTypeChangedEvent, }; use crate::util::{Point, Size}; use crate::{ @@ -306,13 +306,6 @@ where &self.config.inactive_window_border_color, ); - // add all already existing windows to the WM - if let Some(windows) = self.backend.all_windows() { - windows - .into_iter() - .for_each(|window| self.new_client(window)); - } - self } @@ -431,8 +424,6 @@ where self.button_event(&event); } WindowEvent::MapRequestEvent(MapEvent { window }) => { - self.backend.handle_event(event); - if !self.clients.contains(&window) { self.new_client(window); } @@ -448,28 +439,27 @@ where self.do_move_resize_window(&event); } WindowEvent::ConfigureEvent(ConfigureEvent { - window, .. - }) => { - match self.clients.get(&window) { - ClientEntry::Tiled(client) - | ClientEntry::Floating(client) => { - self.backend.configure_window( - window, - Some(client.size), - Some(client.position), - None, - ) - } - ClientEntry::Vacant => self.backend.handle_event(event), + window, + size, + position, + .. + }) => match self.clients.get(&window) { + ClientEntry::Tiled(client) + | ClientEntry::Floating(client) => { + self.backend.configure_window( + window, + Some(client.size), + Some(client.position), + None, + ) } - // TODO - // match self.clients.get(&event.window).into_option() { - // Some(client) => self - // .xlib - // .configure_client(client, self.clients.get_border()), - // None => self.xlib.configure_window(event), - // } - } + ClientEntry::Vacant => self.backend.configure_window( + window, + Some(size), + Some(position), + None, + ), + }, WindowEvent::FullscreenEvent(FullscreenEvent { window, state, @@ -506,6 +496,14 @@ where WindowEvent::WindowNameEvent(WindowNameEvent { .. }) => { info!("{:#?}", event); } + WindowEvent::WindowTypeChangedEvent( + WindowTypeChangedEvent { + window, + window_type, + }, + ) => { + self.clients.update_window_type(&window, window_type); + } // i dont think i actually have to handle destroy notify events. // every window should be unmapped regardless