ewmh root window properties

This commit is contained in:
Armin Friedl 2022-05-13 01:08:56 +02:00
parent 36add615d9
commit 48a8fca18f
8 changed files with 867 additions and 256 deletions

View file

@ -4,14 +4,12 @@ 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"]
keywords = ["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"

View file

@ -1,6 +1,6 @@
use std::collections::HashMap;
const ATOM_NAMES: [&'static str; 82] = [
const ATOM_NAMES: [&str; 82] = [
"_NET_SUPPORTED",
"_NET_CLIENT_LIST",
"_NET_CLIENT_LIST_STACKING",
@ -85,6 +85,7 @@ const ATOM_NAMES: [&'static str; 82] = [
"_NET_WM_ACTION_BELOW"
];
#[allow(non_snake_case)]
pub struct Atoms {
// TODO _NET_WM_CM_Sn atoms
pub _NET_SUPPORTED: xcb::x::Atom,

View file

@ -1,5 +1,3 @@
use xcb::x::{Atom, GetPropertyReply};
use crate::ewmh::proto_traits::{EwmhCookie, EwmhRequest};
use super::atoms::Atoms;
@ -9,6 +7,7 @@ pub struct Connection<'a> {
pub atoms: Atoms,
}
#[allow(dead_code)]
impl<'a> Connection<'a> {
pub fn connect(xcb_con: &'a xcb::Connection) -> Connection<'a> {
Connection {
@ -37,7 +36,7 @@ impl<'a> Connection<'a> {
self.con.send_request(&request)
}
fn send_request<R: EwmhRequest>(&self, request: R) -> R::Cookie {
fn send_request<R: EwmhRequest<'a>>(&self, request: R) -> R::Cookie {
request.send_request(self)
}
@ -53,12 +52,12 @@ mod tests {
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 request = crate::ewmh::proto::GetSupported::new();
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
for atom in reply.value {
for atom in reply.atoms {
let cookie = xcb_con.send_request(&xcb::x::GetAtomName { atom: atom });
println!("{}", xcb_con.wait_for_reply(cookie).unwrap().name());
@ -70,7 +69,7 @@ mod tests {
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 request = crate::ewmh::proto::GetClientList::new();
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
@ -81,7 +80,29 @@ mod tests {
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 request = crate::ewmh::proto::GetNumberOfDesktops::new();
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn set_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::SetNumberOfDesktops::new(4);
let cookie = ewmh_con.send_request(request);
let reply = xcb_con.check_request(cookie);
println!("{:?}", reply);
}
#[test]
fn number_of_desktops2() {
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::GetNumberOfDesktops::new();
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
@ -92,18 +113,51 @@ mod tests {
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 request = crate::ewmh::proto::GetNumberOfDesktops {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn set_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::SetDesktopGeometry::new(1024, 800);
let cookie = ewmh_con.send_request(request);
let reply = xcb_con.check_request(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 request = crate::ewmh::proto::GetDesktopViewport {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn set_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::SetDesktopViewport::new(200, 200);
let cookie = ewmh_con.send_request(request);
let reply = xcb_con.check_request(cookie);
println!("{:?}", reply);
}
#[test]
fn desktop_viewport2() {
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::GetDesktopViewport {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
@ -114,7 +168,7 @@ mod tests {
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 request = crate::ewmh::proto::GetCurrentDesktop {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
@ -125,9 +179,96 @@ mod tests {
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 request = crate::ewmh::proto::GetDesktopNames {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn set_active_window() {
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::GetClientList {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
let request = crate::ewmh::proto::SetActiveWindow::new(
&ewmh_con,
reply.clients.last().unwrap().to_owned(),
1,
xcb::x::CURRENT_TIME,
Option::None,
);
let cookie = ewmh_con.send_request(request);
xcb_con.check_request(cookie).unwrap();
}
#[test]
fn workarea() {
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::GetWorkarea {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn supporting_wm_check() {
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::GetSupportingWmCheck {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn virtual_roots() {
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::GetVirtualRoots {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn desktop_layout() {
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::GetDesktopLayout {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn showing_desktop() {
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::GetShowingDesktop {};
let cookie = ewmh_con.send_request(request);
let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply);
}
#[test]
fn set_showing_desktop() {
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::SetShowingDesktop::new(&ewmh_con, true);
let cookie = ewmh_con.send_request(request);
let reply = xcb_con.check_request(cookie);
println!("{:?}", reply);
}
}

View file

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

View file

@ -1,228 +0,0 @@
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],
}
}
}

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

@ -0,0 +1,4 @@
#[allow(unused)]
mod root_props;
pub use root_props::*;

View file

@ -0,0 +1,606 @@
//! Root Window Properties (and Related Messages)
//!
//! see: https://specifications.freedesktop.org/wm-spec/1.5/ar01s03.html#idm45539547193552
use xcb::{Xid, XidNew};
use crate::ewmh::ewmh::Connection;
use crate::ewmh::proto_traits::{EwmhCookie, EwmhCookieUnchecked, EwmhRequest, EwmhRequestData};
// _NET_SUPPORTED, ATOM[]/32
// {{{
ewmh_get_root_request! {
GetSupported,
_NET_SUPPORTED,
ATOM_ATOM,
GetSupportedCookie,
GetSupportedCookieUnchecked,
GetSupportedReply
}
#[derive(Debug)]
pub struct GetSupportedReply {
pub atoms: Vec<xcb::x::Atom>,
}
impl From<xcb::x::GetPropertyReply> for GetSupportedReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetSupportedReply {
atoms: reply.value().into(),
}
}
}
// }}}
// _NET_CLIENT_LIST, WINDOW[]/32
// {{{
ewmh_get_root_request! {
GetClientList,
_NET_CLIENT_LIST,
ATOM_WINDOW,
GetClientListCookie,
GetClientListCookieUnchecked,
GetClientListReply
}
#[derive(Debug)]
pub struct GetClientListReply {
pub clients: Vec<xcb::x::Window>,
}
impl From<xcb::x::GetPropertyReply> for GetClientListReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetClientListReply {
clients: reply.value().into(),
}
}
}
// }}}
// _NET_CLIENT_LIST_STACKING, WINDOW[]/32
// {{{
ewmh_get_root_request! {
GetClientListStacking,
_NET_CLIENT_LIST_STACKING,
ATOM_WINDOW,
GetClientListStackingCookie,
GetClientListStackingCookieUnchecked,
GetClientListStackingReply
}
#[derive(Debug)]
pub struct GetClientListStackingReply {
pub clients: Vec<xcb::x::Window>,
}
impl From<xcb::x::GetPropertyReply> for GetClientListStackingReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetClientListStackingReply {
clients: reply.value().into(),
}
}
}
// }}}
// _NET_NUMBER_OF_DESKTOPS, CARDINAL/32
// {{{
ewmh_get_root_request! {
GetNumberOfDesktops,
_NET_NUMBER_OF_DESKTOPS,
ATOM_CARDINAL,
GetNumberOfDesktopsCookie,
GetNumberOfDesktopsCookieUnchecked,
GetNumberOfDesktopsReply
}
#[derive(Debug)]
pub struct GetNumberOfDesktopsReply {
pub desktops: u32,
}
impl From<xcb::x::GetPropertyReply> for GetNumberOfDesktopsReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetNumberOfDesktopsReply {
desktops: reply.value::<u32>()[0],
}
}
}
pub struct SetNumberOfDesktops {
data: [u32; 1],
}
impl SetNumberOfDesktops {
pub fn new(desktops: u32) -> SetNumberOfDesktops {
SetNumberOfDesktops { data: [desktops] }
}
}
ewmh_set_root_request! {
SetNumberOfDesktops,
_NET_NUMBER_OF_DESKTOPS,
ATOM_CARDINAL
}
// }}}
// _NET_DESKTOP_GEOMETRY width, height, CARDINAL[2]/32
// {{{
ewmh_get_root_request! {
GetDesktopGeometry,
_NET_DESKTOP_GEOMETRY,
ATOM_CARDINAL,
GetDesktopGeometryCookie,
GetDesktopGeometryCookieUnchecked,
GetDesktopGeometryReply
}
#[derive(Debug)]
pub struct GetDesktopGeometryReply {
pub width: u32,
pub height: u32,
}
impl From<xcb::x::GetPropertyReply> for GetDesktopGeometryReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetDesktopGeometryReply {
width: reply.value::<u32>()[0],
height: reply.value::<u32>()[0],
}
}
}
pub struct SetDesktopGeometry {
data: [u32; 2],
}
impl SetDesktopGeometry {
pub fn new(width: u32, height: u32) -> SetDesktopGeometry {
SetDesktopGeometry {
data: [width, height],
}
}
}
ewmh_set_root_request! {
SetDesktopGeometry,
_NET_DESKTOP_GEOMETRY,
ATOM_CARDINAL
}
// }}}
// _NET_DESTKOP_VIEWPORT x, y, CARDINAL[][2]/32
// {{{
ewmh_get_root_request! {
GetDesktopViewport,
_NET_DESKTOP_VIEWPORT,
ATOM_CARDINAL,
GetDesktopViewportCookie,
GetDesktopViewportCookieUnchecked,
GetDesktopViewportReply
}
#[derive(Debug)]
pub struct GetDesktopViewportReply {
pub x: u32,
pub y: u32,
}
impl From<xcb::x::GetPropertyReply> for GetDesktopViewportReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetDesktopViewportReply {
x: reply.value::<u32>()[0],
y: reply.value::<u32>()[1],
}
}
}
pub struct SetDesktopViewport {
data: [u32; 2],
}
impl SetDesktopViewport {
pub fn new(x: u32, y: u32) -> SetDesktopViewport {
SetDesktopViewport { data: [x, y] }
}
}
ewmh_set_root_request! {
SetDesktopViewport,
_NET_DESKTOP_VIEWPORT,
ATOM_CARDINAL
}
// }}}
// _NET_CURRENT_DESKTOP desktop, CARDINAL/32
// {{{
ewmh_get_root_request! {
GetCurrentDesktop,
_NET_CURRENT_DESKTOP,
ATOM_CARDINAL,
GetCurrentDesktopCookie,
GetCurrentDesktopCookieUnchecked,
GetCurrentDesktopReply
}
#[derive(Debug)]
pub struct GetCurrentDesktopReply {
pub desktop: u32,
}
impl From<xcb::x::GetPropertyReply> for GetCurrentDesktopReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetCurrentDesktopReply {
desktop: reply.value::<u32>()[0],
}
}
}
pub struct SetCurrentDesktop {
data: [u32; 2],
}
impl SetCurrentDesktop {
pub fn new(new_index: u32, timestamp: u32) -> SetCurrentDesktop {
SetCurrentDesktop {
data: [new_index, timestamp],
}
}
}
ewmh_set_root_request! {
SetCurrentDesktop,
_NET_CURRENT_DESKTOP,
ATOM_CARDINAL
}
// }}}
// _NET_DESKTOP_NAMES desktop, UTF8_STRING[]
// {{{
ewmh_get_root_request! {
GetDesktopNames,
_NET_DESKTOP_NAMES,
UTF8_STRING,
GetDesktopNamesCookie,
GetDesktopNamesCookieUnchecked,
GetDesktopNamesReply
}
#[derive(Debug)]
pub struct GetDesktopNamesReply {
pub desktop_names: Vec<String>,
}
impl From<xcb::x::GetPropertyReply> for GetDesktopNamesReply {
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();
}
}
GetDesktopNamesReply {
desktop_names: vals,
}
}
}
pub struct SetDesktopNames {
data: Vec<u8>,
}
impl SetDesktopNames {
pub fn new(new_names: Vec<&str>) -> SetDesktopNames {
let mut data: Vec<u8> = vec![];
// flatten `new_names` into a continuous array of bytes
for name in new_names {
let mut bname = name.as_bytes().to_owned();
bname.push(0b00);
data.extend(bname)
}
SetDesktopNames { data }
}
}
ewmh_set_root_request! {
SetDesktopNames,
_NET_DESKTOP_NAMES,
UTF8_STRING
}
// }}}
// _NET_ACTIVE_WINDOW, WINDOW/32
// {{{
ewmh_get_root_request! {
GetActiveWindow,
_NET_ACTIVE_WINDOW,
ATOM_WINDOW,
GetActiveWindowCookie,
GetActiveWindowCookieUnchecked,
GetActiveWindowReply
}
#[derive(Debug)]
pub struct GetActiveWindowReply {
pub value: xcb::x::Window,
}
impl From<xcb::x::GetPropertyReply> for GetActiveWindowReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetActiveWindowReply {
value: reply.value::<xcb::x::Window>()[0],
}
}
}
pub struct SetActiveWindow {
client_message: xcb::x::ClientMessageEvent,
}
impl SetActiveWindow {
pub fn new(
connection: &Connection,
window: xcb::x::Window,
source_indication: u32,
timestamp: u32,
requestor_window: Option<xcb::x::Window>,
) -> SetActiveWindow {
let data = [
source_indication,
timestamp,
requestor_window.map_or(0, |w| w.resource_id()),
0x00,
0x00,
];
let client_message = xcb::x::ClientMessageEvent::new(
window,
connection.atoms._NET_ACTIVE_WINDOW,
xcb::x::ClientMessageData::Data32(data),
);
SetActiveWindow { client_message }
}
}
impl<'a> EwmhRequest<'a> for SetActiveWindow {
type Cookie = xcb::VoidCookieChecked;
type CookieUnchecked = xcb::VoidCookie;
fn send_request(&self, con: &Connection) -> Self::Cookie {
con.con.send_request_checked(&self.get_request_data(con))
}
fn send_request_unchecked(&self, con: &Connection) -> Self::CookieUnchecked {
con.con.send_request(&self.get_request_data(con))
}
}
impl<'a> EwmhRequestData<'a> for SetActiveWindow {
type Request = xcb::x::SendEvent<'a, xcb::x::ClientMessageEvent>;
fn get_request_data(
&'a self,
con: &Connection,
) -> xcb::x::SendEvent<'a, xcb::x::ClientMessageEvent> {
xcb::x::SendEvent {
propagate: false,
destination: xcb::x::SendEventDest::Window(
con.con.get_setup().roots().next().unwrap().root(),
),
event_mask: xcb::x::EventMask::SUBSTRUCTURE_NOTIFY
| xcb::x::EventMask::SUBSTRUCTURE_REDIRECT,
event: &self.client_message,
}
}
}
// }}}
// _NET_WORKAREA, x, y, width, height, CARDINAL[][4]/32
// {{{
ewmh_get_root_request! {
GetWorkarea,
_NET_WORKAREA,
ATOM_CARDINAL,
GetWorkareaCookie,
GetWorkareaCookieUnchecked,
GetWorkareaReply
}
#[derive(Debug)]
pub struct GetWorkareaReply {
pub x: u32,
pub y: u32,
pub width: u32,
pub height: u32,
}
impl From<xcb::x::GetPropertyReply> for GetWorkareaReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetWorkareaReply {
x: reply.value::<u32>()[0],
y: reply.value::<u32>()[1],
width: reply.value::<u32>()[2],
height: reply.value::<u32>()[3],
}
}
}
// }}}
// _NET_SUPPORTING_WM_CHECK, WINDOW/32
// {{{
ewmh_get_root_request! {
GetSupportingWmCheck,
_NET_SUPPORTING_WM_CHECK,
ATOM_WINDOW,
GetSupportingWmCheckCookie,
GetSupportingWmCheckCookieUnchecked,
GetSupportingWmCheckReply
}
#[derive(Debug)]
pub struct GetSupportingWmCheckReply {
pub window: xcb::x::Window,
}
impl From<xcb::x::GetPropertyReply> for GetSupportingWmCheckReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetSupportingWmCheckReply {
window: unsafe { xcb::x::Window::new(reply.value::<u32>()[0]) },
}
}
}
// }}}
// _NET_VIRTUAL_ROOTS, WINDOW/32
// {{{
ewmh_get_root_request! {
GetVirtualRoots,
_NET_VIRTUAL_ROOTS,
ATOM_WINDOW,
GetVirtualRootsCookie,
GetVirtualRootsCookieUnchecked,
GetVirtualRootsReply
}
#[derive(Debug)]
pub struct GetVirtualRootsReply {
pub window: xcb::x::Window,
}
impl From<xcb::x::GetPropertyReply> for GetVirtualRootsReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetVirtualRootsReply {
window: unsafe { xcb::x::Window::new(reply.value::<u32>()[0]) },
}
}
}
// }}}
// _NET_DESKTOP_LAYOUT, orientation, columns, rows, starting_corner, CARDINAL[4]/32
// {{{
ewmh_get_root_request! {
GetDesktopLayout,
_NET_DESKTOP_LAYOUT,
ATOM_CARDINAL,
GetDesktopLayoutCookie,
GetDesktopLayoutCookieUnchecked,
GetDesktopLayoutReply
}
#[derive(Debug)]
pub struct GetDesktopLayoutReply {
orientation: u32,
columns: u32,
rows: u32,
starting_corner: u32,
}
impl From<xcb::x::GetPropertyReply> for GetDesktopLayoutReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetDesktopLayoutReply {
orientation: reply.value::<u32>()[0],
columns: reply.value::<u32>()[1],
rows: reply.value::<u32>()[2],
starting_corner: reply.value::<u32>()[3],
}
}
}
// }}}
// _NET_SHOWING_DESKTOP desktop, CARDINAL/32
// {{{
ewmh_get_root_request! {
GetShowingDesktop,
_NET_SHOWING_DESKTOP,
ATOM_CARDINAL,
GetShowingDesktopCookie,
GetShowingDesktopCookieUnchecked,
GetShowingDesktopReply
}
#[derive(Debug)]
pub struct GetShowingDesktopReply {
showing_desktop: bool,
}
impl From<xcb::x::GetPropertyReply> for GetShowingDesktopReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetShowingDesktopReply {
showing_desktop: match reply.value::<u32>()[0] {
0 => false,
1 => true,
_ => unreachable!(),
},
}
}
}
pub struct SetShowingDesktop {
client_message: xcb::x::ClientMessageEvent,
}
impl SetShowingDesktop {
pub fn new(connection: &Connection, show_desktop: bool) -> SetShowingDesktop {
let data = match show_desktop {
false => 0 as u32,
true => 1 as u32,
};
let client_message = xcb::x::ClientMessageEvent::new(
connection.con.get_setup().roots().next().unwrap().root(),
connection.atoms._NET_SHOWING_DESKTOP,
xcb::x::ClientMessageData::Data32([data, 0x00, 0x00, 0x00, 0x00]),
);
SetShowingDesktop { client_message }
}
}
impl<'a> EwmhRequest<'a> for SetShowingDesktop {
type Cookie = xcb::VoidCookieChecked;
type CookieUnchecked = xcb::VoidCookie;
fn send_request(&self, con: &Connection) -> Self::Cookie {
con.con.send_request_checked(&self.get_request_data(con))
}
fn send_request_unchecked(&self, con: &Connection) -> Self::CookieUnchecked {
con.con.send_request(&self.get_request_data(con))
}
}
impl<'a> EwmhRequestData<'a> for SetShowingDesktop {
type Request = xcb::x::SendEvent<'a, xcb::x::ClientMessageEvent>;
fn get_request_data(
&'a self,
con: &Connection,
) -> xcb::x::SendEvent<'a, xcb::x::ClientMessageEvent> {
xcb::x::SendEvent {
propagate: false,
destination: xcb::x::SendEventDest::Window(
con.con.get_setup().roots().next().unwrap().root(),
),
event_mask: xcb::x::EventMask::SUBSTRUCTURE_NOTIFY
| xcb::x::EventMask::SUBSTRUCTURE_REDIRECT,
event: &self.client_message,
}
}
}
// }}}

