Consistent naming for set and send, readme

This commit is contained in:
Armin Friedl 2022-05-22 19:36:16 +02:00
parent 35a91427fd
commit c8568f528c
6 changed files with 123 additions and 56 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "xcb-wm" name = "xcb-wm"
version = "0.1.0" version = "0.2.0"
authors = [ "Armin Friedl <dev@friedl.net>" ] authors = [ "Armin Friedl <dev@friedl.net>" ]
description = "Rust implementation of xcb-wm - icccm and ewmh extensions for xcb" description = "Rust implementation of xcb-wm - icccm and ewmh extensions for xcb"
readme = "README.md" readme = "README.md"

View file

@ -1,3 +1,71 @@
# rust-xcb-wm # xcb-wm
Rust implementation of xcb-wm - icccm and ewmh extensions for xcb The long lost Rust implementation of the
[icccm](https://tronche.com/gui/x/icccm/) and
[ewmh](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html)
extensions for the X Window System protocol.
xcb-wm provides type safe and Rust-native abstractions. It simplifies the usage
of window manager extensions to X11.
xcb-wm sits on top of [rust-xcb](https://github.com/rust-x-bindings/rust-xcb)
similar to how [libxcb-wm](https://gitlab.freedesktop.org/xorg/lib/libxcb-wm)
sits on top of [libxcb](https://gitlab.freedesktop.org/xorg/lib/libxcb). If you
are already using rust-xcb you are also familiar with xcb-wm. The public APIs
and general usage are intentionally close.
## Usage
Add this to your Cargo.toml:
```toml
[dependencies]
xcb-wm = "0.2.0"
```
Each request is either a `Get*`, a `Set*` or a `Send*` struct. `Get*` structs
can be used to get ewmh or iccm properties. `Set*` structs can be used to set
properties. `Send*` structs can be used to send client messages. You can read up
on the protocol definitions for more details but in general every property has a
corresponding `Get*` request. `Set*` requests are mostly useful _before_ a
window is mapped. `Send*` requests for everything else.
Each request can be sent either checked or unchecked. This is typesafe by
special cookies for each of them. You get the request cookie by calling
`send_request`/`send_request_unchecked`.
You can retrieve a reply and wrap it into a high level and meaningful Rust
struct by calling `wait_for_reply`/`wait_for_reply_unchecked` on the cookie. For
requests that don't have a reply (i.e. `Set*` and `Send*` requests) you can use
`check_request` to check for errors.
## Examples
Get the names of available desktops:
``` rust
use xcb;
use xcb_wm::ewmh;
// Create a `rust-xcb` connection
let xcb_con = xcb::Connection::connect(Option::None).unwrap().0;
// Wrap the connection in an `xcb-wm::ewmh` connection for ewmh extensions.
//
// Note that this does not take ownership of the `rust-xcb` connection
// so you can continue to use other xcb functionality with the same
// connection.
let ewmh_con = ewmh::Connection::connect(&xcb_con);
// Create a request for the _NET_DESKTOP_NAMES property
let request = ewmh::proto::GetDesktopNames;
let cookie = ewmh_con.send_request(&request);
// Get a `GetDesktopNamesReply` reply
//
// Replies are automatically de-serialized into meaningful Rust structs. You
// take full ownership of the reply struct.
let reply = ewmh_con.wait_for_reply(cookie);
// All replies implement `Debug` so you can also print them
println!("{:?}", reply);
```

View file

@ -33,28 +33,28 @@ impl<'a> Connection<'a> {
} }
} }
fn send_request<'b, R>(&self, request: &'b R) -> R::EwmhCookie pub fn send_request<'b, R>(&self, request: &'b R) -> R::EwmhCookie
where where
R: EwmhRequest<'b>, R: EwmhRequest<'b>,
{ {
request.send(self) request.send(self)
} }
fn send_request_checked<'b, R>(&self, request: &'b R) -> xcb::VoidCookieChecked pub fn send_request_checked<'b, R>(&self, request: &'b R) -> xcb::VoidCookieChecked
where where
R: EwmhVoidRequestChecked<'b>, R: EwmhVoidRequestChecked<'b>,
{ {
request.send(self) request.send(self)
} }
fn send_request_unchecked<R>(&self, request: &R) -> R::EwmhCookie pub fn send_request_unchecked<R>(&self, request: &R) -> R::EwmhCookie
where where
R: EwmhPropertyRequestUnchecked, R: EwmhPropertyRequestUnchecked,
{ {
request.send(self) request.send(self)
} }
fn wait_for_reply<C>(&self, cookie: C) -> C::Reply pub fn wait_for_reply<C>(&self, cookie: C) -> C::Reply
where where
C: EwmhPropertyCookieChecked, C: EwmhPropertyCookieChecked,
{ {
@ -62,7 +62,7 @@ impl<'a> Connection<'a> {
xcb_reply.unwrap().into() xcb_reply.unwrap().into()
} }
fn wait_for_reply_unchecked<C>(&self, cookie: C) -> C::Reply pub fn wait_for_reply_unchecked<C>(&self, cookie: C) -> C::Reply
where where
C: EwmhPropertyCookieUnchecked, C: EwmhPropertyCookieUnchecked,
{ {

View file

@ -274,18 +274,18 @@ ewmh_get_property! {
reply=GetWmStateReply reply=GetWmStateReply
} }
pub struct SetWmState { pub struct SendWmState {
client_message: xcb::x::ClientMessageEvent, client_message: xcb::x::ClientMessageEvent,
} }
impl SetWmState { impl SendWmState {
pub fn new( pub fn new(
connection: &Connection, connection: &Connection,
window: xcb::x::Window, window: xcb::x::Window,
action: xcb::x::PropMode, action: xcb::x::PropMode,
states: [xcb::x::Atom; 2], states: [xcb::x::Atom; 2],
source_indication: u32, source_indication: u32,
) -> SetWmState { ) -> SendWmState {
let data = [ let data = [
unsafe { std::mem::transmute::<_, u32>(action) }, unsafe { std::mem::transmute::<_, u32>(action) },
states[0].resource_id(), states[0].resource_id(),
@ -294,7 +294,7 @@ impl SetWmState {
0x00, 0x00,
]; ];
SetWmState { SendWmState {
client_message: xcb::x::ClientMessageEvent::new( client_message: xcb::x::ClientMessageEvent::new(
window, window,
connection.atoms._NET_WM_DESKTOP, connection.atoms._NET_WM_DESKTOP,
@ -305,6 +305,6 @@ impl SetWmState {
} }
ewmh_client_message! { ewmh_client_message! {
request=SetWmState{destination: root} request=SendWmState{destination: root}
} }
// }}} // }}}

View file

@ -109,13 +109,13 @@ ewmh_get_property! {
reply=GetNumberOfDesktopsReply reply=GetNumberOfDesktopsReply
} }
pub struct SetNumberOfDesktops { pub struct SendNumberOfDesktops {
client_message: xcb::x::ClientMessageEvent, client_message: xcb::x::ClientMessageEvent,
} }
impl SetNumberOfDesktops { impl SendNumberOfDesktops {
pub fn new(connection: &Connection, desktops: u32) -> SetNumberOfDesktops { pub fn new(connection: &Connection, desktops: u32) -> SendNumberOfDesktops {
SetNumberOfDesktops { SendNumberOfDesktops {
client_message: xcb::x::ClientMessageEvent::new( client_message: xcb::x::ClientMessageEvent::new(
connection.con.get_setup().roots().next().unwrap().root(), connection.con.get_setup().roots().next().unwrap().root(),
connection.atoms._NET_NUMBER_OF_DESKTOPS, connection.atoms._NET_NUMBER_OF_DESKTOPS,
@ -126,7 +126,7 @@ impl SetNumberOfDesktops {
} }
ewmh_client_message! { ewmh_client_message! {
request=SetNumberOfDesktops{destination: root} request=SendNumberOfDesktops{destination: root}
} }
// }}} // }}}
@ -156,13 +156,13 @@ ewmh_get_property! {
reply=GetDesktopGeometryReply reply=GetDesktopGeometryReply
} }
pub struct SetDesktopGeometry { pub struct SendDesktopGeometry {
client_message: xcb::x::ClientMessageEvent, client_message: xcb::x::ClientMessageEvent,
} }
impl SetDesktopGeometry { impl SendDesktopGeometry {
pub fn new(connection: &Connection, width: u32, height: u32) -> SetDesktopGeometry { pub fn new(connection: &Connection, width: u32, height: u32) -> SendDesktopGeometry {
SetDesktopGeometry { SendDesktopGeometry {
client_message: xcb::x::ClientMessageEvent::new( client_message: xcb::x::ClientMessageEvent::new(
connection.con.get_setup().roots().next().unwrap().root(), connection.con.get_setup().roots().next().unwrap().root(),
connection.atoms._NET_DESKTOP_GEOMETRY, connection.atoms._NET_DESKTOP_GEOMETRY,
@ -173,9 +173,8 @@ impl SetDesktopGeometry {
} }
ewmh_client_message! { ewmh_client_message! {
request=SetDesktopGeometry{destination: root} request=SendDesktopGeometry{destination: root}
} }
// }}} // }}}
// _NET_DESTKOP_VIEWPORT x, y, CARDINAL[][2]/32 // _NET_DESTKOP_VIEWPORT x, y, CARDINAL[][2]/32
@ -204,13 +203,13 @@ ewmh_get_property! {
reply=GetDesktopViewportReply reply=GetDesktopViewportReply
} }
pub struct SetDesktopViewport { pub struct SendDesktopViewport {
client_message: xcb::x::ClientMessageEvent, client_message: xcb::x::ClientMessageEvent,
} }
impl SetDesktopViewport { impl SendDesktopViewport {
pub fn new(connection: &Connection, x: u32, y: u32) -> SetDesktopViewport { pub fn new(connection: &Connection, x: u32, y: u32) -> SendDesktopViewport {
SetDesktopViewport { SendDesktopViewport {
client_message: xcb::x::ClientMessageEvent::new( client_message: xcb::x::ClientMessageEvent::new(
connection.con.get_setup().roots().next().unwrap().root(), connection.con.get_setup().roots().next().unwrap().root(),
connection.atoms._NET_DESKTOP_VIEWPORT, connection.atoms._NET_DESKTOP_VIEWPORT,
@ -221,7 +220,7 @@ impl SetDesktopViewport {
} }
ewmh_client_message! { ewmh_client_message! {
request=SetDesktopViewport{destination: root} request=SendDesktopViewport{destination: root}
} }
// }}} // }}}
@ -249,13 +248,13 @@ ewmh_get_property! {
reply=GetCurrentDesktopReply reply=GetCurrentDesktopReply
} }
pub struct SetCurrentDesktop { pub struct SendCurrentDesktop {
client_message: xcb::x::ClientMessageEvent, client_message: xcb::x::ClientMessageEvent,
} }
impl SetCurrentDesktop { impl SendCurrentDesktop {
pub fn new(connection: &Connection, desktop: u32) -> SetCurrentDesktop { pub fn new(connection: &Connection, desktop: u32) -> SendCurrentDesktop {
SetCurrentDesktop { SendCurrentDesktop {
client_message: xcb::x::ClientMessageEvent::new( client_message: xcb::x::ClientMessageEvent::new(
connection.con.get_setup().roots().next().unwrap().root(), connection.con.get_setup().roots().next().unwrap().root(),
connection.atoms._NET_CURRENT_DESKTOP, connection.atoms._NET_CURRENT_DESKTOP,
@ -266,7 +265,7 @@ impl SetCurrentDesktop {
} }
ewmh_client_message! { ewmh_client_message! {
request=SetCurrentDesktop{destination: root} request=SendCurrentDesktop{destination: root}
} }
// }}} // }}}
@ -339,19 +338,19 @@ ewmh_get_property! {
reply=GetActiveWindowReply reply=GetActiveWindowReply
} }
pub struct SetActiveWindow { pub struct SendActiveWindow {
client_message: xcb::x::ClientMessageEvent, client_message: xcb::x::ClientMessageEvent,
} }
impl SetActiveWindow { impl SendActiveWindow {
pub fn new( pub fn new(
connection: &Connection, connection: &Connection,
window: xcb::x::Window, window: xcb::x::Window,
source_indication: u32, source_indication: u32,
timestamp: u32, timestamp: u32,
requestor_window: Option<xcb::x::Window>, requestor_window: Option<xcb::x::Window>,
) -> SetActiveWindow { ) -> SendActiveWindow {
SetActiveWindow { SendActiveWindow {
client_message: xcb::x::ClientMessageEvent::new( client_message: xcb::x::ClientMessageEvent::new(
window, window,
connection.atoms._NET_ACTIVE_WINDOW, connection.atoms._NET_ACTIVE_WINDOW,
@ -368,7 +367,7 @@ impl SetActiveWindow {
} }
ewmh_client_message! { ewmh_client_message! {
request=SetActiveWindow{destination: root} request=SendActiveWindow{destination: root}
} }
// }}} // }}}
@ -533,18 +532,18 @@ ewmh_client_message! {
// _NET_CLOSE_WINDOW // _NET_CLOSE_WINDOW
// {{{ // {{{
pub struct CloseWindow { pub struct SendCloseWindow {
client_message: xcb::x::ClientMessageEvent, client_message: xcb::x::ClientMessageEvent,
} }
impl CloseWindow { impl SendCloseWindow {
pub fn new( pub fn new(
connection: &Connection, connection: &Connection,
window: xcb::x::Window, window: xcb::x::Window,
source_indication: u32, source_indication: u32,
timestamp: u32, timestamp: u32,
) -> CloseWindow { ) -> SendCloseWindow {
CloseWindow { SendCloseWindow {
client_message: xcb::x::ClientMessageEvent::new( client_message: xcb::x::ClientMessageEvent::new(
window, window,
connection.atoms._NET_CLOSE_WINDOW, connection.atoms._NET_CLOSE_WINDOW,
@ -555,7 +554,7 @@ impl CloseWindow {
} }
ewmh_client_message! { ewmh_client_message! {
request=CloseWindow{destination: root} request=SendCloseWindow{destination: root}
} }
// // _NET_MOVERESIZE_WINDOW // // _NET_MOVERESIZE_WINDOW
@ -581,13 +580,13 @@ ewmh_client_message! {
// //
// _NET_REQUEST_FRAME_EXTENTS // _NET_REQUEST_FRAME_EXTENTS
// {{{ // {{{
pub struct RequestFrameExtents { pub struct SendRequestFrameExtents {
client_message: xcb::x::ClientMessageEvent, client_message: xcb::x::ClientMessageEvent,
} }
impl RequestFrameExtents { impl SendRequestFrameExtents {
pub fn new(connection: &Connection, window: xcb::x::Window) -> RequestFrameExtents { pub fn new(connection: &Connection, window: xcb::x::Window) -> SendRequestFrameExtents {
RequestFrameExtents { SendRequestFrameExtents {
client_message: xcb::x::ClientMessageEvent::new( client_message: xcb::x::ClientMessageEvent::new(
window, window,
connection.atoms._NET_REQUEST_FRAME_EXTENTS, connection.atoms._NET_REQUEST_FRAME_EXTENTS,
@ -598,6 +597,6 @@ impl RequestFrameExtents {
} }
ewmh_client_message! { ewmh_client_message! {
request=RequestFrameExtents{destination: root} request=SendRequestFrameExtents{destination: root}
} }
// }}} // }}}

View file

@ -9,7 +9,7 @@ use crate::ewmh::connection::Connection;
/// ///
/// This is a bit mind-bending and makes these traits hard to grasp. This was however done to align /// This is a bit mind-bending and makes these traits hard to grasp. This was however done to align
/// with how the parent [`xcb`] crate works. /// with how the parent [`xcb`] crate works.
pub(crate) trait EwmhRequest<'a> { pub trait EwmhRequest<'a> {
/// The underlying [`xcb::Request`] used to do the actual heavy lifting /// The underlying [`xcb::Request`] used to do the actual heavy lifting
type XcbRequest: xcb::Request; type XcbRequest: xcb::Request;
@ -43,7 +43,7 @@ pub(crate) trait EwmhRequest<'a> {
/// > errors using Connection::check_request. /// > errors using Connection::check_request.
/// ///
/// For [`xcb-wm`] this means either `SetProperty` or `ClientMessage` requests. /// For [`xcb-wm`] this means either `SetProperty` or `ClientMessage` requests.
pub(crate) trait EwmhVoidRequestChecked<'a> { pub trait EwmhVoidRequestChecked<'a> {
type XcbRequest: xcb::RequestWithoutReply; type XcbRequest: xcb::RequestWithoutReply;
/// Construct the [`xcb::Request`]. This is implementation specific. /// Construct the [`xcb::Request`]. This is implementation specific.
@ -69,7 +69,7 @@ pub(crate) trait EwmhVoidRequestChecked<'a> {
/// > be sent to the event loop /// > be sent to the event loop
/// ///
/// For [`xcb-wm`] this means a `GetProperty` requests. /// For [`xcb-wm`] this means a `GetProperty` requests.
pub(crate) trait EwmhPropertyRequestUnchecked { pub trait EwmhPropertyRequestUnchecked {
type EwmhCookie: EwmhPropertyCookieUnchecked; type EwmhCookie: EwmhPropertyCookieUnchecked;
/// Construct the [`xcb::Request`]. This is implementation specific. /// Construct the [`xcb::Request`]. This is implementation specific.
@ -99,7 +99,7 @@ pub(crate) trait EwmhPropertyRequestUnchecked {
/// ///
/// At the same time it may have a _response_ for reply requests or it /// At the same time it may have a _response_ for reply requests or it
/// may have no _response_ for void requests. /// may have no _response_ for void requests.
pub(crate) trait EwmhCookie { pub trait EwmhCookie {
/// The wrapped [`xcb::Cookie`] /// The wrapped [`xcb::Cookie`]
type XcbCookie: xcb::Cookie; type XcbCookie: xcb::Cookie;
} }
@ -119,7 +119,7 @@ impl EwmhCookie for xcb::VoidCookie {
/// This is needed for 2 purposes: /// This is needed for 2 purposes:
/// - Carry the reply type from the request to the retrieval of the response /// - Carry the reply type from the request to the retrieval of the response
/// - Restrict the methods that can be called with it, i.e. [`Connection::wait_for_reply`] /// - Restrict the methods that can be called with it, i.e. [`Connection::wait_for_reply`]
pub(crate) trait EwmhPropertyCookieChecked { pub trait EwmhPropertyCookieChecked {
type Reply: EwmhPropertyReply; type Reply: EwmhPropertyReply;
/// Retrieve the inner [`xcb::Cookie`] /// Retrieve the inner [`xcb::Cookie`]
@ -134,7 +134,7 @@ pub(crate) trait EwmhPropertyCookieChecked {
/// This is needed for 2 purposes: /// This is needed for 2 purposes:
/// - Carry the reply type from the request to the retrieval of the response /// - Carry the reply type from the request to the retrieval of the response
/// - Restrict the methods that can be called with it, i.e. [`Connection::wait_for_reply_unchecked`] /// - Restrict the methods that can be called with it, i.e. [`Connection::wait_for_reply_unchecked`]
pub(crate) trait EwmhPropertyCookieUnchecked { pub trait EwmhPropertyCookieUnchecked {
type Reply: EwmhPropertyReply; type Reply: EwmhPropertyReply;
/// Retrieve the inner [`xcb::Cookie`] /// Retrieve the inner [`xcb::Cookie`]
@ -152,5 +152,5 @@ pub(crate) trait EwmhPropertyCookieUnchecked {
/// ///
/// The connection between a ewmh request and the reply struct is made via ewmh property cookies /// The connection between a ewmh request and the reply struct is made via ewmh property cookies
/// ([`EwmhPropertyCookieChecked`] and [`EwmhPropertyCookieUnchecked`] /// ([`EwmhPropertyCookieChecked`] and [`EwmhPropertyCookieUnchecked`]
pub(crate) trait EwmhPropertyReply: From<xcb::x::GetPropertyReply> {} pub trait EwmhPropertyReply: From<xcb::x::GetPropertyReply> {}
impl<T> EwmhPropertyReply for T where T: From<xcb::x::GetPropertyReply> {} impl<T> EwmhPropertyReply for T where T: From<xcb::x::GetPropertyReply> {}