Implement new macros, restructure in sub-modules

This commit is contained in:
Armin Friedl 2022-05-18 23:10:54 +02:00
parent fe183b4c4b
commit bee554c057
7 changed files with 324 additions and 222 deletions

View file

@ -1,5 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
// TODO use xcb::atom_struct!{} for this?
const ATOM_NAMES: [&str; 82] = [ const ATOM_NAMES: [&str; 82] = [
"_NET_SUPPORTED", "_NET_SUPPORTED",
"_NET_CLIENT_LIST", "_NET_CLIENT_LIST",

View file

@ -78,7 +78,7 @@ mod tests {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0; let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::Connection::connect(&xcb_con); let ewmh_con = crate::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::GetSupported; let request = crate::ewmh::proto::GetSupported;
let cookie = ewmh_con.send_request(&request); let cookie = ewmh_con.send_request(&request);
let reply = ewmh_con.wait_for_reply(cookie); let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply); println!("{:?}", reply);
@ -205,7 +205,7 @@ mod tests {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0; let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::Connection::connect(&xcb_con); let ewmh_con = crate::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::GetDesktopNames {}; let request = crate::ewmh::proto::GetDesktopNames {};
let cookie = ewmh_con.send_request(&request); let cookie = ewmh_con.send_request(&request);
let reply = ewmh_con.wait_for_reply(cookie); let reply = ewmh_con.wait_for_reply(cookie);
println!("{:?}", reply); println!("{:?}", reply);
@ -216,7 +216,7 @@ mod tests {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0; let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::Connection::connect(&xcb_con); let ewmh_con = crate::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::SetDesktopNames::new(vec!["X", "Y", "Z"]); let request = crate::ewmh::proto::SetDesktopNames::new(vec!["A", "B", "Z"]);
let cookie = ewmh_con.send_request_checked(&request); let cookie = ewmh_con.send_request_checked(&request);
let reply = xcb_con.check_request(cookie); let reply = xcb_con.check_request(cookie);
println!("{:?}", reply); println!("{:?}", reply);
@ -303,7 +303,7 @@ mod tests {
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0; let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
let ewmh_con = crate::ewmh::Connection::connect(&xcb_con); let ewmh_con = crate::ewmh::Connection::connect(&xcb_con);
let request = crate::ewmh::SetShowingDesktop::new(&ewmh_con, true); let request = crate::ewmh::proto::SetShowingDesktop::new(&ewmh_con, true);
let cookie = ewmh_con.send_request_checked(&request); let cookie = ewmh_con.send_request_checked(&request);
let reply = xcb_con.check_request(cookie); let reply = xcb_con.check_request(cookie);
println!("{:?}", reply); println!("{:?}", reply);

View file

@ -3,11 +3,8 @@ mod traits;
mod atoms; mod atoms;
mod connection; mod connection;
mod proto;
pub use atoms::Atoms; pub use atoms::Atoms;
pub use connection::Connection; pub use connection::Connection;
pub use proto::net_desktop_names::*; pub mod proto;
pub use proto::net_showing_desktop::*;
pub use proto::net_supported::*;

237
src/ewmh/proto/macros.rs Normal file
View file

@ -0,0 +1,237 @@
macro_rules! _get_property_base {
($reply:ident, $cookie:ident, $cookie_unchecked:ident) => {
impl EwmhCookie for $cookie {
type XcbCookie = xcb::x::GetPropertyCookie;
}
impl EwmhPropertyCookieChecked for $cookie {
type Reply = $reply;
fn inner(self) -> xcb::x::GetPropertyCookie {
self.0
}
}
impl EwmhPropertyCookieUnchecked for $cookie_unchecked {
type Reply = $reply;
fn inner(self) -> xcb::x::GetPropertyCookieUnchecked {
self.0
}
}
};
}
macro_rules! _get_property_request {
(root_window, $property:ident, UTF8_STRING) => {
fn xcb_request(&self, con: &Connection) -> xcb::x::GetProperty {
xcb::x::GetProperty {
delete: false,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms.$property,
r#type: con.atoms.UTF8_STRING,
long_offset: 0,
long_length: u32::MAX,
}
}
};
(root_window, $property:ident, $xtype:ident) => {
fn xcb_request(&self, con: &Connection) -> xcb::x::GetProperty {
xcb::x::GetProperty {
delete: false,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms.$property,
r#type: xcb::x::$xtype,
long_offset: 0,
long_length: u32::MAX,
}
}
};
($window:ident, $property:ident, UTF8_STRING) => {
fn xcb_request(&self, con: &Connection) -> xcb::x::GetProperty {
xcb::x::GetProperty {
delete: false,
window: $window,
property: con.atoms.$property,
r#type: con.atoms.UTF8_STRING,
long_offset: 0,
long_length: u32::MAX,
}
}
};
($window:ident, $property:ident, $xtype:ident) => {
fn xcb_request(&self, con: &Connection) -> xcb::x::GetProperty {
xcb::x::GetProperty {
delete: false,
window: $window,
property: con.atoms.$property,
r#type: xcb::x::$xtype,
long_offset: 0,
long_length: u32::MAX,
}
}
};
}
macro_rules! ewmh_get_property {
(request=$request:ident{
window: $window:ident,
property: $property:ident,
xtype: $xtype: ident
},
reply=$reply:ident,
cookie=$cookie:ident,
cookie_unchecked=$cookie_unchecked:ident) => {
impl<'a> EwmhRequest<'a> for $request {
type XcbRequest = xcb::x::GetProperty;
type EwmhCookie = $cookie;
_get_property_request! {$window, $property, $xtype}
fn convert_cookie(&'a self, xcb_cookie: xcb::x::GetPropertyCookie) -> Self::EwmhCookie {
$cookie(xcb_cookie)
}
}
impl EwmhPropertyRequestUnchecked for $request {
type EwmhCookie = $cookie_unchecked;
#[rustfmt::skip]
fn convert_cookie(&self,xcb_cookie: xcb::x::GetPropertyCookieUnchecked,) -> Self::EwmhCookie {
$cookie_unchecked(xcb_cookie)
}
_get_property_request! {$window, $property, $xtype}
}
_get_property_base! {$reply, $cookie, $cookie_unchecked}
};
}
macro_rules! _set_property_base {
(root_window, $property:ident, UTF8_STRING) => {
fn xcb_request(&'a self, con: &Connection) -> xcb::x::ChangeProperty<'a, u8> {
xcb::x::ChangeProperty {
mode: xcb::x::PropMode::Replace,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms.$property,
r#type: con.atoms.UTF8_STRING,
data: &self.data,
}
}
};
(root_window, $property:ident, $xtype:ident) => {
fn xcb_request(&'a self, con: &Connection) -> xcb::x::ChangeProperty<'a, u8> {
xcb::x::ChangeProperty {
mode: xcb::x::PropMode::Replace,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms.$property,
r#type: xcb::x::$xtype,
data: &self.data,
}
}
};
($window:ident, $property:ident, UTF8_STRING) => {
fn xcb_request(&'a self, con: &Connection) -> xcb::x::ChangeProperty<'a, u8> {
xcb::x::ChangeProperty {
mode: xcb::x::PropMode::Replace,
window: $window,
property: con.atoms.$property,
r#type: con.atoms.UTF8_STRING,
data: &self.data,
}
}
};
($window:ident, $property:ident, $xtype:ident) => {
fn xcb_request(&'a self, con: &Connection) -> xcb::x::ChangeProperty<'a, u8> {
xcb::x::ChangeProperty {
mode: xcb::x::PropMode::Replace,
window: $window,
property: con.atoms.$property,
r#type: xcb::x::$xtype,
data: &self.data,
}
}
};
}
macro_rules! ewmh_set_property {
(request=$request:ident{
window: $window:ident,
property: $property:ident,
xtype: $xtype: ident
}) => {
impl<'a> EwmhRequest<'a> for $request {
// TODO fixing to u8 is probably not flexible enough
type XcbRequest = xcb::x::ChangeProperty<'a, u8>;
type EwmhCookie = xcb::VoidCookie;
_set_property_base! {$window, $property, $xtype}
fn convert_cookie(&'a self, xcb_cookie: xcb::VoidCookie) -> Self::EwmhCookie {
xcb_cookie
}
}
impl<'a> EwmhVoidRequestChecked<'a> for $request {
type XcbRequest = xcb::x::ChangeProperty<'a, u8>;
_set_property_base! {$window, $property, $xtype}
}
};
}
macro_rules! _client_message_base {
(root_window) => {
fn xcb_request(&'a self, con: &Connection) -> Self::XcbRequest {
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,
}
}
};
($destination: ident) => {
fn xcb_request(&'a self, con: &Connection) -> Self::XcbRequest {
xcb::x::SendEvent {
propagate: false,
destination: xcb::x::SendEventDest::Window($destination),
event_mask: xcb::x::EventMask::SUBSTRUCTURE_NOTIFY
| xcb::x::EventMask::SUBSTRUCTURE_REDIRECT,
event: &self.client_message,
}
}
};
}
macro_rules! ewmh_client_message {
(request=$request:ident{destination: $destination:ident}) => {
impl<'a> EwmhRequest<'a> for $request {
type XcbRequest = xcb::x::SendEvent<'a, xcb::x::ClientMessageEvent>;
type EwmhCookie = xcb::VoidCookie;
_client_message_base! {$destination}
fn convert_cookie(&'a self, xcb_cookie: xcb::VoidCookie) -> Self::EwmhCookie {
xcb_cookie
}
}
impl<'a> EwmhVoidRequestChecked<'a> for $request {
type XcbRequest = xcb::x::SendEvent<'a, xcb::x::ClientMessageEvent>;
_client_message_base! {$destination}
}
};
}

View file

@ -1,9 +1,24 @@
//! Request and response definitions for the ewmh protocol
//!
//! # Example
//! ```
//! let connection = xcb_wm::ewmh::Connection();
//! let request = xcb_wm::ewmh::proto::GetSupported;
//! let cookie = connection.send_request(request);
//! let response = connection.wait_for_reply(cookie);
//! ```
// mod application_props; // mod application_props;
// #[allow(unused)] // #[allow(unused)]
// mod root_props; // mod root_props;
// //
// pub use application_props::*; // pub use application_props::*;
// pub use root_props::*; // pub use root_props::*;
#[macro_use]
mod macros;
mod test_props; mod test_props;
pub(crate) use test_props::*;
pub use test_props::net_desktop_names::*;
pub use test_props::net_showing_desktop::*;
pub use test_props::net_supported::*;

View file

@ -1,3 +1,34 @@
fn x_buffer_to_strings(xbuf: &[u8]) -> Vec<String> {
let mut vals = vec![];
let mut buf = vec![];
for b in xbuf {
if *b != 0x00 {
buf.push(*b)
} else if !buf.is_empty() {
vals.push(String::from_utf8(buf.clone()).unwrap());
buf.clear();
} else {
buf.clear();
}
}
vals
}
fn strings_to_x_buffer(strings: Vec<&str>) -> Vec<u8> {
let mut data = vec![];
// flatten `strings` into a continuous, NULL separated, array of bytes
for s in strings {
let mut bs = s.as_bytes().to_owned();
bs.push(0b00);
data.extend(bs)
}
data
}
pub(crate) mod net_supported { pub(crate) mod net_supported {
use crate::ewmh::traits::*; use crate::ewmh::traits::*;
use crate::ewmh::Connection; use crate::ewmh::Connection;
@ -13,68 +44,6 @@ pub(crate) mod net_supported {
pub atoms: Vec<xcb::x::Atom>, pub atoms: Vec<xcb::x::Atom>,
} }
impl<'a> EwmhRequest<'a> for GetSupported {
type XcbRequest = xcb::x::GetProperty;
type EwmhCookie = GetSupportedCookie;
fn xcb_request(&self, con: &Connection) -> Self::XcbRequest {
xcb::x::GetProperty {
delete: false,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms._NET_SUPPORTED,
r#type: xcb::x::ATOM_ATOM,
long_offset: 0,
long_length: u32::MAX,
}
}
fn convert_cookie(&'a self, xcb_cookie: xcb::x::GetPropertyCookie) -> Self::EwmhCookie {
GetSupportedCookie(xcb_cookie)
}
}
impl EwmhPropertyRequestUnchecked for GetSupported {
type EwmhCookie = GetSupportedCookieUnchecked;
fn convert_cookie(
&self,
xcb_cookie: xcb::x::GetPropertyCookieUnchecked,
) -> Self::EwmhCookie {
GetSupportedCookieUnchecked(xcb_cookie)
}
fn xcb_request(&self, con: &Connection) -> xcb::x::GetProperty {
xcb::x::GetProperty {
delete: false,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms._NET_SUPPORTED,
r#type: xcb::x::ATOM_ATOM,
long_offset: 0,
long_length: u32::MAX,
}
}
}
impl EwmhCookie for GetSupportedCookie {
type XcbCookie = xcb::x::GetPropertyCookie;
}
impl EwmhPropertyCookieChecked for GetSupportedCookie {
type Reply = GetSupportedReply;
fn inner(self) -> xcb::x::GetPropertyCookie {
self.0
}
}
impl EwmhPropertyCookieUnchecked for GetSupportedCookieUnchecked {
type Reply = GetSupportedReply;
fn inner(self) -> xcb::x::GetPropertyCookieUnchecked {
self.0
}
}
impl From<xcb::x::GetPropertyReply> for GetSupportedReply { impl From<xcb::x::GetPropertyReply> for GetSupportedReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self { fn from(reply: xcb::x::GetPropertyReply) -> Self {
GetSupportedReply { GetSupportedReply {
@ -82,9 +51,21 @@ pub(crate) mod net_supported {
} }
} }
} }
ewmh_get_property! {
request=GetSupported{
window: root_window,
property: _NET_SUPPORTED,
xtype: ATOM_ATOM
},
reply=GetSupportedReply,
cookie=GetSupportedCookie,
cookie_unchecked=GetSupportedCookieUnchecked
}
} }
pub(crate) mod net_desktop_names { pub(crate) mod net_desktop_names {
use crate::ewmh::proto::test_props::{strings_to_x_buffer, x_buffer_to_strings};
use crate::ewmh::traits::*; use crate::ewmh::traits::*;
use crate::ewmh::Connection; use crate::ewmh::Connection;
@ -99,139 +80,42 @@ pub(crate) mod net_desktop_names {
pub desktop_names: Vec<String>, pub desktop_names: Vec<String>,
} }
impl<'a> EwmhRequest<'a> for GetDesktopNames {
type XcbRequest = xcb::x::GetProperty;
type EwmhCookie = GetDesktopNamesCookie;
fn xcb_request(&self, con: &Connection) -> Self::XcbRequest {
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,
}
}
fn convert_cookie(&'a self, xcb_cookie: xcb::x::GetPropertyCookie) -> Self::EwmhCookie {
GetDesktopNamesCookie(xcb_cookie)
}
}
impl EwmhPropertyRequestUnchecked for GetDesktopNames {
type EwmhCookie = GetDesktopNamesCookieUnchecked;
fn convert_cookie(
&self,
xcb_cookie: xcb::x::GetPropertyCookieUnchecked,
) -> Self::EwmhCookie {
GetDesktopNamesCookieUnchecked(xcb_cookie)
}
fn xcb_request(&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 EwmhCookie for GetDesktopNamesCookie {
type XcbCookie = xcb::x::GetPropertyCookie;
}
impl EwmhPropertyCookieChecked for GetDesktopNamesCookie {
type Reply = GetDesktopNamesReply;
fn inner(self) -> xcb::x::GetPropertyCookie {
self.0
}
}
impl EwmhPropertyCookieUnchecked for GetDesktopNamesCookieUnchecked {
type Reply = GetDesktopNamesReply;
fn inner(self) -> xcb::x::GetPropertyCookieUnchecked {
self.0
}
}
impl From<xcb::x::GetPropertyReply> for GetDesktopNamesReply { impl From<xcb::x::GetPropertyReply> for GetDesktopNamesReply {
fn from(reply: xcb::x::GetPropertyReply) -> Self { 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 { GetDesktopNamesReply {
desktop_names: vals, desktop_names: x_buffer_to_strings(reply.value::<u8>()),
} }
} }
} }
ewmh_get_property! {
request=GetDesktopNames{
window: root_window,
property: _NET_DESKTOP_NAMES,
xtype: UTF8_STRING
},
reply=GetDesktopNamesReply,
cookie=GetDesktopNamesCookie,
cookie_unchecked=GetDesktopNamesCookieUnchecked
}
pub struct SetDesktopNames { pub struct SetDesktopNames {
data: Vec<u8>, data: Vec<u8>,
} }
impl SetDesktopNames { impl SetDesktopNames {
pub fn new(names: Vec<&str>) -> SetDesktopNames { pub fn new(names: Vec<&str>) -> SetDesktopNames {
let mut data: Vec<u8> = vec![]; SetDesktopNames {
data: strings_to_x_buffer(names),
// flatten `new_names` into a continuous array of bytes
for name in names {
let mut bname = name.as_bytes().to_owned();
bname.push(0b00);
data.extend(bname)
} }
SetDesktopNames { data }
} }
} }
impl<'a> EwmhRequest<'a> for SetDesktopNames { ewmh_set_property! {
type XcbRequest = xcb::x::ChangeProperty<'a, u8>; request=SetDesktopNames{
type EwmhCookie = xcb::VoidCookie; window: root_window,
property: _NET_DESKTOP_NAMES,
fn xcb_request(&'a self, con: &Connection) -> Self::XcbRequest { xtype: UTF8_STRING
xcb::x::ChangeProperty {
mode: xcb::x::PropMode::Replace,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms._NET_DESKTOP_NAMES,
r#type: con.atoms.UTF8_STRING,
data: &self.data,
}
}
fn convert_cookie(&'a self, xcb_cookie: xcb::VoidCookie) -> Self::EwmhCookie {
xcb_cookie
}
}
impl<'a> EwmhVoidRequestChecked<'a> for SetDesktopNames {
type XcbRequest = xcb::x::ChangeProperty<'a, u8>;
fn xcb_request(&'a self, con: &Connection) -> xcb::x::ChangeProperty<'a, u8> {
xcb::x::ChangeProperty {
mode: xcb::x::PropMode::Replace,
window: con.con.get_setup().roots().next().unwrap().root(),
property: con.atoms._NET_DESKTOP_NAMES,
r#type: con.atoms.UTF8_STRING,
data: &self.data,
}
} }
} }
} }
@ -261,40 +145,7 @@ pub(crate) mod net_showing_desktop {
} }
} }
impl<'a> EwmhRequest<'a> for SetShowingDesktop { ewmh_client_message! {
type XcbRequest = xcb::x::SendEvent<'a, xcb::x::ClientMessageEvent>; request=SetShowingDesktop{destination: root_window}
type EwmhCookie = xcb::VoidCookie;
fn xcb_request(&'a self, con: &Connection) -> Self::XcbRequest {
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,
}
}
fn convert_cookie(&'a self, xcb_cookie: xcb::VoidCookie) -> Self::EwmhCookie {
xcb_cookie
}
}
impl<'a> EwmhVoidRequestChecked<'a> for SetShowingDesktop {
type XcbRequest = xcb::x::SendEvent<'a, xcb::x::ClientMessageEvent>;
fn xcb_request(&'a self, con: &Connection) -> Self::XcbRequest {
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,7 @@
#![warn(rustdoc::broken_intra_doc_links)] #![warn(rustdoc::broken_intra_doc_links)]
#![warn(rustdoc::private_intra_doc_links)] #![warn(rustdoc::private_intra_doc_links)]
#[cfg(feature = "ewmh")]
pub mod ewmh; pub mod ewmh;
#[cfg(test)] #[cfg(test)]