Beginning of rust-xcb-wm

Initial structure and first basic working EWMH requests
This commit is contained in:
Armin Friedl 2022-05-08 16:18:29 +02:00
parent a22ba5a60c
commit 36add615d9
8 changed files with 773 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
/Cargo.lock
.idea

20
Cargo.toml Normal file
View file

@ -0,0 +1,20 @@
[package]
name = "rust-xcb-wm"
version = "0.1.0"
authors = [ "Armin Friedl <dev@friedl.net>" ]
description = "Rust implementation of xcb-wm - icccm and ewmh extensions for xcb"
readme = "README.md"
keyworks = ["icccm", "ewmh", "xcb-wm", "xcb", "window", "xlib", "x11"]
license = "MIT"
edition = "2018"
repository = "https://git.friedl.net/incubator/rust-xcb-wm"
homepage = "https://git.friedl.net/incubator/rust-xcb-wm"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
xcb = "1"
[features]
icccm = []
ewmh = []

282
src/ewmh/atoms.rs Normal file
View file

@ -0,0 +1,282 @@
use std::collections::HashMap;
const ATOM_NAMES: [&'static str; 82] = [
"_NET_SUPPORTED",
"_NET_CLIENT_LIST",
"_NET_CLIENT_LIST_STACKING",
"_NET_NUMBER_OF_DESKTOPS",
"_NET_DESKTOP_GEOMETRY",
"_NET_DESKTOP_VIEWPORT",
"_NET_CURRENT_DESKTOP",
"_NET_DESKTOP_NAMES",
"_NET_ACTIVE_WINDOW",
"_NET_WORKAREA",
"_NET_SUPPORTING_WM_CHECK",
"_NET_VIRTUAL_ROOTS",
"_NET_DESKTOP_LAYOUT",
"_NET_SHOWING_DESKTOP",
"_NET_CLOSE_WINDOW",
"_NET_MOVERESIZE_WINDOW",
"_NET_WM_MOVERESIZE",
"_NET_RESTACK_WINDOW",
"_NET_REQUEST_FRAME_EXTENTS",
"_NET_WM_NAME",
"_NET_WM_VISIBLE_NAME",
"_NET_WM_ICON_NAME",
"_NET_WM_VISIBLE_ICON_NAME",
"_NET_WM_DESKTOP",
"_NET_WM_WINDOW_TYPE",
"_NET_WM_STATE",
"_NET_WM_ALLOWED_ACTIONS",
"_NET_WM_STRUT",
"_NET_WM_STRUT_PARTIAL",
"_NET_WM_ICON_GEOMETRY",
"_NET_WM_ICON",
"_NET_WM_PID",
"_NET_WM_HANDLED_ICONS",
"_NET_WM_USER_TIME",
"_NET_WM_USER_TIME_WINDOW",
"_NET_FRAME_EXTENTS",
"_NET_WM_PING",
"_NET_WM_SYNC_REQUEST",
"_NET_WM_SYNC_REQUEST_COUNTER",
"_NET_WM_FULLSCREEN_MONITORS",
"_NET_WM_FULL_PLACEMENT",
"UTF8_STRING",
"WM_PROTOCOLS",
"MANAGER",
"_NET_WM_WINDOW_TYPE_DESKTOP",
"_NET_WM_WINDOW_TYPE_DOCK",
"_NET_WM_WINDOW_TYPE_TOOLBAR",
"_NET_WM_WINDOW_TYPE_MENU",
"_NET_WM_WINDOW_TYPE_UTILITY",
"_NET_WM_WINDOW_TYPE_SPLASH",
"_NET_WM_WINDOW_TYPE_DIALOG",
"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
"_NET_WM_WINDOW_TYPE_POPUP_MENU",
"_NET_WM_WINDOW_TYPE_TOOLTIP",
"_NET_WM_WINDOW_TYPE_NOTIFICATION",
"_NET_WM_WINDOW_TYPE_COMBO",
"_NET_WM_WINDOW_TYPE_DND",
"_NET_WM_WINDOW_TYPE_NORMAL",
"_NET_WM_STATE_MODAL",
"_NET_WM_STATE_STICKY",
"_NET_WM_STATE_MAXIMIZED_VERT",
"_NET_WM_STATE_MAXIMIZED_HORZ",
"_NET_WM_STATE_SHADED",
"_NET_WM_STATE_SKIP_TASKBAR",
"_NET_WM_STATE_SKIP_PAGER",
"_NET_WM_STATE_HIDDEN",
"_NET_WM_STATE_FULLSCREEN",
"_NET_WM_STATE_ABOVE",
"_NET_WM_STATE_BELOW",
"_NET_WM_STATE_DEMANDS_ATTENTION",
"_NET_WM_ACTION_MOVE",
"_NET_WM_ACTION_RESIZE",
"_NET_WM_ACTION_MINIMIZE",
"_NET_WM_ACTION_SHADE",
"_NET_WM_ACTION_STICK",
"_NET_WM_ACTION_MAXIMIZE_HORZ",
"_NET_WM_ACTION_MAXIMIZE_VERT",
"_NET_WM_ACTION_FULLSCREEN",
"_NET_WM_ACTION_CHANGE_DESKTOP",
"_NET_WM_ACTION_CLOSE",
"_NET_WM_ACTION_ABOVE",
"_NET_WM_ACTION_BELOW"
];
pub struct Atoms {
// TODO _NET_WM_CM_Sn atoms
pub _NET_SUPPORTED: xcb::x::Atom,
pub _NET_CLIENT_LIST: xcb::x::Atom,
pub _NET_CLIENT_LIST_STACKING: xcb::x::Atom,
pub _NET_NUMBER_OF_DESKTOPS: xcb::x::Atom,
pub _NET_DESKTOP_GEOMETRY: xcb::x::Atom,
pub _NET_DESKTOP_VIEWPORT: xcb::x::Atom,
pub _NET_CURRENT_DESKTOP: xcb::x::Atom,
pub _NET_DESKTOP_NAMES: xcb::x::Atom,
pub _NET_ACTIVE_WINDOW: xcb::x::Atom,
pub _NET_WORKAREA: xcb::x::Atom,
pub _NET_SUPPORTING_WM_CHECK: xcb::x::Atom,
pub _NET_VIRTUAL_ROOTS: xcb::x::Atom,
pub _NET_DESKTOP_LAYOUT: xcb::x::Atom,
pub _NET_SHOWING_DESKTOP: xcb::x::Atom,
pub _NET_CLOSE_WINDOW: xcb::x::Atom,
pub _NET_MOVERESIZE_WINDOW: xcb::x::Atom,
pub _NET_WM_MOVERESIZE: xcb::x::Atom,
pub _NET_RESTACK_WINDOW: xcb::x::Atom,
pub _NET_REQUEST_FRAME_EXTENTS: xcb::x::Atom,
pub _NET_WM_NAME: xcb::x::Atom,
pub _NET_WM_VISIBLE_NAME: xcb::x::Atom,
pub _NET_WM_ICON_NAME: xcb::x::Atom,
pub _NET_WM_VISIBLE_ICON_NAME: xcb::x::Atom,
pub _NET_WM_DESKTOP: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE: xcb::x::Atom,
pub _NET_WM_STATE: xcb::x::Atom,
pub _NET_WM_ALLOWED_ACTIONS: xcb::x::Atom,
pub _NET_WM_STRUT: xcb::x::Atom,
pub _NET_WM_STRUT_PARTIAL: xcb::x::Atom,
pub _NET_WM_ICON_GEOMETRY: xcb::x::Atom,
pub _NET_WM_ICON: xcb::x::Atom,
pub _NET_WM_PID: xcb::x::Atom,
pub _NET_WM_HANDLED_ICONS: xcb::x::Atom,
pub _NET_WM_USER_TIME: xcb::x::Atom,
pub _NET_WM_USER_TIME_WINDOW: xcb::x::Atom,
pub _NET_FRAME_EXTENTS: xcb::x::Atom,
pub _NET_WM_PING: xcb::x::Atom,
pub _NET_WM_SYNC_REQUEST: xcb::x::Atom,
pub _NET_WM_SYNC_REQUEST_COUNTER: xcb::x::Atom,
pub _NET_WM_FULLSCREEN_MONITORS: xcb::x::Atom,
pub _NET_WM_FULL_PLACEMENT: xcb::x::Atom,
pub UTF8_STRING: xcb::x::Atom,
pub WM_PROTOCOLS: xcb::x::Atom,
pub MANAGER: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_DESKTOP: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_DOCK: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_TOOLBAR: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_MENU: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_UTILITY: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_SPLASH: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_DIALOG: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_DROPDOWN_MENU: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_POPUP_MENU: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_TOOLTIP: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_NOTIFICATION: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_COMBO: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_DND: xcb::x::Atom,
pub _NET_WM_WINDOW_TYPE_NORMAL: xcb::x::Atom,
pub _NET_WM_STATE_MODAL: xcb::x::Atom,
pub _NET_WM_STATE_STICKY: xcb::x::Atom,
pub _NET_WM_STATE_MAXIMIZED_VERT: xcb::x::Atom,
pub _NET_WM_STATE_MAXIMIZED_HORZ: xcb::x::Atom,
pub _NET_WM_STATE_SHADED: xcb::x::Atom,
pub _NET_WM_STATE_SKIP_TASKBAR: xcb::x::Atom,
pub _NET_WM_STATE_SKIP_PAGER: xcb::x::Atom,
pub _NET_WM_STATE_HIDDEN: xcb::x::Atom,
pub _NET_WM_STATE_FULLSCREEN: xcb::x::Atom,
pub _NET_WM_STATE_ABOVE: xcb::x::Atom,
pub _NET_WM_STATE_BELOW: xcb::x::Atom,
pub _NET_WM_STATE_DEMANDS_ATTENTION: xcb::x::Atom,
pub _NET_WM_ACTION_MOVE: xcb::x::Atom,
pub _NET_WM_ACTION_RESIZE: xcb::x::Atom,
pub _NET_WM_ACTION_MINIMIZE: xcb::x::Atom,
pub _NET_WM_ACTION_SHADE: xcb::x::Atom,
pub _NET_WM_ACTION_STICK: xcb::x::Atom,
pub _NET_WM_ACTION_MAXIMIZE_HORZ: xcb::x::Atom,
pub _NET_WM_ACTION_MAXIMIZE_VERT: xcb::x::Atom,
pub _NET_WM_ACTION_FULLSCREEN: xcb::x::Atom,
pub _NET_WM_ACTION_CHANGE_DESKTOP: xcb::x::Atom,
pub _NET_WM_ACTION_CLOSE: xcb::x::Atom,
pub _NET_WM_ACTION_ABOVE: xcb::x::Atom,
pub _NET_WM_ACTION_BELOW: xcb::x::Atom
}
impl Atoms {
pub (crate) fn intern(con: &xcb::Connection) -> Atoms {
let mut cookies: HashMap<&'static str, xcb::x::InternAtomCookie> = HashMap::new();
for atom in ATOM_NAMES {
let intern_atom = xcb::x::InternAtom {
only_if_exists: false,
name: atom.as_bytes(),
};
cookies.insert(atom, con.send_request(&intern_atom));
}
let interned_atoms: HashMap<&'static str, xcb::x::Atom> = cookies.into_iter()
.map(|(atom_name, cookie)| (atom_name, con.wait_for_reply(cookie).unwrap()))
.map(|(atom_name, reply)| (atom_name, reply.atom()))
.collect();
Atoms::from_interned_atoms(interned_atoms)
}
fn from_interned_atoms(mut atoms: HashMap<&'static str, xcb::x::Atom>) -> Atoms {
Atoms {
_NET_SUPPORTED: atoms.remove("_NET_SUPPORTED").unwrap(),
_NET_CLIENT_LIST: atoms.remove("_NET_CLIENT_LIST").unwrap(),
_NET_CLIENT_LIST_STACKING: atoms.remove("_NET_CLIENT_LIST_STACKING").unwrap(),
_NET_NUMBER_OF_DESKTOPS: atoms.remove("_NET_NUMBER_OF_DESKTOPS").unwrap(),
_NET_DESKTOP_GEOMETRY: atoms.remove("_NET_DESKTOP_GEOMETRY").unwrap(),
_NET_DESKTOP_VIEWPORT: atoms.remove("_NET_DESKTOP_VIEWPORT").unwrap(),
_NET_CURRENT_DESKTOP: atoms.remove("_NET_CURRENT_DESKTOP").unwrap(),
_NET_DESKTOP_NAMES: atoms.remove("_NET_DESKTOP_NAMES").unwrap(),
_NET_ACTIVE_WINDOW: atoms.remove("_NET_ACTIVE_WINDOW").unwrap(),
_NET_WORKAREA: atoms.remove("_NET_WORKAREA").unwrap(),
_NET_SUPPORTING_WM_CHECK: atoms.remove("_NET_SUPPORTING_WM_CHECK").unwrap(),
_NET_VIRTUAL_ROOTS: atoms.remove("_NET_VIRTUAL_ROOTS").unwrap(),
_NET_DESKTOP_LAYOUT: atoms.remove("_NET_DESKTOP_LAYOUT").unwrap(),
_NET_SHOWING_DESKTOP: atoms.remove("_NET_SHOWING_DESKTOP").unwrap(),
_NET_CLOSE_WINDOW: atoms.remove("_NET_CLOSE_WINDOW").unwrap(),
_NET_MOVERESIZE_WINDOW: atoms.remove("_NET_MOVERESIZE_WINDOW").unwrap(),
_NET_WM_MOVERESIZE: atoms.remove("_NET_WM_MOVERESIZE").unwrap(),
_NET_RESTACK_WINDOW: atoms.remove("_NET_RESTACK_WINDOW").unwrap(),
_NET_REQUEST_FRAME_EXTENTS: atoms.remove("_NET_REQUEST_FRAME_EXTENTS").unwrap(),
_NET_WM_NAME: atoms.remove("_NET_WM_NAME").unwrap(),
_NET_WM_VISIBLE_NAME: atoms.remove("_NET_WM_VISIBLE_NAME").unwrap(),
_NET_WM_ICON_NAME: atoms.remove("_NET_WM_ICON_NAME").unwrap(),
_NET_WM_VISIBLE_ICON_NAME: atoms.remove("_NET_WM_VISIBLE_ICON_NAME").unwrap(),
_NET_WM_DESKTOP: atoms.remove("_NET_WM_DESKTOP").unwrap(),
_NET_WM_WINDOW_TYPE: atoms.remove("_NET_WM_WINDOW_TYPE").unwrap(),
_NET_WM_STATE: atoms.remove("_NET_WM_STATE").unwrap(),
_NET_WM_ALLOWED_ACTIONS: atoms.remove("_NET_WM_ALLOWED_ACTIONS").unwrap(),
_NET_WM_STRUT: atoms.remove("_NET_WM_STRUT").unwrap(),
_NET_WM_STRUT_PARTIAL: atoms.remove("_NET_WM_STRUT_PARTIAL").unwrap(),
_NET_WM_ICON_GEOMETRY: atoms.remove("_NET_WM_ICON_GEOMETRY").unwrap(),
_NET_WM_ICON: atoms.remove("_NET_WM_ICON").unwrap(),
_NET_WM_PID: atoms.remove("_NET_WM_PID").unwrap(),
_NET_WM_HANDLED_ICONS: atoms.remove("_NET_WM_HANDLED_ICONS").unwrap(),
_NET_WM_USER_TIME: atoms.remove("_NET_WM_USER_TIME").unwrap(),
_NET_WM_USER_TIME_WINDOW: atoms.remove("_NET_WM_USER_TIME_WINDOW").unwrap(),
_NET_FRAME_EXTENTS: atoms.remove("_NET_FRAME_EXTENTS").unwrap(),
_NET_WM_PING: atoms.remove("_NET_WM_PING").unwrap(),
_NET_WM_SYNC_REQUEST: atoms.remove("_NET_WM_SYNC_REQUEST").unwrap(),
_NET_WM_SYNC_REQUEST_COUNTER: atoms.remove("_NET_WM_SYNC_REQUEST_COUNTER").unwrap(),
_NET_WM_FULLSCREEN_MONITORS: atoms.remove("_NET_WM_FULLSCREEN_MONITORS").unwrap(),
_NET_WM_FULL_PLACEMENT: atoms.remove("_NET_WM_FULL_PLACEMENT").unwrap(),
UTF8_STRING: atoms.remove("UTF8_STRING").unwrap(),
WM_PROTOCOLS: atoms.remove("WM_PROTOCOLS").unwrap(),
MANAGER: atoms.remove("MANAGER").unwrap(),
_NET_WM_WINDOW_TYPE_DESKTOP: atoms.remove("_NET_WM_WINDOW_TYPE_DESKTOP").unwrap(),
_NET_WM_WINDOW_TYPE_DOCK: atoms.remove("_NET_WM_WINDOW_TYPE_DOCK").unwrap(),
_NET_WM_WINDOW_TYPE_TOOLBAR: atoms.remove("_NET_WM_WINDOW_TYPE_TOOLBAR").unwrap(),
_NET_WM_WINDOW_TYPE_MENU: atoms.remove("_NET_WM_WINDOW_TYPE_MENU").unwrap(),
_NET_WM_WINDOW_TYPE_UTILITY: atoms.remove("_NET_WM_WINDOW_TYPE_UTILITY").unwrap(),
_NET_WM_WINDOW_TYPE_SPLASH: atoms.remove("_NET_WM_WINDOW_TYPE_SPLASH").unwrap(),
_NET_WM_WINDOW_TYPE_DIALOG: atoms.remove("_NET_WM_WINDOW_TYPE_DIALOG").unwrap(),
_NET_WM_WINDOW_TYPE_DROPDOWN_MENU: atoms.remove("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU").unwrap(),
_NET_WM_WINDOW_TYPE_POPUP_MENU: atoms.remove("_NET_WM_WINDOW_TYPE_POPUP_MENU").unwrap(),
_NET_WM_WINDOW_TYPE_TOOLTIP: atoms.remove("_NET_WM_WINDOW_TYPE_TOOLTIP").unwrap(),
_NET_WM_WINDOW_TYPE_NOTIFICATION: atoms.remove("_NET_WM_WINDOW_TYPE_NOTIFICATION").unwrap(),
_NET_WM_WINDOW_TYPE_COMBO: atoms.remove("_NET_WM_WINDOW_TYPE_COMBO").unwrap(),
_NET_WM_WINDOW_TYPE_DND: atoms.remove("_NET_WM_WINDOW_TYPE_DND").unwrap(),
_NET_WM_WINDOW_TYPE_NORMAL: atoms.remove("_NET_WM_WINDOW_TYPE_NORMAL").unwrap(),
_NET_WM_STATE_MODAL: atoms.remove("_NET_WM_STATE_MODAL").unwrap(),
_NET_WM_STATE_STICKY: atoms.remove("_NET_WM_STATE_STICKY").unwrap(),
_NET_WM_STATE_MAXIMIZED_VERT: atoms.remove("_NET_WM_STATE_MAXIMIZED_VERT").unwrap(),
_NET_WM_STATE_MAXIMIZED_HORZ: atoms.remove("_NET_WM_STATE_MAXIMIZED_HORZ").unwrap(),
_NET_WM_STATE_SHADED: atoms.remove("_NET_WM_STATE_SHADED").unwrap(),
_NET_WM_STATE_SKIP_TASKBAR: atoms.remove("_NET_WM_STATE_SKIP_TASKBAR").unwrap(),
_NET_WM_STATE_SKIP_PAGER: atoms.remove("_NET_WM_STATE_SKIP_PAGER").unwrap(),
_NET_WM_STATE_HIDDEN: atoms.remove("_NET_WM_STATE_HIDDEN").unwrap(),
_NET_WM_STATE_FULLSCREEN: atoms.remove("_NET_WM_STATE_FULLSCREEN").unwrap(),
_NET_WM_STATE_ABOVE: atoms.remove("_NET_WM_STATE_ABOVE").unwrap(),
_NET_WM_STATE_BELOW: atoms.remove("_NET_WM_STATE_BELOW").unwrap(),
_NET_WM_STATE_DEMANDS_ATTENTION: atoms.remove("_NET_WM_STATE_DEMANDS_ATTENTION").unwrap(),
_NET_WM_ACTION_MOVE: atoms.remove("_NET_WM_ACTION_MOVE").unwrap(),
_NET_WM_ACTION_RESIZE: atoms.remove("_NET_WM_ACTION_RESIZE").unwrap(),
_NET_WM_ACTION_MINIMIZE: atoms.remove("_NET_WM_ACTION_MINIMIZE").unwrap(),
_NET_WM_ACTION_SHADE: atoms.remove("_NET_WM_ACTION_SHADE").unwrap(),
_NET_WM_ACTION_STICK: atoms.remove("_NET_WM_ACTION_STICK").unwrap(),
_NET_WM_ACTION_MAXIMIZE_HORZ: atoms.remove("_NET_WM_ACTION_MAXIMIZE_HORZ").unwrap(),
_NET_WM_ACTION_MAXIMIZE_VERT: atoms.remove("_NET_WM_ACTION_MAXIMIZE_VERT").unwrap(),
_NET_WM_ACTION_FULLSCREEN: atoms.remove("_NET_WM_ACTION_FULLSCREEN").unwrap(),
_NET_WM_ACTION_CHANGE_DESKTOP: atoms.remove("_NET_WM_ACTION_CHANGE_DESKTOP").unwrap(),
_NET_WM_ACTION_CLOSE: atoms.remove("_NET_WM_ACTION_CLOSE").unwrap(),
_NET_WM_ACTION_ABOVE: atoms.remove("_NET_WM_ACTION_ABOVE").unwrap(),
_NET_WM_ACTION_BELOW: atoms.remove("_NET_WM_ACTION_BELOW").unwrap(),
}
}
}