View file

@ -1,6 +1,8 @@
#![macro_use]
use crate::ewmh::ewmh::Connection;
pub(crate) trait EwmhRequest: EwmhRequestData {
pub(crate) trait EwmhRequest<'a>: EwmhRequestData<'a> {
type Cookie;
type CookieUnchecked;
@ -8,8 +10,9 @@ pub(crate) trait EwmhRequest: EwmhRequestData {
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 EwmhRequestData<'a> {
type Request: xcb::Request;
fn get_request_data(&'a self, con: &Connection) -> Self::Request;
}
pub(crate) trait EwmhCookie {
@ -22,10 +25,10 @@ pub(crate) trait EwmhCookieUnchecked {
fn reply(self, con: &Connection) -> Option<Self::Reply>;
}
#[macro_export]
macro_rules! request {
macro_rules! ewmh_get_request {
($req: ident, $cookie: ident, $cookie_unchecked: ident, $reply: ident) => {
pub struct $req {}
pub struct $cookie {
cookie: xcb::x::GetPropertyCookie,
}
@ -33,7 +36,13 @@ macro_rules! request {
cookie: xcb::x::GetPropertyCookieUnchecked,
}
impl EwmhRequest for $req {
impl $req {
pub fn new() -> $req {
$req {}
}
}
impl<'a> EwmhRequest<'a> for $req {
type Cookie = $cookie;
type CookieUnchecked = $cookie_unchecked;
@ -72,13 +81,33 @@ macro_rules! request {
};
}
#[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}
macro_rules! ewmh_get_root_request {
($req: ident, $prop: ident, UTF8_STRING, $cookie: ident, $cookie_unchecked: ident, $reply: ident) => {
ewmh_get_request! {$req, $cookie, $cookie_unchecked, $reply}
impl EwmhRequestData for $req {
fn get_request_data(&self, con: &Connection) -> xcb::x::GetProperty {
impl<'a> EwmhRequestData<'a> for $req {
type Request = xcb::x::GetProperty;
fn get_request_data(&'a self, con: &Connection) -> Self::Request {
xcb::x::GetProperty {
delete: false,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms.$prop,
r#type: con.atoms.UTF8_STRING,
long_offset: 0,
long_length: u32::MAX,
}
}
}
};
($req: ident, $prop: ident, $type: ident, $cookie: ident, $cookie_unchecked: ident, $reply: ident) => {
ewmh_get_request! {$req, $cookie, $cookie_unchecked, $reply}
impl<'a> EwmhRequestData<'a> for $req {
type Request = xcb::x::GetProperty;
fn get_request_data(&'a self, con: &Connection) -> Self::Request {
xcb::x::GetProperty {
delete: false,
window: con.con.get_setup().roots().next().unwrap().root(),
@ -91,3 +120,63 @@ macro_rules! root_request {
}
};
}
macro_rules! ewmh_set_root_request {
($req: ident, $prop: ident, UTF8_STRING) => {
impl<'a> EwmhRequest<'a> for $req {
type Cookie = xcb::VoidCookieChecked;
type CookieUnchecked = xcb::VoidCookie;
fn send_request(&self, con: &Connection) -> Self::Cookie {
con.con.send_request_checked(&self.get_request_data(con))
}
fn send_request_unchecked(&self, con: &Connection) -> Self::CookieUnchecked {
con.con.send_request(&self.get_request_data(con))
}
}
impl<'a> EwmhRequestData<'a> for $req {
type Request = xcb::x::ChangeProperty<'a, u8>;
fn get_request_data(&'a self, con: &Connection) -> Self::Request {
xcb::x::ChangeProperty {
mode: xcb::x::PropMode::Replace,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms.$prop,
r#type: con.atoms.UTF8_STRING,
data: self.data.as_slice(),
}
}
}
};
($req: ident, $prop: ident, $type: ident) => {
impl<'a> EwmhRequest<'a> for $req {
type Cookie = xcb::VoidCookieChecked;
type CookieUnchecked = xcb::VoidCookie;
fn send_request(&self, con: &Connection) -> Self::Cookie {
con.con.send_request_checked(&self.get_request_data(con))
}
fn send_request_unchecked(&self, con: &Connection) -> Self::CookieUnchecked {
con.con.send_request(&self.get_request_data(con))
}
}
impl<'a> EwmhRequestData<'a> for $req {
type Request = xcb::x::ChangeProperty<'a, u32>;
fn get_request_data(&'a self, con: &Connection) -> Self::Request {
xcb::x::ChangeProperty {
mode: xcb::x::PropMode::Replace,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms.$prop,
r#type: xcb::x::$type,
data: &self.data,
}
}
}
};
}