From 56fff2698b1b91e42eba0f043c0bf6fa7d6cf479 Mon Sep 17 00:00:00 2001 From: Janis Date: Sat, 7 May 2022 14:23:31 +0200 Subject: [PATCH 01/14] added all EWMH atoms as enum --- Cargo.toml | 1 + src/backends/xlib/mod.rs | 203 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index e633f9a..d3d4112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,4 @@ 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 diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index 7d322ba..30746a8 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -99,6 +99,209 @@ impl From for XlibError { } } +pub mod ewmh { + use std::ffi::CString; + + use strum::{EnumCount, EnumIter}; + use x11::xlib::Atom; + + use super::Display; + + #[derive(Debug, PartialEq, Eq, EnumIter, EnumCount, Clone, Copy)] + pub enum EWMHAtom { + NetSupported, + NetClientList, + NetNumberOfDesktops, + NetDesktopGeometry, + NetDesktopViewport, + NetCurrentDesktop, + NetDesktopNames, + NetActiveWindow, + NetWorkarea, + NetSupportingWmCheck, + NetVirtualRoots, + NetDesktopLayout, + NetShowingDesktop, + NetCloseWindow, + NetMoveresizeWindow, + NetWmMoveresize, + NetRestackWindow, + NetRequestFrameExtents, + NetWmName, + NetWmVisibleName, + NetWmIconName, + NetWmVisibleIconName, + NetWmDesktop, + NetWmWindowType, + NetWmState, + NetWmAllowedActions, + NetWmStrut, + NetWmStrutPartial, + NetWmIconGeometry, + NetWmIcon, + NetWmPid, + NetWmHandledIcons, + NetWmUserTime, + NetFrameExtents, + NetWmPing, + NetWmSyncRequest, + + // idk if these are atoms? + NetWmWindowTypeDesktop, + NetWmWindowTypeDock, + NetWmWindowTypeToolbar, + NetWmWindowTypeMenu, + NetWmWindowTypeUtility, + NetWmWindowTypeSplash, + NetWmWindowTypeDialog, + NetWmWindowTypeNormal, + NetWmStateModal, + NetWmStateSticky, + NetWmStateMaximizedVert, + NetWmStateMaximizedHorz, + NetWmStateShaded, + NetWmStateSkipTaskbar, + NetWmStateSkipPager, + NetWmStateHidden, + NetWmStateFullscreen, + NetWmStateAbove, + NetWmStateBelow, + NetWmStateDemandsAttention, + NetWmActionMove, + NetWmActionResize, + NetWmActionMinimize, + NetWmActionShade, + NetWmActionStick, + NetWmActionMaximizeHorz, + NetWmActionMaximizeVert, + NetWmActionFullscreen, + NetWmActionChangeDesktop, + NetWmActionClose, + } + + impl EWMHAtom { + pub fn try_get_atoms(display: Display) -> Option> { + use strum::IntoEnumIterator; + Self::iter() + .map(|atom| atom.try_into_x_atom(&display)) + .collect::>>() + } + + fn try_into_x_atom(self, display: &Display) -> Option { + let name = CString::new::<&str>(self.into()).ok()?; + match unsafe { + x11::xlib::XInternAtom( + display.get(), + name.as_c_str().as_ptr(), + 0, + ) + } { + 0 => None, + atom => Some(atom), + } + } + } + + impl From for u8 { + fn from(atom: EWMHAtom) -> Self { + atom as u8 + } + } + + impl From for &str { + fn from(atom: EWMHAtom) -> Self { + match atom { + EWMHAtom::NetSupported => "_NET_SUPPORTED", + EWMHAtom::NetClientList => "_NET_CLIENT_LIST", + EWMHAtom::NetNumberOfDesktops => "_NET_NUMBER_OF_DESKTOPS", + EWMHAtom::NetDesktopGeometry => "_NET_DESKTOP_GEOMETRY", + EWMHAtom::NetDesktopViewport => "_NET_DESKTOP_VIEWPORT", + EWMHAtom::NetCurrentDesktop => "_NET_CURRENT_DESKTOP", + EWMHAtom::NetDesktopNames => "_NET_DESKTOP_NAMES", + EWMHAtom::NetActiveWindow => "_NET_ACTIVE_WINDOW", + EWMHAtom::NetWorkarea => "_NET_WORKAREA", + EWMHAtom::NetSupportingWmCheck => "_NET_SUPPORTING_WM_CHECK", + EWMHAtom::NetVirtualRoots => "_NET_VIRTUAL_ROOTS", + EWMHAtom::NetDesktopLayout => "_NET_DESKTOP_LAYOUT", + EWMHAtom::NetShowingDesktop => "_NET_SHOWING_DESKTOP", + EWMHAtom::NetCloseWindow => "_NET_CLOSE_WINDOW", + EWMHAtom::NetMoveresizeWindow => "_NET_MOVERESIZE_WINDOW", + EWMHAtom::NetWmMoveresize => "_NET_WM_MOVERESIZE", + EWMHAtom::NetRestackWindow => "_NET_RESTACK_WINDOW", + EWMHAtom::NetRequestFrameExtents => { + "_NET_REQUEST_FRAME_EXTENTS" + } + EWMHAtom::NetWmName => "_NET_WM_NAME", + EWMHAtom::NetWmVisibleName => "_NET_WM_VISIBLE_NAME", + EWMHAtom::NetWmIconName => "_NET_WM_ICON_NAME", + EWMHAtom::NetWmVisibleIconName => "_NET_WM_VISIBLE_ICON_NAME", + EWMHAtom::NetWmDesktop => "_NET_WM_DESKTOP", + EWMHAtom::NetWmWindowType => "_NET_WM_WINDOW_TYPE", + EWMHAtom::NetWmState => "_NET_WM_STATE", + EWMHAtom::NetWmAllowedActions => "_NET_WM_ALLOWED_ACTIONS", + EWMHAtom::NetWmStrut => "_NET_WM_STRUT", + EWMHAtom::NetWmStrutPartial => "_NET_WM_STRUT_PARTIAL", + EWMHAtom::NetWmIconGeometry => "_NET_WM_ICON_GEOMETRY", + EWMHAtom::NetWmIcon => "_NET_WM_ICON", + EWMHAtom::NetWmPid => "_NET_WM_PID", + EWMHAtom::NetWmHandledIcons => "_NET_WM_HANDLED_ICONS", + EWMHAtom::NetWmUserTime => "_NET_WM_USER_TIME", + EWMHAtom::NetFrameExtents => "_NET_FRAME_EXTENTS", + EWMHAtom::NetWmPing => "_NET_WM_PING", + EWMHAtom::NetWmSyncRequest => "_NET_WM_SYNC_REQUEST", + EWMHAtom::NetWmWindowTypeDesktop => { + "_NET_WM_WINDOW_TYPE_DESKTOP" + } + EWMHAtom::NetWmWindowTypeDock => "_NET_WM_WINDOW_TYPE_DOCK", + EWMHAtom::NetWmWindowTypeToolbar => { + "_NET_WM_WINDOW_TYPE_TOOLBAR" + } + EWMHAtom::NetWmWindowTypeMenu => "_NET_WM_WINDOW_TYPE_MENU", + EWMHAtom::NetWmWindowTypeUtility => { + "_NET_WM_WINDOW_TYPE_UTILITY" + } + EWMHAtom::NetWmWindowTypeSplash => "_NET_WM_WINDOW_TYPE_SPLASH", + EWMHAtom::NetWmWindowTypeDialog => "_NET_WM_WINDOW_TYPE_DIALOG", + EWMHAtom::NetWmWindowTypeNormal => "_NET_WM_WINDOW_TYPE_NORMAL", + EWMHAtom::NetWmStateModal => "_NET_WM_STATE_MODAL", + EWMHAtom::NetWmStateSticky => "_NET_WM_STATE_STICKY", + EWMHAtom::NetWmStateMaximizedVert => { + "_NET_WM_STATE_MAXIMIZED_VERT" + } + EWMHAtom::NetWmStateMaximizedHorz => { + "_NET_WM_STATE_MAXIMIZED_HORZ" + } + EWMHAtom::NetWmStateShaded => "_NET_WM_STATE_SHADED", + EWMHAtom::NetWmStateSkipTaskbar => "_NET_WM_STATE_SKIP_TASKBAR", + EWMHAtom::NetWmStateSkipPager => "_NET_WM_STATE_SKIP_PAGER", + EWMHAtom::NetWmStateHidden => "_NET_WM_STATE_HIDDEN", + EWMHAtom::NetWmStateFullscreen => "_NET_WM_STATE_FULLSCREEN", + EWMHAtom::NetWmStateAbove => "_NET_WM_STATE_ABOVE", + EWMHAtom::NetWmStateBelow => "_NET_WM_STATE_BELOW", + EWMHAtom::NetWmStateDemandsAttention => { + "_NET_WM_STATE_DEMANDS_ATTENTION" + } + EWMHAtom::NetWmActionMove => "_NET_WM_ACTION_MOVE", + EWMHAtom::NetWmActionResize => "_NET_WM_ACTION_RESIZE", + EWMHAtom::NetWmActionMinimize => "_NET_WM_ACTION_MINIMIZE", + EWMHAtom::NetWmActionShade => "_NET_WM_ACTION_SHADE", + EWMHAtom::NetWmActionStick => "_NET_WM_ACTION_STICK", + EWMHAtom::NetWmActionMaximizeHorz => { + "_NET_WM_ACTION_MAXIMIZE_HORZ" + } + EWMHAtom::NetWmActionMaximizeVert => { + "_NET_WM_ACTION_MAXIMIZE_VERT" + } + EWMHAtom::NetWmActionFullscreen => "_NET_WM_ACTION_FULLSCREEN", + EWMHAtom::NetWmActionChangeDesktop => { + "_NET_WM_ACTION_CHANGE_DESKTOP" + } + EWMHAtom::NetWmActionClose => "_NET_WM_ACTION_CLOSE", + } + } + } +} + impl Display { pub fn new(display: *mut x11::xlib::Display) -> Self { Self { From 2f805dab21544150e79d11b7bb40a2248fce6406 Mon Sep 17 00:00:00 2001 From: Janis Date: Sat, 7 May 2022 14:37:17 +0200 Subject: [PATCH 02/14] added test to EWMH atoms --- src/backends/xlib/mod.rs | 193 ++++++++++++++++++++++----------------- 1 file changed, 109 insertions(+), 84 deletions(-) diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index 30746a8..b6abf4a 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -1,6 +1,10 @@ use log::{debug, error, warn}; use num_traits::Zero; +use std::{ffi::CString, mem::MaybeUninit, ptr::NonNull, rc::Rc}; use std::{ffi::CString, rc::Rc}; +>>>>>>> variant B +use std::{ffi::CString, mem::MaybeUninit, ptr::NonNull, rc::Rc}; +======= end use thiserror::Error; @@ -32,7 +36,7 @@ pub mod keysym; pub type XLibWindowEvent = WindowEvent; #[derive(Clone)] -pub struct Display(Rc<*mut x11::xlib::Display>); +pub struct Display(Rc>); #[derive(Debug, Error)] pub enum XlibError { @@ -108,7 +112,7 @@ pub mod ewmh { use super::Display; #[derive(Debug, PartialEq, Eq, EnumIter, EnumCount, Clone, Copy)] - pub enum EWMHAtom { + pub enum EWMHAtoms { NetSupported, NetClientList, NetNumberOfDesktops, @@ -179,7 +183,7 @@ pub mod ewmh { NetWmActionClose, } - impl EWMHAtom { + impl EWMHAtoms { pub fn try_get_atoms(display: Display) -> Option> { use strum::IntoEnumIterator; Self::iter() @@ -202,115 +206,139 @@ pub mod ewmh { } } - impl From for u8 { - fn from(atom: EWMHAtom) -> Self { + impl From for u8 { + fn from(atom: EWMHAtoms) -> Self { atom as u8 } } - impl From for &str { - fn from(atom: EWMHAtom) -> Self { + impl From for &str { + fn from(atom: EWMHAtoms) -> Self { match atom { - EWMHAtom::NetSupported => "_NET_SUPPORTED", - EWMHAtom::NetClientList => "_NET_CLIENT_LIST", - EWMHAtom::NetNumberOfDesktops => "_NET_NUMBER_OF_DESKTOPS", - EWMHAtom::NetDesktopGeometry => "_NET_DESKTOP_GEOMETRY", - EWMHAtom::NetDesktopViewport => "_NET_DESKTOP_VIEWPORT", - EWMHAtom::NetCurrentDesktop => "_NET_CURRENT_DESKTOP", - EWMHAtom::NetDesktopNames => "_NET_DESKTOP_NAMES", - EWMHAtom::NetActiveWindow => "_NET_ACTIVE_WINDOW", - EWMHAtom::NetWorkarea => "_NET_WORKAREA", - EWMHAtom::NetSupportingWmCheck => "_NET_SUPPORTING_WM_CHECK", - EWMHAtom::NetVirtualRoots => "_NET_VIRTUAL_ROOTS", - EWMHAtom::NetDesktopLayout => "_NET_DESKTOP_LAYOUT", - EWMHAtom::NetShowingDesktop => "_NET_SHOWING_DESKTOP", - EWMHAtom::NetCloseWindow => "_NET_CLOSE_WINDOW", - EWMHAtom::NetMoveresizeWindow => "_NET_MOVERESIZE_WINDOW", - EWMHAtom::NetWmMoveresize => "_NET_WM_MOVERESIZE", - EWMHAtom::NetRestackWindow => "_NET_RESTACK_WINDOW", - EWMHAtom::NetRequestFrameExtents => { + EWMHAtoms::NetSupported => "_NET_SUPPORTED", + EWMHAtoms::NetClientList => "_NET_CLIENT_LIST", + EWMHAtoms::NetNumberOfDesktops => "_NET_NUMBER_OF_DESKTOPS", + EWMHAtoms::NetDesktopGeometry => "_NET_DESKTOP_GEOMETRY", + EWMHAtoms::NetDesktopViewport => "_NET_DESKTOP_VIEWPORT", + EWMHAtoms::NetCurrentDesktop => "_NET_CURRENT_DESKTOP", + EWMHAtoms::NetDesktopNames => "_NET_DESKTOP_NAMES", + EWMHAtoms::NetActiveWindow => "_NET_ACTIVE_WINDOW", + EWMHAtoms::NetWorkarea => "_NET_WORKAREA", + EWMHAtoms::NetSupportingWmCheck => "_NET_SUPPORTING_WM_CHECK", + EWMHAtoms::NetVirtualRoots => "_NET_VIRTUAL_ROOTS", + EWMHAtoms::NetDesktopLayout => "_NET_DESKTOP_LAYOUT", + EWMHAtoms::NetShowingDesktop => "_NET_SHOWING_DESKTOP", + EWMHAtoms::NetCloseWindow => "_NET_CLOSE_WINDOW", + EWMHAtoms::NetMoveresizeWindow => "_NET_MOVERESIZE_WINDOW", + EWMHAtoms::NetWmMoveresize => "_NET_WM_MOVERESIZE", + EWMHAtoms::NetRestackWindow => "_NET_RESTACK_WINDOW", + EWMHAtoms::NetRequestFrameExtents => { "_NET_REQUEST_FRAME_EXTENTS" } - EWMHAtom::NetWmName => "_NET_WM_NAME", - EWMHAtom::NetWmVisibleName => "_NET_WM_VISIBLE_NAME", - EWMHAtom::NetWmIconName => "_NET_WM_ICON_NAME", - EWMHAtom::NetWmVisibleIconName => "_NET_WM_VISIBLE_ICON_NAME", - EWMHAtom::NetWmDesktop => "_NET_WM_DESKTOP", - EWMHAtom::NetWmWindowType => "_NET_WM_WINDOW_TYPE", - EWMHAtom::NetWmState => "_NET_WM_STATE", - EWMHAtom::NetWmAllowedActions => "_NET_WM_ALLOWED_ACTIONS", - EWMHAtom::NetWmStrut => "_NET_WM_STRUT", - EWMHAtom::NetWmStrutPartial => "_NET_WM_STRUT_PARTIAL", - EWMHAtom::NetWmIconGeometry => "_NET_WM_ICON_GEOMETRY", - EWMHAtom::NetWmIcon => "_NET_WM_ICON", - EWMHAtom::NetWmPid => "_NET_WM_PID", - EWMHAtom::NetWmHandledIcons => "_NET_WM_HANDLED_ICONS", - EWMHAtom::NetWmUserTime => "_NET_WM_USER_TIME", - EWMHAtom::NetFrameExtents => "_NET_FRAME_EXTENTS", - EWMHAtom::NetWmPing => "_NET_WM_PING", - EWMHAtom::NetWmSyncRequest => "_NET_WM_SYNC_REQUEST", - EWMHAtom::NetWmWindowTypeDesktop => { + EWMHAtoms::NetWmName => "_NET_WM_NAME", + EWMHAtoms::NetWmVisibleName => "_NET_WM_VISIBLE_NAME", + EWMHAtoms::NetWmIconName => "_NET_WM_ICON_NAME", + EWMHAtoms::NetWmVisibleIconName => "_NET_WM_VISIBLE_ICON_NAME", + EWMHAtoms::NetWmDesktop => "_NET_WM_DESKTOP", + EWMHAtoms::NetWmWindowType => "_NET_WM_WINDOW_TYPE", + EWMHAtoms::NetWmState => "_NET_WM_STATE", + EWMHAtoms::NetWmAllowedActions => "_NET_WM_ALLOWED_ACTIONS", + EWMHAtoms::NetWmStrut => "_NET_WM_STRUT", + EWMHAtoms::NetWmStrutPartial => "_NET_WM_STRUT_PARTIAL", + EWMHAtoms::NetWmIconGeometry => "_NET_WM_ICON_GEOMETRY", + EWMHAtoms::NetWmIcon => "_NET_WM_ICON", + EWMHAtoms::NetWmPid => "_NET_WM_PID", + EWMHAtoms::NetWmHandledIcons => "_NET_WM_HANDLED_ICONS", + EWMHAtoms::NetWmUserTime => "_NET_WM_USER_TIME", + EWMHAtoms::NetFrameExtents => "_NET_FRAME_EXTENTS", + EWMHAtoms::NetWmPing => "_NET_WM_PING", + EWMHAtoms::NetWmSyncRequest => "_NET_WM_SYNC_REQUEST", + EWMHAtoms::NetWmWindowTypeDesktop => { "_NET_WM_WINDOW_TYPE_DESKTOP" } - EWMHAtom::NetWmWindowTypeDock => "_NET_WM_WINDOW_TYPE_DOCK", - EWMHAtom::NetWmWindowTypeToolbar => { + EWMHAtoms::NetWmWindowTypeDock => "_NET_WM_WINDOW_TYPE_DOCK", + EWMHAtoms::NetWmWindowTypeToolbar => { "_NET_WM_WINDOW_TYPE_TOOLBAR" } - EWMHAtom::NetWmWindowTypeMenu => "_NET_WM_WINDOW_TYPE_MENU", - EWMHAtom::NetWmWindowTypeUtility => { + EWMHAtoms::NetWmWindowTypeMenu => "_NET_WM_WINDOW_TYPE_MENU", + EWMHAtoms::NetWmWindowTypeUtility => { "_NET_WM_WINDOW_TYPE_UTILITY" } - EWMHAtom::NetWmWindowTypeSplash => "_NET_WM_WINDOW_TYPE_SPLASH", - EWMHAtom::NetWmWindowTypeDialog => "_NET_WM_WINDOW_TYPE_DIALOG", - EWMHAtom::NetWmWindowTypeNormal => "_NET_WM_WINDOW_TYPE_NORMAL", - EWMHAtom::NetWmStateModal => "_NET_WM_STATE_MODAL", - EWMHAtom::NetWmStateSticky => "_NET_WM_STATE_STICKY", - EWMHAtom::NetWmStateMaximizedVert => { + EWMHAtoms::NetWmWindowTypeSplash => { + "_NET_WM_WINDOW_TYPE_SPLASH" + } + EWMHAtoms::NetWmWindowTypeDialog => { + "_NET_WM_WINDOW_TYPE_DIALOG" + } + EWMHAtoms::NetWmWindowTypeNormal => { + "_NET_WM_WINDOW_TYPE_NORMAL" + } + EWMHAtoms::NetWmStateModal => "_NET_WM_STATE_MODAL", + EWMHAtoms::NetWmStateSticky => "_NET_WM_STATE_STICKY", + EWMHAtoms::NetWmStateMaximizedVert => { "_NET_WM_STATE_MAXIMIZED_VERT" } - EWMHAtom::NetWmStateMaximizedHorz => { + EWMHAtoms::NetWmStateMaximizedHorz => { "_NET_WM_STATE_MAXIMIZED_HORZ" } - EWMHAtom::NetWmStateShaded => "_NET_WM_STATE_SHADED", - EWMHAtom::NetWmStateSkipTaskbar => "_NET_WM_STATE_SKIP_TASKBAR", - EWMHAtom::NetWmStateSkipPager => "_NET_WM_STATE_SKIP_PAGER", - EWMHAtom::NetWmStateHidden => "_NET_WM_STATE_HIDDEN", - EWMHAtom::NetWmStateFullscreen => "_NET_WM_STATE_FULLSCREEN", - EWMHAtom::NetWmStateAbove => "_NET_WM_STATE_ABOVE", - EWMHAtom::NetWmStateBelow => "_NET_WM_STATE_BELOW", - EWMHAtom::NetWmStateDemandsAttention => { + EWMHAtoms::NetWmStateShaded => "_NET_WM_STATE_SHADED", + EWMHAtoms::NetWmStateSkipTaskbar => { + "_NET_WM_STATE_SKIP_TASKBAR" + } + EWMHAtoms::NetWmStateSkipPager => "_NET_WM_STATE_SKIP_PAGER", + EWMHAtoms::NetWmStateHidden => "_NET_WM_STATE_HIDDEN", + EWMHAtoms::NetWmStateFullscreen => "_NET_WM_STATE_FULLSCREEN", + EWMHAtoms::NetWmStateAbove => "_NET_WM_STATE_ABOVE", + EWMHAtoms::NetWmStateBelow => "_NET_WM_STATE_BELOW", + EWMHAtoms::NetWmStateDemandsAttention => { "_NET_WM_STATE_DEMANDS_ATTENTION" } - EWMHAtom::NetWmActionMove => "_NET_WM_ACTION_MOVE", - EWMHAtom::NetWmActionResize => "_NET_WM_ACTION_RESIZE", - EWMHAtom::NetWmActionMinimize => "_NET_WM_ACTION_MINIMIZE", - EWMHAtom::NetWmActionShade => "_NET_WM_ACTION_SHADE", - EWMHAtom::NetWmActionStick => "_NET_WM_ACTION_STICK", - EWMHAtom::NetWmActionMaximizeHorz => { + EWMHAtoms::NetWmActionMove => "_NET_WM_ACTION_MOVE", + EWMHAtoms::NetWmActionResize => "_NET_WM_ACTION_RESIZE", + EWMHAtoms::NetWmActionMinimize => "_NET_WM_ACTION_MINIMIZE", + EWMHAtoms::NetWmActionShade => "_NET_WM_ACTION_SHADE", + EWMHAtoms::NetWmActionStick => "_NET_WM_ACTION_STICK", + EWMHAtoms::NetWmActionMaximizeHorz => { "_NET_WM_ACTION_MAXIMIZE_HORZ" } - EWMHAtom::NetWmActionMaximizeVert => { + EWMHAtoms::NetWmActionMaximizeVert => { "_NET_WM_ACTION_MAXIMIZE_VERT" } - EWMHAtom::NetWmActionFullscreen => "_NET_WM_ACTION_FULLSCREEN", - EWMHAtom::NetWmActionChangeDesktop => { + EWMHAtoms::NetWmActionFullscreen => "_NET_WM_ACTION_FULLSCREEN", + EWMHAtoms::NetWmActionChangeDesktop => { "_NET_WM_ACTION_CHANGE_DESKTOP" } - EWMHAtom::NetWmActionClose => "_NET_WM_ACTION_CLOSE", + EWMHAtoms::NetWmActionClose => "_NET_WM_ACTION_CLOSE", } } } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn get_atoms() { + let display = Display::open().unwrap(); + let atoms = EWMHAtoms::try_get_atoms(display).expect("atoms"); + println!("{:?}", atoms); + } + } } impl Display { - pub fn new(display: *mut x11::xlib::Display) -> Self { - Self { - 0: Rc::new(display), - } + pub fn new(display: *mut x11::xlib::Display) -> Option { + NonNull::new(display).map(|ptr| Self(Rc::new(ptr))) } + // TODO: error communication + pub fn open() -> Option { + Self::new(unsafe { xlib::XOpenDisplay(std::ptr::null()) }) + } + + /// this should definitely be unsafe lmao pub fn get(&self) -> *mut x11::xlib::Display { - *self.0 + self.0.as_ptr() } } @@ -333,12 +361,9 @@ impl Drop for XLib { impl XLib { fn new() -> Self { let (display, screen, root) = { - let display = unsafe { xlib::XOpenDisplay(std::ptr::null()) }; - assert_ne!(display, std::ptr::null_mut()); - let screen = unsafe { xlib::XDefaultScreen(display) }; - let root = unsafe { xlib::XRootWindow(display, screen) }; - - let display = Display::new(display); + let display = Display::open().expect("failed to open x display"); + let screen = unsafe { xlib::XDefaultScreen(display.get()) }; + let root = unsafe { xlib::XRootWindow(display.get(), screen) }; (display, screen, root) }; From fb011ea23f765f1ec18ce4b17557e3a16dad8e47 Mon Sep 17 00:00:00 2001 From: Janis Date: Sat, 7 May 2022 15:52:53 +0200 Subject: [PATCH 03/14] added XLibConnection, EWMHAtoms type that stores atoms --- src/backends/xlib/mod.rs | 323 +++++++++++++++++++++++++++++---------- 1 file changed, 240 insertions(+), 83 deletions(-) diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index b6abf4a..00d3a51 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -104,15 +104,18 @@ impl From for XlibError { } pub mod ewmh { - use std::ffi::CString; + use std::{borrow::Borrow, ffi::CString, ops::Index, os::raw::c_long}; use strum::{EnumCount, EnumIter}; - use x11::xlib::Atom; + use x11::xlib::{Atom, XA_ATOM}; - use super::Display; + use super::{ + connection::{PropMode, XLibConnection}, + Display, + }; #[derive(Debug, PartialEq, Eq, EnumIter, EnumCount, Clone, Copy)] - pub enum EWMHAtoms { + pub enum EWMHAtom { NetSupported, NetClientList, NetNumberOfDesktops, @@ -183,7 +186,52 @@ pub mod ewmh { NetWmActionClose, } + #[derive(Debug, Clone)] + pub struct EWMHAtoms { + inner: Vec, + } + + impl Index for EWMHAtoms { + type Output = Atom; + + fn index(&self, index: EWMHAtom) -> &Self::Output { + &self.inner[usize::from(index)] + } + } + impl EWMHAtoms { + pub fn from_connection>( + con: C, + ) -> Option { + EWMHAtom::try_get_atoms(con.borrow().display()) + .map(|atoms| Self { inner: atoms }) + } + + pub fn set_supported_atoms>(&self, con: C) { + let supported_atoms = [ + self[EWMHAtom::NetActiveWindow], + self[EWMHAtom::NetWmWindowType], + self[EWMHAtom::NetWmWindowTypeDialog], + self[EWMHAtom::NetWmState], + self[EWMHAtom::NetWmName], + self[EWMHAtom::NetClientList], + self[EWMHAtom::NetWmStateFullscreen], + ] + .to_vec(); + + con.borrow().change_property_long( + self[EWMHAtom::NetSupported], + XA_ATOM, + PropMode::Replace, + supported_atoms + .into_iter() + .map(|atom| atom as c_long) + .collect::>(), + ); + } + } + + impl EWMHAtom { pub fn try_get_atoms(display: Display) -> Option> { use strum::IntoEnumIterator; Self::iter() @@ -206,109 +254,107 @@ pub mod ewmh { } } - impl From for u8 { - fn from(atom: EWMHAtoms) -> Self { + impl From for u8 { + fn from(atom: EWMHAtom) -> Self { atom as u8 } } - impl From for &str { - fn from(atom: EWMHAtoms) -> Self { + impl From for usize { + fn from(atom: EWMHAtom) -> Self { + atom as usize + } + } + + impl From for &str { + fn from(atom: EWMHAtom) -> Self { match atom { - EWMHAtoms::NetSupported => "_NET_SUPPORTED", - EWMHAtoms::NetClientList => "_NET_CLIENT_LIST", - EWMHAtoms::NetNumberOfDesktops => "_NET_NUMBER_OF_DESKTOPS", - EWMHAtoms::NetDesktopGeometry => "_NET_DESKTOP_GEOMETRY", - EWMHAtoms::NetDesktopViewport => "_NET_DESKTOP_VIEWPORT", - EWMHAtoms::NetCurrentDesktop => "_NET_CURRENT_DESKTOP", - EWMHAtoms::NetDesktopNames => "_NET_DESKTOP_NAMES", - EWMHAtoms::NetActiveWindow => "_NET_ACTIVE_WINDOW", - EWMHAtoms::NetWorkarea => "_NET_WORKAREA", - EWMHAtoms::NetSupportingWmCheck => "_NET_SUPPORTING_WM_CHECK", - EWMHAtoms::NetVirtualRoots => "_NET_VIRTUAL_ROOTS", - EWMHAtoms::NetDesktopLayout => "_NET_DESKTOP_LAYOUT", - EWMHAtoms::NetShowingDesktop => "_NET_SHOWING_DESKTOP", - EWMHAtoms::NetCloseWindow => "_NET_CLOSE_WINDOW", - EWMHAtoms::NetMoveresizeWindow => "_NET_MOVERESIZE_WINDOW", - EWMHAtoms::NetWmMoveresize => "_NET_WM_MOVERESIZE", - EWMHAtoms::NetRestackWindow => "_NET_RESTACK_WINDOW", - EWMHAtoms::NetRequestFrameExtents => { + EWMHAtom::NetSupported => "_NET_SUPPORTED", + EWMHAtom::NetClientList => "_NET_CLIENT_LIST", + EWMHAtom::NetNumberOfDesktops => "_NET_NUMBER_OF_DESKTOPS", + EWMHAtom::NetDesktopGeometry => "_NET_DESKTOP_GEOMETRY", + EWMHAtom::NetDesktopViewport => "_NET_DESKTOP_VIEWPORT", + EWMHAtom::NetCurrentDesktop => "_NET_CURRENT_DESKTOP", + EWMHAtom::NetDesktopNames => "_NET_DESKTOP_NAMES", + EWMHAtom::NetActiveWindow => "_NET_ACTIVE_WINDOW", + EWMHAtom::NetWorkarea => "_NET_WORKAREA", + EWMHAtom::NetSupportingWmCheck => "_NET_SUPPORTING_WM_CHECK", + EWMHAtom::NetVirtualRoots => "_NET_VIRTUAL_ROOTS", + EWMHAtom::NetDesktopLayout => "_NET_DESKTOP_LAYOUT", + EWMHAtom::NetShowingDesktop => "_NET_SHOWING_DESKTOP", + EWMHAtom::NetCloseWindow => "_NET_CLOSE_WINDOW", + EWMHAtom::NetMoveresizeWindow => "_NET_MOVERESIZE_WINDOW", + EWMHAtom::NetWmMoveresize => "_NET_WM_MOVERESIZE", + EWMHAtom::NetRestackWindow => "_NET_RESTACK_WINDOW", + EWMHAtom::NetRequestFrameExtents => { "_NET_REQUEST_FRAME_EXTENTS" } - EWMHAtoms::NetWmName => "_NET_WM_NAME", - EWMHAtoms::NetWmVisibleName => "_NET_WM_VISIBLE_NAME", - EWMHAtoms::NetWmIconName => "_NET_WM_ICON_NAME", - EWMHAtoms::NetWmVisibleIconName => "_NET_WM_VISIBLE_ICON_NAME", - EWMHAtoms::NetWmDesktop => "_NET_WM_DESKTOP", - EWMHAtoms::NetWmWindowType => "_NET_WM_WINDOW_TYPE", - EWMHAtoms::NetWmState => "_NET_WM_STATE", - EWMHAtoms::NetWmAllowedActions => "_NET_WM_ALLOWED_ACTIONS", - EWMHAtoms::NetWmStrut => "_NET_WM_STRUT", - EWMHAtoms::NetWmStrutPartial => "_NET_WM_STRUT_PARTIAL", - EWMHAtoms::NetWmIconGeometry => "_NET_WM_ICON_GEOMETRY", - EWMHAtoms::NetWmIcon => "_NET_WM_ICON", - EWMHAtoms::NetWmPid => "_NET_WM_PID", - EWMHAtoms::NetWmHandledIcons => "_NET_WM_HANDLED_ICONS", - EWMHAtoms::NetWmUserTime => "_NET_WM_USER_TIME", - EWMHAtoms::NetFrameExtents => "_NET_FRAME_EXTENTS", - EWMHAtoms::NetWmPing => "_NET_WM_PING", - EWMHAtoms::NetWmSyncRequest => "_NET_WM_SYNC_REQUEST", - EWMHAtoms::NetWmWindowTypeDesktop => { + EWMHAtom::NetWmName => "_NET_WM_NAME", + EWMHAtom::NetWmVisibleName => "_NET_WM_VISIBLE_NAME", + EWMHAtom::NetWmIconName => "_NET_WM_ICON_NAME", + EWMHAtom::NetWmVisibleIconName => "_NET_WM_VISIBLE_ICON_NAME", + EWMHAtom::NetWmDesktop => "_NET_WM_DESKTOP", + EWMHAtom::NetWmWindowType => "_NET_WM_WINDOW_TYPE", + EWMHAtom::NetWmState => "_NET_WM_STATE", + EWMHAtom::NetWmAllowedActions => "_NET_WM_ALLOWED_ACTIONS", + EWMHAtom::NetWmStrut => "_NET_WM_STRUT", + EWMHAtom::NetWmStrutPartial => "_NET_WM_STRUT_PARTIAL", + EWMHAtom::NetWmIconGeometry => "_NET_WM_ICON_GEOMETRY", + EWMHAtom::NetWmIcon => "_NET_WM_ICON", + EWMHAtom::NetWmPid => "_NET_WM_PID", + EWMHAtom::NetWmHandledIcons => "_NET_WM_HANDLED_ICONS", + EWMHAtom::NetWmUserTime => "_NET_WM_USER_TIME", + EWMHAtom::NetFrameExtents => "_NET_FRAME_EXTENTS", + EWMHAtom::NetWmPing => "_NET_WM_PING", + EWMHAtom::NetWmSyncRequest => "_NET_WM_SYNC_REQUEST", + EWMHAtom::NetWmWindowTypeDesktop => { "_NET_WM_WINDOW_TYPE_DESKTOP" } - EWMHAtoms::NetWmWindowTypeDock => "_NET_WM_WINDOW_TYPE_DOCK", - EWMHAtoms::NetWmWindowTypeToolbar => { + EWMHAtom::NetWmWindowTypeDock => "_NET_WM_WINDOW_TYPE_DOCK", + EWMHAtom::NetWmWindowTypeToolbar => { "_NET_WM_WINDOW_TYPE_TOOLBAR" } - EWMHAtoms::NetWmWindowTypeMenu => "_NET_WM_WINDOW_TYPE_MENU", - EWMHAtoms::NetWmWindowTypeUtility => { + EWMHAtom::NetWmWindowTypeMenu => "_NET_WM_WINDOW_TYPE_MENU", + EWMHAtom::NetWmWindowTypeUtility => { "_NET_WM_WINDOW_TYPE_UTILITY" } - EWMHAtoms::NetWmWindowTypeSplash => { - "_NET_WM_WINDOW_TYPE_SPLASH" - } - EWMHAtoms::NetWmWindowTypeDialog => { - "_NET_WM_WINDOW_TYPE_DIALOG" - } - EWMHAtoms::NetWmWindowTypeNormal => { - "_NET_WM_WINDOW_TYPE_NORMAL" - } - EWMHAtoms::NetWmStateModal => "_NET_WM_STATE_MODAL", - EWMHAtoms::NetWmStateSticky => "_NET_WM_STATE_STICKY", - EWMHAtoms::NetWmStateMaximizedVert => { + EWMHAtom::NetWmWindowTypeSplash => "_NET_WM_WINDOW_TYPE_SPLASH", + EWMHAtom::NetWmWindowTypeDialog => "_NET_WM_WINDOW_TYPE_DIALOG", + EWMHAtom::NetWmWindowTypeNormal => "_NET_WM_WINDOW_TYPE_NORMAL", + EWMHAtom::NetWmStateModal => "_NET_WM_STATE_MODAL", + EWMHAtom::NetWmStateSticky => "_NET_WM_STATE_STICKY", + EWMHAtom::NetWmStateMaximizedVert => { "_NET_WM_STATE_MAXIMIZED_VERT" } - EWMHAtoms::NetWmStateMaximizedHorz => { + EWMHAtom::NetWmStateMaximizedHorz => { "_NET_WM_STATE_MAXIMIZED_HORZ" } - EWMHAtoms::NetWmStateShaded => "_NET_WM_STATE_SHADED", - EWMHAtoms::NetWmStateSkipTaskbar => { - "_NET_WM_STATE_SKIP_TASKBAR" - } - EWMHAtoms::NetWmStateSkipPager => "_NET_WM_STATE_SKIP_PAGER", - EWMHAtoms::NetWmStateHidden => "_NET_WM_STATE_HIDDEN", - EWMHAtoms::NetWmStateFullscreen => "_NET_WM_STATE_FULLSCREEN", - EWMHAtoms::NetWmStateAbove => "_NET_WM_STATE_ABOVE", - EWMHAtoms::NetWmStateBelow => "_NET_WM_STATE_BELOW", - EWMHAtoms::NetWmStateDemandsAttention => { + EWMHAtom::NetWmStateShaded => "_NET_WM_STATE_SHADED", + EWMHAtom::NetWmStateSkipTaskbar => "_NET_WM_STATE_SKIP_TASKBAR", + EWMHAtom::NetWmStateSkipPager => "_NET_WM_STATE_SKIP_PAGER", + EWMHAtom::NetWmStateHidden => "_NET_WM_STATE_HIDDEN", + EWMHAtom::NetWmStateFullscreen => "_NET_WM_STATE_FULLSCREEN", + EWMHAtom::NetWmStateAbove => "_NET_WM_STATE_ABOVE", + EWMHAtom::NetWmStateBelow => "_NET_WM_STATE_BELOW", + EWMHAtom::NetWmStateDemandsAttention => { "_NET_WM_STATE_DEMANDS_ATTENTION" } - EWMHAtoms::NetWmActionMove => "_NET_WM_ACTION_MOVE", - EWMHAtoms::NetWmActionResize => "_NET_WM_ACTION_RESIZE", - EWMHAtoms::NetWmActionMinimize => "_NET_WM_ACTION_MINIMIZE", - EWMHAtoms::NetWmActionShade => "_NET_WM_ACTION_SHADE", - EWMHAtoms::NetWmActionStick => "_NET_WM_ACTION_STICK", - EWMHAtoms::NetWmActionMaximizeHorz => { + EWMHAtom::NetWmActionMove => "_NET_WM_ACTION_MOVE", + EWMHAtom::NetWmActionResize => "_NET_WM_ACTION_RESIZE", + EWMHAtom::NetWmActionMinimize => "_NET_WM_ACTION_MINIMIZE", + EWMHAtom::NetWmActionShade => "_NET_WM_ACTION_SHADE", + EWMHAtom::NetWmActionStick => "_NET_WM_ACTION_STICK", + EWMHAtom::NetWmActionMaximizeHorz => { "_NET_WM_ACTION_MAXIMIZE_HORZ" } - EWMHAtoms::NetWmActionMaximizeVert => { + EWMHAtom::NetWmActionMaximizeVert => { "_NET_WM_ACTION_MAXIMIZE_VERT" } - EWMHAtoms::NetWmActionFullscreen => "_NET_WM_ACTION_FULLSCREEN", - EWMHAtoms::NetWmActionChangeDesktop => { + EWMHAtom::NetWmActionFullscreen => "_NET_WM_ACTION_FULLSCREEN", + EWMHAtom::NetWmActionChangeDesktop => { "_NET_WM_ACTION_CHANGE_DESKTOP" } - EWMHAtoms::NetWmActionClose => "_NET_WM_ACTION_CLOSE", + EWMHAtom::NetWmActionClose => "_NET_WM_ACTION_CLOSE", } } } @@ -320,12 +366,123 @@ pub mod ewmh { #[test] fn get_atoms() { let display = Display::open().unwrap(); - let atoms = EWMHAtoms::try_get_atoms(display).expect("atoms"); + let atoms = EWMHAtom::try_get_atoms(display).expect("atoms"); println!("{:?}", atoms); } } } +pub mod connection { + use std::os::raw::c_long; + + use x11::xlib::{self, Atom, Window}; + + use super::Display; + + pub struct XLibConnection { + display: Display, + root: Window, + screen: i32, + } + + impl Drop for XLibConnection { + fn drop(&mut self) { + unsafe { xlib::XCloseDisplay(self.display.get()) }; + } + } + + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + pub enum PropMode { + Replace, + Append, + Prepend, + } + + impl From for i32 { + fn from(mode: PropMode) -> Self { + match mode { + PropMode::Replace => xlib::PropModeReplace, + PropMode::Append => xlib::PropModeAppend, + PropMode::Prepend => xlib::PropModePrepend, + } + } + } + + impl XLibConnection { + pub fn new() -> Option { + if let Some(display) = Display::open() { + let screen = unsafe { xlib::XDefaultScreen(display.get()) }; + let root = unsafe { xlib::XRootWindow(display.get(), screen) }; + + Some(Self { + display, + root, + screen, + }) + } else { + None + } + } + + pub fn dpy(&self) -> *mut xlib::Display { + self.display.get() + } + pub fn display(&self) -> Display { + self.display.clone() + } + + pub fn root(&self) -> Window { + self.root + } + + pub fn screen(&self) -> i32 { + self.screen + } + + pub fn change_property_byte>( + &self, + atom: Atom, + atom_type: Atom, + mode: PropMode, + data: T, + ) { + unsafe { + xlib::XChangeProperty( + self.dpy(), + self.root, + atom, + atom_type, + 8, + mode.into(), + data.as_ref().as_ptr().cast::(), + data.as_ref().len() as i32, + ); + } + } + + pub fn change_property_long>( + &self, + atom: Atom, + atom_type: Atom, + 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, + ); + } + } + } +} + impl Display { pub fn new(display: *mut x11::xlib::Display) -> Option { NonNull::new(display).map(|ptr| Self(Rc::new(ptr))) From 85d3c3ce79782959507ac6232fbb3af6899c02de Mon Sep 17 00:00:00 2001 From: Janis Date: Sat, 7 May 2022 16:14:54 +0200 Subject: [PATCH 04/14] about to breka everything --- src/backends/traits.rs | 1 + src/backends/xlib/mod.rs | 182 ++++++++++++++++++++++++++++----------- 2 files changed, 135 insertions(+), 48 deletions(-) diff --git a/src/backends/traits.rs b/src/backends/traits.rs index 1698c69..0d6104b 100644 --- a/src/backends/traits.rs +++ b/src/backends/traits.rs @@ -31,6 +31,7 @@ pub trait WindowServerBackend { fn screen_size(&self) -> Size; fn get_window_size(&self, window: Self::Window) -> Option>; + fn get_window_name(&self, window: Self::Window) -> Option; fn grab_cursor(&self); fn ungrab_cursor(&self); diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index 00d3a51..29067d6 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -14,9 +14,14 @@ use crate::backends::{ keycodes::KeyOrButton, xlib::keysym::mouse_button_to_xbutton, }; -use self::keysym::{ - keysym_to_virtual_keycode, virtual_keycode_to_keysym, xev_to_mouse_button, - XKeySym, +use self::{ + connection::XLibConnection, + ewmh::EWMHAtoms, + keysym::{ + keysym_to_virtual_keycode, virtual_keycode_to_keysym, + xev_to_mouse_button, XKeySym, + }, + wmh::ICCCMAtoms, }; use super::{ @@ -103,6 +108,92 @@ impl From for XlibError { } } +pub mod wmh { + use std::{borrow::Borrow, ffi::CString, ops::Index, os::raw::c_long}; + + use strum::{EnumCount, EnumIter}; + use x11::xlib::{Atom, XA_ATOM}; + + use super::{ + connection::{PropMode, XLibConnection}, + Display, + }; + + #[derive(Debug, PartialEq, Eq, EnumIter, EnumCount, Clone, Copy)] + pub enum ICCCMAtom { + WmName, + WmProtocols, + WmDeleteWindow, + WmActiveWindow, + WmTakeFocus, + WmState, + } + + #[derive(Debug, Clone)] + pub struct ICCCMAtoms { + inner: Vec, + } + + impl Index for ICCCMAtoms { + type Output = Atom; + + fn index(&self, index: ICCCMAtom) -> &Self::Output { + &self.inner[usize::from(index)] + } + } + + impl ICCCMAtoms { + pub fn from_connection>( + con: C, + ) -> Option { + ICCCMAtom::try_get_atoms(con.borrow().display()) + .map(|atoms| Self { inner: atoms }) + } + } + + impl ICCCMAtom { + pub fn try_get_atoms(display: Display) -> Option> { + use strum::IntoEnumIterator; + Self::iter() + .map(|atom| atom.try_into_x_atom(&display)) + .collect::>>() + } + + fn try_into_x_atom(self, display: &Display) -> Option { + let name = CString::new::<&str>(self.into()).ok()?; + match unsafe { + x11::xlib::XInternAtom( + display.get(), + name.as_c_str().as_ptr(), + 0, + ) + } { + 0 => None, + atom => Some(atom), + } + } + } + + impl From for usize { + fn from(atom: ICCCMAtom) -> Self { + atom as usize + } + } + + impl From for &str { + fn from(atom: ICCCMAtom) -> Self { + match atom { + ICCCMAtom::WmName => "WM_NAME", + ICCCMAtom::WmProtocols => "WM_PROTOCOLS", + ICCCMAtom::WmDeleteWindow => "WM_DELETE_WINDOW", + ICCCMAtom::WmActiveWindow => "WM_ACTIVE_WINDOW", + ICCCMAtom::WmTakeFocus => "WM_TAKE_FOCUS", + ICCCMAtom::WmState => "WM_STATE", + } + } + } +} + pub mod ewmh { use std::{borrow::Borrow, ffi::CString, ops::Index, os::raw::c_long}; @@ -500,38 +591,26 @@ impl Display { } pub struct XLib { - display: Display, - root: Window, - screen: i32, - atoms: XLibAtoms, + connection: Rc, + //atoms: XLibAtoms, + icccm_atoms: ICCCMAtoms, + ewmh_atoms: EWMHAtoms, keybinds: Vec, active_border_color: Option, inactive_border_color: Option, } -impl Drop for XLib { - fn drop(&mut self) { - self.close_dpy(); - } -} - impl XLib { fn new() -> Self { - let (display, screen, root) = { - let display = Display::open().expect("failed to open x display"); - let screen = unsafe { xlib::XDefaultScreen(display.get()) }; - let root = unsafe { xlib::XRootWindow(display.get(), screen) }; - - (display, screen, root) - }; - - let atoms = XLibAtoms::init(display.clone()); + let con = + Rc::new(XLibConnection::new().expect("failed to open x display")); Self { - display, - screen, - root, - atoms, + connection: con.clone(), + icccm_atoms: ICCCMAtoms::from_connection(con.clone()) + .expect("atoms"), + ewmh_atoms: EWMHAtoms::from_connection(con.clone()) + .expect("ewmh atoms"), keybinds: Vec::new(), active_border_color: None, inactive_border_color: None, @@ -551,25 +630,24 @@ impl XLib { | xlib::ButtonPressMask; xlib::XChangeWindowAttributes( - self.dpy(), - self.root, + self.connection.dpy(), + self.connection.root(), xlib::CWEventMask, &mut window_attributes, ); - xlib::XSelectInput(self.dpy(), self.root, window_attributes.event_mask); + xlib::XSelectInput( + self.dpy(), + self.connection.root(), + window_attributes.event_mask, + ); xlib::XSetErrorHandler(Some(xlib_error_handler)); xlib::XSync(self.dpy(), 0); } + #[deprecated = "use `self.connection.dpy()` instead"] fn dpy(&self) -> *mut xlib::Display { - self.display.get() - } - - fn close_dpy(&self) { - unsafe { - xlib::XCloseDisplay(self.dpy()); - } + self.connection.dpy() } fn next_xevent(&mut self) -> XEvent { @@ -1119,7 +1197,7 @@ impl WindowServerBackend for XLib { } fn add_keybind(&mut self, keybind: super::window_event::KeyOrMouseBind) { - self.grab_key_or_button(&keybind, self.root); + self.grab_key_or_button(&keybind, self.connection.root()); self.keybinds.push(keybind); } @@ -1154,7 +1232,7 @@ impl WindowServerBackend for XLib { xlib::XChangeProperty( self.dpy(), - self.root, + self.connection.root(), self.atoms.wm_active_window, xlib::XA_WINDOW, 32, @@ -1171,7 +1249,7 @@ impl WindowServerBackend for XLib { unsafe { xlib::XSetInputFocus( self.dpy(), - self.root, + self.connection.root(), xlib::RevertToPointerRoot, xlib::CurrentTime, ); @@ -1193,7 +1271,7 @@ impl WindowServerBackend for XLib { xlib::XDeleteProperty( self.dpy(), - self.root, + self.connection.root(), self.atoms.wm_active_window, ); } @@ -1274,7 +1352,11 @@ impl WindowServerBackend for XLib { let mut wa = std::mem::MaybeUninit::::zeroed(); - xlib::XGetWindowAttributes(self.dpy(), self.root, wa.as_mut_ptr()); + xlib::XGetWindowAttributes( + self.dpy(), + self.connection.root(), + wa.as_mut_ptr(), + ); let wa = wa.assume_init(); @@ -1291,7 +1373,7 @@ impl WindowServerBackend for XLib { unsafe { xlib::XGrabPointer( self.dpy(), - self.root, + self.connection.root(), 0, (xlib::ButtonPressMask | xlib::ButtonReleaseMask @@ -1316,7 +1398,7 @@ impl WindowServerBackend for XLib { xlib::XWarpPointer( self.dpy(), 0, - window.unwrap_or(self.root), + window.unwrap_or(self.connection.root()), 0, 0, 0, @@ -1336,7 +1418,7 @@ impl WindowServerBackend for XLib { unsafe { xlib::XQueryTree( self.dpy(), - self.root, + self.connection.root(), &mut root, &mut parent, &mut children, @@ -1357,8 +1439,8 @@ impl WindowServerBackend for XLib { fn set_active_window_border_color(&mut self, color_name: &str) { self.active_border_color = color::XftColor::new( - self.display.clone(), - self.screen, + self.connection.display(), + self.connection.screen(), color_name.to_owned(), ) .ok(); @@ -1366,12 +1448,16 @@ impl WindowServerBackend for XLib { fn set_inactive_window_border_color(&mut self, color_name: &str) { self.inactive_border_color = color::XftColor::new( - self.display.clone(), - self.screen, + self.connection.display(), + self.connection.screen(), color_name.to_owned(), ) .ok(); } + + fn get_window_name(&self, window: Self::Window) -> Option { + todo!() + } } #[allow(dead_code)] From 0dd42a7039623ddc17294f9eba425c502630713a Mon Sep 17 00:00:00 2001 From: Janis Date: Sat, 7 May 2022 16:34:44 +0200 Subject: [PATCH 05/14] refactored atoms --- src/backends/xlib/mod.rs | 151 +++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 85 deletions(-) diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index 29067d6..90967fb 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -1,5 +1,6 @@ use log::{debug, error, warn}; use num_traits::Zero; +<<<<<<< HEAD use std::{ffi::CString, mem::MaybeUninit, ptr::NonNull, rc::Rc}; use std::{ffi::CString, rc::Rc}; >>>>>>> variant B @@ -9,6 +10,13 @@ use std::{ffi::CString, mem::MaybeUninit, ptr::NonNull, rc::Rc}; use thiserror::Error; use x11::xlib::{self, Atom, Success, Window, XEvent, XInternAtom, XKeyEvent}; +======= +use std::{mem::MaybeUninit, ptr::NonNull, rc::Rc}; + +use thiserror::Error; + +use x11::xlib::{self, Atom, XEvent, XKeyEvent}; +>>>>>>> ab99fdd (refactored atoms) use crate::backends::{ keycodes::KeyOrButton, xlib::keysym::mouse_button_to_xbutton, @@ -16,12 +24,12 @@ use crate::backends::{ use self::{ connection::XLibConnection, - ewmh::EWMHAtoms, + ewmh::{EWMHAtom, EWMHAtoms}, keysym::{ keysym_to_virtual_keycode, virtual_keycode_to_keysym, xev_to_mouse_button, XKeySym, }, - wmh::ICCCMAtoms, + wmh::{ICCCMAtom, ICCCMAtoms}, }; use super::{ @@ -109,15 +117,12 @@ impl From for XlibError { } pub mod wmh { - use std::{borrow::Borrow, ffi::CString, ops::Index, os::raw::c_long}; + use std::{borrow::Borrow, ffi::CString, ops::Index}; use strum::{EnumCount, EnumIter}; - use x11::xlib::{Atom, XA_ATOM}; + use x11::xlib::Atom; - use super::{ - connection::{PropMode, XLibConnection}, - Display, - }; + use super::{connection::XLibConnection, Display}; #[derive(Debug, PartialEq, Eq, EnumIter, EnumCount, Clone, Copy)] pub enum ICCCMAtom { @@ -310,7 +315,7 @@ pub mod ewmh { ] .to_vec(); - con.borrow().change_property_long( + con.borrow().change_root_property_long( self[EWMHAtom::NetSupported], XA_ATOM, PropMode::Replace, @@ -464,7 +469,10 @@ pub mod ewmh { } pub mod connection { - use std::os::raw::c_long; + use std::{ + ffi::CString, + os::raw::{c_char, c_long}, + }; use x11::xlib::{self, Atom, Window}; @@ -530,7 +538,33 @@ pub mod connection { self.screen } - pub fn change_property_byte>( + pub fn get_text_property( + &self, + window: Window, + atom: Atom, + ) -> Option { + unsafe { + let mut text_prop = + std::mem::MaybeUninit::::zeroed() + .assume_init(); + + if xlib::XGetTextProperty( + self.dpy(), + window, + &mut text_prop, + atom, + ) == 0 + { + return None; + } + + CString::from_raw(text_prop.value.cast::()) + .into_string() + .ok() + } + } + + pub fn change_root_property_byte>( &self, atom: Atom, atom_type: Atom, @@ -551,7 +585,7 @@ pub mod connection { } } - pub fn change_property_long>( + pub fn change_root_property_long>( &self, atom: Atom, atom_type: Atom, @@ -593,7 +627,7 @@ impl Display { pub struct XLib { connection: Rc, //atoms: XLibAtoms, - icccm_atoms: ICCCMAtoms, + atoms: ICCCMAtoms, ewmh_atoms: EWMHAtoms, keybinds: Vec, active_border_color: Option, @@ -607,8 +641,7 @@ impl XLib { Self { connection: con.clone(), - icccm_atoms: ICCCMAtoms::from_connection(con.clone()) - .expect("atoms"), + atoms: ICCCMAtoms::from_connection(con.clone()).expect("atoms"), ewmh_atoms: EWMHAtoms::from_connection(con.clone()) .expect("ewmh atoms"), keybinds: Vec::new(), @@ -645,7 +678,7 @@ impl XLib { xlib::XSync(self.dpy(), 0); } - #[deprecated = "use `self.connection.dpy()` instead"] + //#[deprecated = "use `self.connection.dpy()` instead"] fn dpy(&self) -> *mut xlib::Display { self.connection.dpy() } @@ -754,14 +787,14 @@ impl XLib { let ev = unsafe { &event.property }; match ev.atom { - atom if atom == self.atoms.net_wm_window_type => { + atom if atom == self.ewmh_atoms[EWMHAtom::NetWmWindowType] => { if self .get_atom_property( ev.window, - self.atoms.net_wm_state, + ewmh_atoms[EWMHAtom::NetWmState], ) .map(|atom| { - *atom == self.atoms.net_wm_state_fullscreen + *atom == self.ewmh_atoms[EWMHAtom::NetWmStateFullscreen] }) .unwrap_or(false) { @@ -783,11 +816,11 @@ impl XLib { let ev = unsafe { &event.client_message }; match ev.message_type { - message_type if message_type == self.atoms.net_wm_state => { + message_type if message_type == self.ewmh_atoms[EWMHAtom::NetWmState] => { let data = ev.data.as_longs(); - if data[1] as u64 == self.atoms.net_wm_state_fullscreen + if data[1] as u64 == self.ewmh_atoms[EWMHAtom::NetWmStateFullScreen] || data[2] as u64 - == self.atoms.net_wm_state_fullscreen + == self.ewmh_atoms[EWMHAtom::NetWmStateFullscreen] { debug!("fullscreen event"); Some(XLibWindowEvent::FullscreenEvent( @@ -906,7 +939,7 @@ impl XLib { send_event: 0, window, format: 32, - message_type: self.atoms.wm_protocols, + message_type: self.atoms[ICCCMAtom::WmProtocols], data, }, }; @@ -1233,7 +1266,7 @@ impl WindowServerBackend for XLib { xlib::XChangeProperty( self.dpy(), self.connection.root(), - self.atoms.wm_active_window, + self.atoms[ICCCMAtom::WmActiveWindow], xlib::XA_WINDOW, 32, xlib::PropModeReplace, @@ -1242,7 +1275,7 @@ impl WindowServerBackend for XLib { ); } - self.send_protocol(window, self.atoms.wm_take_focus); + self.send_protocol(window, self.atoms[ICCCMAtom::WmTakeFocus]); } fn unfocus_window(&self, window: Self::Window) { @@ -1272,7 +1305,7 @@ impl WindowServerBackend for XLib { xlib::XDeleteProperty( self.dpy(), self.connection.root(), - self.atoms.wm_active_window, + self.atoms[ICCCMAtom::WmActiveWindow], ); } } @@ -1289,7 +1322,7 @@ impl WindowServerBackend for XLib { } fn kill_window(&self, window: Self::Window) { - if !self.send_protocol(window, self.atoms.wm_delete_window) { + if !self.send_protocol(window, self.atoms[ICCCMAtom::WmDeleteWindow]) { unsafe { xlib::XKillClient(self.dpy(), window); } @@ -1456,64 +1489,12 @@ impl WindowServerBackend for XLib { } fn get_window_name(&self, window: Self::Window) -> Option { - todo!() - } -} - -#[allow(dead_code)] -struct XLibAtoms { - wm_protocols: Atom, - wm_delete_window: Atom, - wm_active_window: Atom, - wm_take_focus: Atom, - net_supported: Atom, - net_active_window: Atom, - net_client_list: Atom, - net_wm_name: Atom, - net_wm_state: Atom, - net_wm_state_fullscreen: Atom, - net_wm_window_type: Atom, - net_wm_window_type_dialog: Atom, -} - -impl XLibAtoms { - fn init(display: Display) -> Self { - Self { - wm_protocols: Self::get_atom(&display, "WM_PROTOCOLS").unwrap(), - wm_delete_window: Self::get_atom(&display, "WM_DELETE_WINDOW") - .unwrap(), - wm_active_window: Self::get_atom(&display, "WM_ACTIVE_WINDOW") - .unwrap(), - wm_take_focus: Self::get_atom(&display, "WM_TAKE_FOCUS").unwrap(), - net_supported: Self::get_atom(&display, "_NET_SUPPORTED").unwrap(), - net_active_window: Self::get_atom(&display, "_NET_ACTIVE_WINDOW") - .unwrap(), - net_client_list: Self::get_atom(&display, "_NET_CLIENT_LIST") - .unwrap(), - net_wm_name: Self::get_atom(&display, "_NET_WM_NAME").unwrap(), - net_wm_state: Self::get_atom(&display, "_NET_WM_STATE").unwrap(), - net_wm_state_fullscreen: Self::get_atom( - &display, - "_NET_WM_STATE_FULLSCREEN", - ) - .unwrap(), - net_wm_window_type: Self::get_atom(&display, "_NET_WM_WINDOW_TYPE") - .unwrap(), - net_wm_window_type_dialog: Self::get_atom( - &display, - "_NET_WM_WINDOW_TYPE_DIALOG", - ) - .unwrap(), - } - } - - fn get_atom(display: &Display, atom: &str) -> Option { - let name = CString::new(atom).ok()?; - match unsafe { XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) } - { - 0 => None, - atom => Some(atom), - } + self.connection + .get_text_property(window, self.ewmh_atoms[EWMHAtom::NetWmName]) + .or_else(|| { + self.connection + .get_text_property(window, self.atoms[ICCCMAtom::WmName]) + }) } } From ac433847c542008fa80c1a8cf6af7b8c4da35ab8 Mon Sep 17 00:00:00 2001 From: Janis Date: Sat, 7 May 2022 17:11:42 +0200 Subject: [PATCH 06/14] added window name event --- src/backends/window_event.rs | 13 ++++++++++ src/backends/xlib/mod.rs | 50 ++++++++++++++++++++---------------- src/state.rs | 7 ++++- 3 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/backends/window_event.rs b/src/backends/window_event.rs index 44ddb1e..71b7dfd 100644 --- a/src/backends/window_event.rs +++ b/src/backends/window_event.rs @@ -17,6 +17,7 @@ pub enum WindowEvent { EnterEvent(EnterEvent), ConfigureEvent(ConfigureEvent), FullscreenEvent(FullscreenEvent), //1 { window: Window, event: 1 }, + WindowNameEvent(WindowNameEvent), } #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -257,6 +258,18 @@ impl FullscreenEvent { } } +#[derive(Debug)] +pub struct WindowNameEvent { + pub window: Window, + pub name: String, +} + +impl WindowNameEvent { + pub fn new(window: Window, name: String) -> Self { + Self { window, name } + } +} + #[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 90967fb..c583657 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -1,22 +1,10 @@ use log::{debug, error, warn}; use num_traits::Zero; -<<<<<<< HEAD -use std::{ffi::CString, mem::MaybeUninit, ptr::NonNull, rc::Rc}; -use std::{ffi::CString, rc::Rc}; ->>>>>>> variant B -use std::{ffi::CString, mem::MaybeUninit, ptr::NonNull, rc::Rc}; -======= end +use std::{ptr::NonNull, rc::Rc}; use thiserror::Error; -use x11::xlib::{self, Atom, Success, Window, XEvent, XInternAtom, XKeyEvent}; -======= -use std::{mem::MaybeUninit, ptr::NonNull, rc::Rc}; - -use thiserror::Error; - -use x11::xlib::{self, Atom, XEvent, XKeyEvent}; ->>>>>>> ab99fdd (refactored atoms) +use x11::xlib::{self, Atom, Success, XEvent, XKeyEvent}; use crate::backends::{ keycodes::KeyOrButton, xlib::keysym::mouse_button_to_xbutton, @@ -37,7 +25,7 @@ use super::{ window_event::{ ButtonEvent, ConfigureEvent, DestroyEvent, EnterEvent, FullscreenEvent, FullscreenState, KeyEvent, KeyOrMouseBind, KeyState, MapEvent, - ModifierState, MotionEvent, UnmapEvent, WindowEvent, + ModifierState, MotionEvent, UnmapEvent, WindowEvent, WindowNameEvent, }, WindowServerBackend, }; @@ -787,14 +775,27 @@ impl XLib { let ev = unsafe { &event.property }; match ev.atom { - atom if atom == self.ewmh_atoms[EWMHAtom::NetWmWindowType] => { + atom if atom == self.ewmh_atoms[EWMHAtom::NetWmName] + || atom == self.atoms[ICCCMAtom::WmName] => + { + self.get_window_name(ev.window).map(|name| { + XLibWindowEvent::WindowNameEvent( + WindowNameEvent::new(ev.window, name), + ) + }) + } + atom if atom + == self.ewmh_atoms[EWMHAtom::NetWmWindowType] => + { if self .get_atom_property( ev.window, - ewmh_atoms[EWMHAtom::NetWmState], + self.ewmh_atoms[EWMHAtom::NetWmState], ) .map(|atom| { - *atom == self.ewmh_atoms[EWMHAtom::NetWmStateFullscreen] + *atom + == self.ewmh_atoms + [EWMHAtom::NetWmStateFullscreen] }) .unwrap_or(false) { @@ -816,11 +817,16 @@ impl XLib { let ev = unsafe { &event.client_message }; match ev.message_type { - message_type if message_type == self.ewmh_atoms[EWMHAtom::NetWmState] => { + message_type + if message_type + == self.ewmh_atoms[EWMHAtom::NetWmState] => + { let data = ev.data.as_longs(); - if data[1] as u64 == self.ewmh_atoms[EWMHAtom::NetWmStateFullScreen] + if data[1] as u64 + == self.ewmh_atoms[EWMHAtom::NetWmStateFullscreen] || data[2] as u64 - == self.ewmh_atoms[EWMHAtom::NetWmStateFullscreen] + == self.ewmh_atoms + [EWMHAtom::NetWmStateFullscreen] { debug!("fullscreen event"); Some(XLibWindowEvent::FullscreenEvent( @@ -891,7 +897,7 @@ impl XLib { &mut dl0, &mut dl1, ptr as *mut _ as *mut _, - ) == Success.into() + ) == i32::from(Success) }); debug!("get_atom_property: {} {:?}", success, atom_out); diff --git a/src/state.rs b/src/state.rs index f184d36..3e12f82 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,7 +4,9 @@ use log::{error, info}; use x11::xlib::{self, Window}; -use crate::backends::window_event::{FullscreenEvent, FullscreenState}; +use crate::backends::window_event::{ + FullscreenEvent, FullscreenState, WindowNameEvent, +}; use crate::util::{Point, Size}; use crate::{ backends::{ @@ -500,6 +502,9 @@ where self.arrange_clients(); } } + WindowEvent::WindowNameEvent(WindowNameEvent { .. }) => { + info!("{:#?}", event); + } // i dont think i actually have to handle destroy notify events. // every window should be unmapped regardless From f9afdc990d70c21d4e57fdf025d8f770b916c1a5 Mon Sep 17 00:00:00 2001 From: Janis Date: Sat, 7 May 2022 17:14:43 +0200 Subject: [PATCH 07/14] setting supported ewmh atoms fixes fullscreen --- src/backends/xlib/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index c583657..92ca57d 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -662,8 +662,11 @@ impl XLib { self.connection.root(), window_attributes.event_mask, ); + xlib::XSetErrorHandler(Some(xlib_error_handler)); xlib::XSync(self.dpy(), 0); + + self.ewmh_atoms.set_supported_atoms(self.connection.clone()); } //#[deprecated = "use `self.connection.dpy()` instead"] From 4eb1cb4555f74fb1cffa42e2df17e5827c07e82b Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 8 May 2022 01:09:42 +0200 Subject: [PATCH 08/14] added WindowType enum --- src/backends/mod.rs | 15 +++++++++++++ src/backends/traits.rs | 6 ++++- src/backends/xlib/mod.rs | 47 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 73f4ef0..5b1d5a6 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -4,3 +4,18 @@ pub mod window_event; pub mod xlib; pub use traits::*; + +pub mod structs { + + #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] + pub enum WindowType { + Splash, + Dialog, + Normal, + Utility, + Menu, + Toolbar, + Dock, + Desktop, + } +} diff --git a/src/backends/traits.rs b/src/backends/traits.rs index 0d6104b..7928a57 100644 --- a/src/backends/traits.rs +++ b/src/backends/traits.rs @@ -1,4 +1,7 @@ -use super::window_event::{self, KeyOrMouseBind}; +use super::{ + structs::WindowType, + window_event::{self, KeyOrMouseBind}, +}; use crate::util::{Point, Size}; pub trait WindowServerBackend { @@ -32,6 +35,7 @@ pub trait WindowServerBackend { fn screen_size(&self) -> Size; fn get_window_size(&self, window: Self::Window) -> Option>; fn get_window_name(&self, window: Self::Window) -> Option; + fn get_window_type(&self, window: Self::Window) -> WindowType; fn grab_cursor(&self); fn ungrab_cursor(&self); diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index 92ca57d..b559a41 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -22,6 +22,7 @@ use self::{ use super::{ keycodes::VirtualKeyCode, + structs::WindowType, window_event::{ ButtonEvent, ConfigureEvent, DestroyEvent, EnterEvent, FullscreenEvent, FullscreenState, KeyEvent, KeyOrMouseBind, KeyState, MapEvent, @@ -291,6 +292,14 @@ pub mod ewmh { .map(|atoms| Self { inner: atoms }) } + pub fn reverse_lookup(&self, atom: Atom) -> Option { + self.inner + .iter() + .position(|a| *a == atom) + .map(|position| EWMHAtom::from_repr(position)) + .flatten() + } + pub fn set_supported_atoms>(&self, con: C) { let supported_atoms = [ self[EWMHAtom::NetActiveWindow], @@ -1505,6 +1514,44 @@ impl WindowServerBackend for XLib { .get_text_property(window, self.atoms[ICCCMAtom::WmName]) }) } + + fn get_window_type( + &self, + window: Self::Window, + ) -> super::structs::WindowType { + match self + .get_atom_property( + window, + self.ewmh_atoms[EWMHAtom::NetWmWindowType], + ) + .and_then(|atom| self.ewmh_atoms.reverse_lookup(*atom)) + .and_then(|atom| WindowType::try_from(atom).ok()) + { + Some(window_type) => window_type, + None => match self.get_parent_window(window) { + Some(_) => WindowType::Dialog, + None => WindowType::Normal, + }, + } + } +} + +impl TryFrom for WindowType { + type Error = (); + + fn try_from(value: EWMHAtom) -> Result { + match value { + EWMHAtom::NetWmWindowTypeDesktop => Ok(Self::Desktop), + EWMHAtom::NetWmWindowTypeDock => Ok(Self::Dock), + EWMHAtom::NetWmWindowTypeUtility => Ok(Self::Utility), + EWMHAtom::NetWmWindowTypeMenu => Ok(Self::Menu), + EWMHAtom::NetWmWindowTypeToolbar => Ok(Self::Toolbar), + EWMHAtom::NetWmWindowTypeSplash => Ok(Self::Splash), + EWMHAtom::NetWmWindowTypeDialog => Ok(Self::Dialog), + EWMHAtom::NetWmWindowTypeNormal => Ok(Self::Normal), + _ => Err(()), + } + } } #[allow(dead_code)] From 6c4f0d54bdf4d15ace982151a5660e168ecf407a Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 8 May 2022 01:10:06 +0200 Subject: [PATCH 09/14] added wm_transient_for atom --- src/backends/xlib/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index b559a41..00bb14a 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -121,6 +121,7 @@ pub mod wmh { WmActiveWindow, WmTakeFocus, WmState, + WmTransientFor, } #[derive(Debug, Clone)] @@ -183,6 +184,7 @@ pub mod wmh { ICCCMAtom::WmActiveWindow => "WM_ACTIVE_WINDOW", ICCCMAtom::WmTakeFocus => "WM_TAKE_FOCUS", ICCCMAtom::WmState => "WM_STATE", + ICCCMAtom::WmTransientFor => "WM_TRANSIENT_FOR", } } } From 449b4cccd8bf75902f0367841eaf14ad0a953872 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 8 May 2022 01:10:23 +0200 Subject: [PATCH 10/14] added clamp function to Size --- src/util.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util.rs b/src/util.rs index bfc5fc2..c052b1b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -123,6 +123,13 @@ mod size { (self.width, self.height) } + pub fn clamp(self, other: Self) -> Self { + Self::new( + self.width.min(other.width), + self.height.min(other.height), + ) + } + pub fn map(self, f: F) -> Self where F: FnOnce(I, I) -> Self, From bae880c5e1338551a048523753be4551624a4fd5 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 8 May 2022 01:11:04 +0200 Subject: [PATCH 11/14] refactor Client to better reflect different window types --- src/backends/xlib/mod.rs | 10 ++-- src/clients.rs | 113 +++++++++++++++++++++++++++------------ src/state.rs | 26 ++++----- 3 files changed, 98 insertions(+), 51 deletions(-) diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index 00bb14a..a0f2cdd 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -1,6 +1,6 @@ use log::{debug, error, warn}; use num_traits::Zero; -use std::{ptr::NonNull, rc::Rc}; +use std::{convert::TryFrom, ptr::NonNull, rc::Rc}; use thiserror::Error; @@ -193,7 +193,7 @@ pub mod wmh { pub mod ewmh { use std::{borrow::Borrow, ffi::CString, ops::Index, os::raw::c_long}; - use strum::{EnumCount, EnumIter}; + use strum::{EnumCount, EnumIter, FromRepr}; use x11::xlib::{Atom, XA_ATOM}; use super::{ @@ -201,7 +201,9 @@ pub mod ewmh { Display, }; - #[derive(Debug, PartialEq, Eq, EnumIter, EnumCount, Clone, Copy)] + #[derive( + Debug, PartialEq, Eq, EnumIter, EnumCount, Clone, Copy, FromRepr, + )] pub enum EWMHAtom { NetSupported, NetClientList, @@ -914,8 +916,6 @@ impl XLib { ) == i32::from(Success) }); - debug!("get_atom_property: {} {:?}", success, atom_out); - success.then(|| atom_out).flatten() } diff --git a/src/clients.rs b/src/clients.rs index 1c6d3a8..f581489 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -4,13 +4,17 @@ use indexmap::IndexMap; use log::error; use num_traits::Zero; +use crate::backends::structs::WindowType; use crate::util::BuildIdentityHasher; use crate::util::{Point, Size}; mod client { use std::hash::{Hash, Hasher}; - use crate::util::{Point, Size}; + use crate::{ + backends::structs::WindowType, + util::{Point, Size}, + }; use x11::xlib::Window; #[derive(Clone, Debug)] @@ -18,7 +22,8 @@ mod client { pub(crate) window: Window, pub(crate) size: Size, pub(crate) position: Point, - pub(crate) transient_for: Option, + pub(crate) parent_window: Option, + pub(crate) window_type: WindowType, pub(crate) fullscreen: bool, } @@ -28,8 +33,9 @@ mod client { window: 0, size: (100, 100).into(), position: (0, 0).into(), - transient_for: None, + parent_window: None, fullscreen: false, + window_type: WindowType::Normal, } } } @@ -49,7 +55,7 @@ mod client { } } - pub fn new_transient( + pub fn new_dialog( window: Window, size: Size, transient: Window, @@ -57,7 +63,7 @@ mod client { Self { window, size, - transient_for: Some(transient), + parent_window: Some(transient), ..Default::default() } } @@ -69,6 +75,24 @@ mod client { } } + pub fn with_window_type(self, window_type: WindowType) -> Self { + Self { + window_type, + ..self + } + } + + pub fn with_parent_window(self, parent_window: Option) -> Self { + Self { + parent_window, + ..self + } + } + + pub fn with_size(self, size: Size) -> Self { + Self { size, ..self } + } + /// toggles the clients fullscreen flag. /// returns `true` if the client is now fullscreen. pub fn toggle_fullscreen(&mut self) -> bool { @@ -90,8 +114,8 @@ mod client { self.fullscreen } - pub fn is_transient(&self) -> bool { - self.transient_for.is_some() + pub fn has_parent_window(&self) -> bool { + self.parent_window.is_some() } } @@ -240,26 +264,41 @@ impl ClientState { pub fn insert(&mut self, mut client: Client) -> Option<&Client> { let key = client.key(); - if client.is_transient() - && self.contains(&client.transient_for.unwrap()) - { - let transient = self.get(&client.transient_for.unwrap()).unwrap(); + match client.window_type { + // idk how to handle docks and desktops, for now they float innit + WindowType::Splash + | WindowType::Dialog + | WindowType::Utility + | WindowType::Menu + | WindowType::Toolbar + | WindowType::Dock + | WindowType::Desktop => { + if let Some(parent) = client + .parent_window + .and_then(|window| self.get(&window).into_option()) + { + client.position = { + ( + parent.position.x + + (parent.size.width - client.size.width) / 2, + parent.position.y + + (parent.size.height - client.size.height) / 2, + ) + .into() + }; + } - client.position = { - ( - transient.position.x - + (transient.size.width - client.size.width) / 2, - transient.position.y - + (transient.size.height - client.size.height) / 2, - ) - .into() - }; + client.size = client.size.clamp( + self.screen_size + - Size::new(self.border_size * 2, self.border_size * 2), + ); - self.floating_clients.insert(key, client); - } else { - self.clients.insert(key, client); - - self.virtual_screens.get_mut_current().insert(&key); + self.floating_clients.insert(key, client); + } + WindowType::Normal => { + self.clients.insert(key, client); + self.virtual_screens.get_mut_current().insert(&key); + } } // adding a client changes the liling layout, rearrange @@ -320,7 +359,15 @@ impl ClientState { } pub fn iter_transient(&self) -> impl Iterator { - self.iter_floating().filter(|&(_, c)| c.is_transient()) + self.iter_floating().filter(|&(_, c)| c.has_parent_window()) + } + + pub fn iter_by_window_type( + &self, + window_type: WindowType, + ) -> impl Iterator { + self.iter_floating() + .filter(move |&(_, c)| c.window_type == window_type) } pub fn iter_visible(&self) -> impl Iterator { @@ -357,7 +404,7 @@ impl ClientState { { match self.get(key) { ClientEntry::Floating(c) => { - if let Some(transient_for) = c.transient_for { + if let Some(transient_for) = c.parent_window { self.is_client_visible(&transient_for) } else { true @@ -519,15 +566,15 @@ impl ClientState { } (None, Some(floating_client)) => { // transient clients cannot be tiled - match floating_client.is_transient() { - true => { - self.floating_clients.insert(key, floating_client); - } - - false => { + // only normal windows can be tiled + match floating_client.window_type { + WindowType::Normal => { self.clients.insert(key, floating_client); self.virtual_screens.get_mut_current().insert(&key); } + _ => { + self.floating_clients.insert(key, floating_client); + } } } _ => { diff --git a/src/state.rs b/src/state.rs index 3e12f82..2fd9fa4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -4,6 +4,7 @@ use log::{error, info}; use x11::xlib::{self, Window}; +use crate::backends::structs::WindowType; use crate::backends::window_event::{ FullscreenEvent, FullscreenState, WindowNameEvent, }; @@ -748,19 +749,16 @@ where } fn new_client(&mut self, window: Window) { - info!("new client: {:?}", window); - let client = if let Some(transient_window) = - self.backend.get_parent_window(window) - { - Client::new_transient( - window, - self.backend - .get_window_size(window) - .unwrap_or((100, 100).into()), - transient_window, - ) - } else { - Client::new_default(window) + let client = match self.backend.get_window_type(window) { + WindowType::Normal => Client::new_default(window), + window_type @ _ => Client::new_default(window) + .with_window_type(window_type) + .with_size( + self.backend + .get_window_size(window) + .unwrap_or((100, 100).into()), + ) + .with_parent_window(self.backend.get_parent_window(window)), }; self.backend.configure_window( @@ -770,6 +768,8 @@ where Some(self.clients.get_border()), ); + info!("new client: {:#?}", client); + self.clients.insert(client).unwrap(); self.arrange_clients(); From 364d621b72874ccccd0c76207c24e83a8e083a32 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 8 May 2022 02:53:10 +0200 Subject: [PATCH 12/14] added set_tiled function to change window from floating to tiled --- src/clients.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/clients.rs b/src/clients.rs index f581489..567e350 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -537,6 +537,23 @@ impl ClientState { } } + /** + Sets a floating client to tiled and returns true, does nothing for a tiled client and + returns false. If this function returns `true` you have to call `arrange_clients` after. + */ + pub fn set_tiled(&mut self, key: &K) -> bool + where + K: ClientKey, + { + if self.get(key).is_floating() { + self.toggle_floating(key); + + true + } else { + false + } + } + /** This function invalidates the tiling, call `arrange_clients` to fix it again (it doesn't do it automatically since xlib has to move and resize all windows anyways). From 30867df46c3896c87c99f93ffe5dd105fafa3e52 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 8 May 2022 02:53:31 +0200 Subject: [PATCH 13/14] added update_window_type function to clients which updates floating for dialog style windows --- src/clients.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/clients.rs b/src/clients.rs index 567e350..d44221b 100644 --- a/src/clients.rs +++ b/src/clients.rs @@ -606,6 +606,20 @@ impl ClientState { } } + pub fn update_window_type(&mut self, key: &K, window_type: WindowType) + where + K: ClientKey, + { + if let Some(client) = self.get_mut(key).into_option() { + client.window_type = window_type; + + match window_type { + WindowType::Normal => self.set_floating(key), + _ => self.set_tiled(key), + }; + } + } + fn remove_from_virtual_screens(&mut self, key: &K) where K: ClientKey, From c826556e83b86df400c200f762e1bad3d1117183 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 8 May 2022 02:54:40 +0200 Subject: [PATCH 14/14] 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