133
src/ewmh/ewmh.rs Normal file
View file

@ -0,0 +1,133 @@
use xcb::x::{Atom, GetPropertyReply};
use crate::ewmh::proto_traits::{EwmhCookie, EwmhRequest};
use super::atoms::Atoms;
pub struct Connection<'a> {
pub(crate) con: &'a xcb::Connection,
pub atoms: Atoms,
}
impl<'a> Connection<'a> {
pub fn connect(xcb_con: &'a xcb::Connection) -> Connection<'a> {
Connection {
atoms: Atoms::intern(xcb_con),
con: xcb_con,
}
}
fn send_client_message(
&self,
window: xcb::x::Window,
dest: xcb::x::Window,
atom: xcb::x::Atom,
data: xcb::x::ClientMessageData,
) -> xcb::VoidCookie {
let event = xcb::x::ClientMessageEvent::new(window, atom, data);
let request = xcb::x::SendEvent {
propagate: false,
destination: xcb::x::SendEventDest::Window(dest),
event_mask: xcb::x::EventMask::SUBSTRUCTURE_NOTIFY
| xcb::x::EventMask::SUBSTRUCTURE_REDIRECT,
event: &event,
};
self.con.send_request(&request)
}
fn send_request<R: EwmhRequest>(&self, request: R) -> R::Cookie {
request.send_request(self)
}
fn wait_for_reply<C: EwmhCookie>(&self, cookie: C) -> C::Reply {
cookie.reply(self)
}
}
#[cfg(test)]
mod tests {
#[test]
fn supported_atoms_list() {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::proto::GetNetSupportedRequest {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
for atom in reply.value {
let cookie = xcb_con.send_request(&xcb::x::GetAtomName { atom: atom });
println!("{}", xcb_con.wait_for_reply(cookie).unwrap().name());
}
}
#[test]
fn client_list() {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::proto::GetNetClientListRequest {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn number_of_desktops() {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::proto::GetNetNumberOfDesktopsRequest {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn desktop_geometry() {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::proto::GetNetDesktopGeometryRequest {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn desktop_viewport() {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::proto::GetNetDesktopViewportRequest {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn desktop_current() {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::proto::GetNetCurrentDesktopRequest {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn desktop_names() {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::proto::GetNetDesktopNamesRequest {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
}

4
src/ewmh/mod.rs Normal file
View file

@ -0,0 +1,4 @@
mod ewmh;
mod atoms;
mod proto;
mod proto_traits;

228
src/ewmh/proto.rs Normal file
View file

@ -0,0 +1,228 @@
use crate::ewmh::ewmh::Connection;
use crate::ewmh::proto_traits::{EwmhCookie, EwmhCookieUnchecked, EwmhRequest, EwmhRequestData};
use crate::{request, root_request};
root_request! {
GetNetSupportedRequest,
_NET_SUPPORTED,
ATOM_ATOM,
GetNetSupportedCookie,
GetNetSupportedCookieUnchecked,
GetNetSupportedReply
}
#[derive(Debug)]
pub struct GetNetSupportedReply {
pub value: Vec<xcb::x::Atom>,
}
impl From<xcb::x::GetPropertyReply> for GetNetSupportedReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNetSupportedReply {
value: reply.value().into(),
}
}
}
root_request! {
GetNetClientListRequest,
_NET_CLIENT_LIST,
ATOM_WINDOW,
GetNetClientListCookie,
GetNetClientListCookieUnchecked,
GetNetClientListReply
}
#[derive(Debug)]
pub struct GetNetClientListReply {
pub value: Vec<xcb::x::Window>,
}
impl From<xcb::x::GetPropertyReply> for GetNetClientListReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNetClientListReply {
value: reply.value().into(),
}
}
}
root_request! {
GetNetClientListStackingRequest,
_NET_CLIENT_LIST_STACKING,
ATOM_WINDOW,
GetNetClientListStackingCookie,
GetNetClientListStackingCookieUnchecked,
GetNetClientListStackingReply
}
#[derive(Debug)]
pub struct GetNetClientListStackingReply {
pub value: Vec<xcb::x::Window>,
}
impl From<xcb::x::GetPropertyReply> for GetNetClientListStackingReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNetClientListStackingReply {
value: reply.value().into(),
}
}
}
root_request! {
GetNetNumberOfDesktopsRequest,
_NET_NUMBER_OF_DESKTOPS,
ATOM_CARDINAL,
GetNetNumberOfDesktopsCookie,
GetNetNumberOfDesktopsCookieUnchecked,
GetNetNumberOfDesktopsReply
}
#[derive(Debug)]
pub struct GetNetNumberOfDesktopsReply {
pub value: u32,
}
impl From<xcb::x::GetPropertyReply> for GetNetNumberOfDesktopsReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNetNumberOfDesktopsReply {
value: reply.value::<u32>()[0],
}
}
}
root_request! {
GetNetDesktopGeometryRequest,
_NET_DESKTOP_GEOMETRY,
ATOM_CARDINAL,
GetNetDesktopGeometryCookie,
GetNetDesktopGeometryCookieUnchecked,
GetNetDesktopGeometryReply
}
#[derive(Debug)]
pub struct GetNetDesktopGeometryReply {
pub width: u32,
pub height: u32,
}
impl From<xcb::x::GetPropertyReply> for GetNetDesktopGeometryReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNetDesktopGeometryReply {
width: reply.value::<u32>()[0],
height: reply.value::<u32>()[1],
}
}
}
root_request! {
GetNetDesktopViewportRequest,
_NET_DESKTOP_VIEWPORT,
ATOM_CARDINAL,
GetNetDesktopViewportCookie,
GetNetDesktopViewportCookieUnchecked,
GetNetDesktopViewportReply
}
#[derive(Debug)]
pub struct GetNetDesktopViewportReply {
pub x: u32,
pub y: u32,
}
impl From<xcb::x::GetPropertyReply> for GetNetDesktopViewportReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNetDesktopViewportReply {
x: reply.value::<u32>()[0],
y: reply.value::<u32>()[1],
}
}
}
root_request! {
GetNetCurrentDesktopRequest,
_NET_CURRENT_DESKTOP,
ATOM_CARDINAL,
GetNetCurrentDesktopCookie,
GetNetCurrentDesktopCookieUnchecked,
GetNetCurrentDesktopReply
}
#[derive(Debug)]
pub struct GetNetCurrentDesktopReply {
pub desktop: u32,
}
impl From<xcb::x::GetPropertyReply> for GetNetCurrentDesktopReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNetCurrentDesktopReply {
desktop: reply.value::<u32>()[0],
}
}
}
request! {
GetNetDesktopNamesRequest,
GetNetDesktopNamesCookie,
GetNetDesktopNamesCookieUnchecked,
GetNetDesktopNamesReply
}
#[derive(Debug)]
pub struct GetNetDesktopNamesReply {
pub value: Vec<String>,
}
impl EwmhRequestData for GetNetDesktopNamesRequest {
fn get_request_data(&self, con: &Connection) -> xcb::x::GetProperty {
xcb::x::GetProperty {
delete: false,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms._NET_DESKTOP_NAMES,
r#type: con.atoms.UTF8_STRING,
long_offset: 0,
long_length: u32::MAX,
}
}
}
impl From<xcb::x::GetPropertyReply> for GetNetDesktopNamesReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
let mut vals = vec![];
let mut buf = vec![];
for b in reply.value::<u8>() {
if *b != 0x00 {
buf.push(*b)
} else if !buf.is_empty() {
vals.push(String::from_utf8(buf.clone()).unwrap());
buf.clear();
} else {
buf.clear();
}
}
GetNetDesktopNamesReply { value: vals }
}
}
root_request! {
GetNetActiveWindowRequest,
_NET_ACTIVE_WINDOW,
ATOM_WINDOW,
GetNetActiveWindowCookie,
GetNetActiveWindowCookieUnchecked,
GetNetActiveWindowReply
}
#[derive(Debug)]
pub struct GetNetActiveWindowReply {
pub value: xcb::x::Window,
}
impl From<xcb::x::GetPropertyReply> for GetNetActiveWindowReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNetActiveWindowReply {
value: reply.value::<xcb::x::Window>()[0],
}
}
}

93
src/ewmh/proto_traits.rs Normal file
View file

@ -0,0 +1,93 @@
use crate::ewmh::ewmh::Connection;
pub(crate) trait EwmhRequest: EwmhRequestData {
type Cookie;
type CookieUnchecked;
fn send_request(&self, con: &Connection) -> Self::Cookie;
fn send_request_unchecked(&self, con: &Connection) -> Self::CookieUnchecked;
}
pub(crate) trait EwmhRequestData {
fn get_request_data(&self, con: &Connection) -> xcb::x::GetProperty;
}
pub(crate) trait EwmhCookie {
type Reply: From<xcb::x::GetPropertyReply>;
fn reply(self, con: &Connection) -> Self::Reply;
}
pub(crate) trait EwmhCookieUnchecked {
type Reply;
fn reply(self, con: &Connection) -> Option<Self::Reply>;
}
#[macro_export]
macro_rules! request {
($req: ident, $cookie: ident, $cookie_unchecked: ident, $reply: ident) => {
pub struct $req {}
pub struct $cookie {
cookie: xcb::x::GetPropertyCookie,
}
pub struct $cookie_unchecked {
cookie: xcb::x::GetPropertyCookieUnchecked,
}
impl EwmhRequest for $req {
type Cookie = $cookie;
type CookieUnchecked = $cookie_unchecked;
fn send_request(&self, con: &Connection) -> Self::Cookie {
$cookie {
cookie: con.con.send_request(&self.get_request_data(con)),
}
}
fn send_request_unchecked(&self, con: &Connection) -> Self::CookieUnchecked {
$cookie_unchecked {
cookie: con.con.send_request_unchecked(&self.get_request_data(con)),
}
}
}
impl EwmhCookie for $cookie {
type Reply = $reply;
fn reply(self, con: &Connection) -> Self::Reply {
let reply = con.con.wait_for_reply(self.cookie).unwrap();
reply.into()
}
}
impl EwmhCookieUnchecked for $cookie_unchecked {
type Reply = $reply;
fn reply(self, con: &Connection) -> Option<Self::Reply> {
con.con
.wait_for_reply_unchecked(self.cookie)
.unwrap()
.map(|reply| reply.into())
}
}
};
}
#[macro_export]
macro_rules! root_request {
($req: ident, $prop: ident, $type: ident, $cookie: ident, $cookie_unchecked: ident, $reply: ident) => {
request! {$req, $cookie, $cookie_unchecked, $reply}
impl EwmhRequestData for $req {
fn get_request_data(&self, con: &Connection) -> xcb::x::GetProperty {
xcb::x::GetProperty {
delete: false,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms.$prop,
r#type: xcb::x::$type,
long_offset: 0,
long_length: u32::MAX,
}
}
}
};
}

10
src/lib.rs Normal file
View file

@ -0,0 +1,10 @@
mod ewmh;
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}