From c8568f528c851ec20e48ec4fadea4f5cba7b936a Mon Sep 17 00:00:00 2001 From: Armin Friedl Date: Sun, 22 May 2022 19:36:16 +0200 Subject: [PATCH] Consistent naming for set and send, readme --- Cargo.toml | 2 +- README.md | 72 ++++++++++++++++++++++++++++- src/ewmh/connection.rs | 10 ++-- src/ewmh/proto/application_props.rs | 10 ++-- src/ewmh/proto/root_props.rs | 71 ++++++++++++++-------------- src/ewmh/traits.rs | 14 +++--- 6 files changed, 123 insertions(+), 56 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f142d9b..d67a793 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xcb-wm" -version = "0.1.0" +version = "0.2.0" authors = [ "Armin Friedl " ] description = "Rust implementation of xcb-wm - icccm and ewmh extensions for xcb" readme = "README.md" diff --git a/README.md b/README.md index 1642fa1..b8ed467 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,71 @@ -# rust-xcb-wm +# xcb-wm -Rust implementation of xcb-wm - icccm and ewmh extensions for xcb \ No newline at end of file +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); +``` diff --git a/src/ewmh/connection.rs b/src/ewmh/connection.rs index aa5984e..dd1c70e 100644 --- a/src/ewmh/connection.rs +++ b/src/ewmh/connection.rs @@ -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 R: EwmhRequest<'b>, { 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 R: EwmhVoidRequestChecked<'b>, { request.send(self) } - fn send_request_unchecked(&self, request: &R) -> R::EwmhCookie + pub fn send_request_unchecked(&self, request: &R) -> R::EwmhCookie where R: EwmhPropertyRequestUnchecked, { request.send(self) } - fn wait_for_reply(&self, cookie: C) -> C::Reply + pub fn wait_for_reply(&self, cookie: C) -> C::Reply where C: EwmhPropertyCookieChecked, { @@ -62,7 +62,7 @@ impl<'a> Connection<'a> { xcb_reply.unwrap().into() } - fn wait_for_reply_unchecked(&self, cookie: C) -> C::Reply + pub fn wait_for_reply_unchecked(&self, cookie: C) -> C::Reply where C: EwmhPropertyCookieUnchecked, { diff --git a/src/ewmh/proto/application_props.rs b/src/ewmh/proto/application_props.rs index 706b7a2..c894de2 100644 --- a/src/ewmh/proto/application_props.rs +++ b/src/ewmh/proto/application_props.rs @@ -274,18 +274,18 @@ ewmh_get_property! { reply=GetWmStateReply } -pub struct SetWmState { +pub struct SendWmState { client_message: xcb::x::ClientMessageEvent, } -impl SetWmState { +impl SendWmState { pub fn new( connection: &Connection, window: xcb::x::Window, action: xcb::x::PropMode, states: [xcb::x::Atom; 2], source_indication: u32, - ) -> SetWmState { + ) -> SendWmState { let data = [ unsafe { std::mem::transmute::<_, u32>(action) }, states[0].resource_id(), @@ -294,7 +294,7 @@ impl SetWmState { 0x00, ]; - SetWmState { + SendWmState { client_message: xcb::x::ClientMessageEvent::new( window, connection.atoms._NET_WM_DESKTOP, @@ -305,6 +305,6 @@ impl SetWmState { } ewmh_client_message! { - request=SetWmState{destination: root} + request=SendWmState{destination: root} } // }}} diff --git a/src/ewmh/proto/root_props.rs b/src/ewmh/proto/root_props.rs index afd35d6..6a25ce6 100644 --- a/src/ewmh/proto/root_props.rs +++ b/src/ewmh/proto/root_props.rs @@ -109,13 +109,13 @@ ewmh_get_property! { reply=GetNumberOfDesktopsReply } -pub struct SetNumberOfDesktops { +pub struct SendNumberOfDesktops { client_message: xcb::x::ClientMessageEvent, } -impl SetNumberOfDesktops { - pub fn new(connection: &Connection, desktops: u32) -> SetNumberOfDesktops { - SetNumberOfDesktops { +impl SendNumberOfDesktops { + pub fn new(connection: &Connection, desktops: u32) -> SendNumberOfDesktops { + SendNumberOfDesktops { client_message: xcb::x::ClientMessageEvent::new( connection.con.get_setup().roots().next().unwrap().root(), connection.atoms._NET_NUMBER_OF_DESKTOPS, @@ -126,7 +126,7 @@ impl SetNumberOfDesktops { } ewmh_client_message! { - request=SetNumberOfDesktops{destination: root} + request=SendNumberOfDesktops{destination: root} } // }}} @@ -156,13 +156,13 @@ ewmh_get_property! { reply=GetDesktopGeometryReply } -pub struct SetDesktopGeometry { +pub struct SendDesktopGeometry { client_message: xcb::x::ClientMessageEvent, } -impl SetDesktopGeometry { - pub fn new(connection: &Connection, width: u32, height: u32) -> SetDesktopGeometry { - SetDesktopGeometry { +impl SendDesktopGeometry { + pub fn new(connection: &Connection, width: u32, height: u32) -> SendDesktopGeometry { + SendDesktopGeometry { client_message: xcb::x::ClientMessageEvent::new( connection.con.get_setup().roots().next().unwrap().root(), connection.atoms._NET_DESKTOP_GEOMETRY, @@ -173,9 +173,8 @@ impl SetDesktopGeometry { } ewmh_client_message! { - request=SetDesktopGeometry{destination: root} + request=SendDesktopGeometry{destination: root} } - // }}} // _NET_DESTKOP_VIEWPORT x, y, CARDINAL[][2]/32 @@ -204,13 +203,13 @@ ewmh_get_property! { reply=GetDesktopViewportReply } -pub struct SetDesktopViewport { +pub struct SendDesktopViewport { client_message: xcb::x::ClientMessageEvent, } -impl SetDesktopViewport { - pub fn new(connection: &Connection, x: u32, y: u32) -> SetDesktopViewport { - SetDesktopViewport { +impl SendDesktopViewport { + pub fn new(connection: &Connection, x: u32, y: u32) -> SendDesktopViewport { + SendDesktopViewport { client_message: xcb::x::ClientMessageEvent::new( connection.con.get_setup().roots().next().unwrap().root(), connection.atoms._NET_DESKTOP_VIEWPORT, @@ -221,7 +220,7 @@ impl SetDesktopViewport { } ewmh_client_message! { - request=SetDesktopViewport{destination: root} + request=SendDesktopViewport{destination: root} } // }}} @@ -249,13 +248,13 @@ ewmh_get_property! { reply=GetCurrentDesktopReply } -pub struct SetCurrentDesktop { +pub struct SendCurrentDesktop { client_message: xcb::x::ClientMessageEvent, } -impl SetCurrentDesktop { - pub fn new(connection: &Connection, desktop: u32) -> SetCurrentDesktop { - SetCurrentDesktop { +impl SendCurrentDesktop { + pub fn new(connection: &Connection, desktop: u32) -> SendCurrentDesktop { + SendCurrentDesktop { client_message: xcb::x::ClientMessageEvent::new( connection.con.get_setup().roots().next().unwrap().root(), connection.atoms._NET_CURRENT_DESKTOP, @@ -266,7 +265,7 @@ impl SetCurrentDesktop { } ewmh_client_message! { - request=SetCurrentDesktop{destination: root} + request=SendCurrentDesktop{destination: root} } // }}} @@ -339,19 +338,19 @@ ewmh_get_property! { reply=GetActiveWindowReply } -pub struct SetActiveWindow { +pub struct SendActiveWindow { client_message: xcb::x::ClientMessageEvent, } -impl SetActiveWindow { +impl SendActiveWindow { pub fn new( connection: &Connection, window: xcb::x::Window, source_indication: u32, timestamp: u32, requestor_window: Option, - ) -> SetActiveWindow { - SetActiveWindow { + ) -> SendActiveWindow { + SendActiveWindow { client_message: xcb::x::ClientMessageEvent::new( window, connection.atoms._NET_ACTIVE_WINDOW, @@ -368,7 +367,7 @@ impl SetActiveWindow { } ewmh_client_message! { - request=SetActiveWindow{destination: root} + request=SendActiveWindow{destination: root} } // }}} @@ -533,18 +532,18 @@ ewmh_client_message! { // _NET_CLOSE_WINDOW // {{{ -pub struct CloseWindow { +pub struct SendCloseWindow { client_message: xcb::x::ClientMessageEvent, } -impl CloseWindow { +impl SendCloseWindow { pub fn new( connection: &Connection, window: xcb::x::Window, source_indication: u32, timestamp: u32, - ) -> CloseWindow { - CloseWindow { + ) -> SendCloseWindow { + SendCloseWindow { client_message: xcb::x::ClientMessageEvent::new( window, connection.atoms._NET_CLOSE_WINDOW, @@ -555,7 +554,7 @@ impl CloseWindow { } ewmh_client_message! { - request=CloseWindow{destination: root} + request=SendCloseWindow{destination: root} } // // _NET_MOVERESIZE_WINDOW @@ -581,13 +580,13 @@ ewmh_client_message! { // // _NET_REQUEST_FRAME_EXTENTS // {{{ -pub struct RequestFrameExtents { +pub struct SendRequestFrameExtents { client_message: xcb::x::ClientMessageEvent, } -impl RequestFrameExtents { - pub fn new(connection: &Connection, window: xcb::x::Window) -> RequestFrameExtents { - RequestFrameExtents { +impl SendRequestFrameExtents { + pub fn new(connection: &Connection, window: xcb::x::Window) -> SendRequestFrameExtents { + SendRequestFrameExtents { client_message: xcb::x::ClientMessageEvent::new( window, connection.atoms._NET_REQUEST_FRAME_EXTENTS, @@ -598,6 +597,6 @@ impl RequestFrameExtents { } ewmh_client_message! { - request=RequestFrameExtents{destination: root} + request=SendRequestFrameExtents{destination: root} } // }}} diff --git a/src/ewmh/traits.rs b/src/ewmh/traits.rs index f252c39..f552d11 100644 --- a/src/ewmh/traits.rs +++ b/src/ewmh/traits.rs @@ -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 /// 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 type XcbRequest: xcb::Request; @@ -43,7 +43,7 @@ pub(crate) trait EwmhRequest<'a> { /// > errors using Connection::check_request. /// /// For [`xcb-wm`] this means either `SetProperty` or `ClientMessage` requests. -pub(crate) trait EwmhVoidRequestChecked<'a> { +pub trait EwmhVoidRequestChecked<'a> { type XcbRequest: xcb::RequestWithoutReply; /// Construct the [`xcb::Request`]. This is implementation specific. @@ -69,7 +69,7 @@ pub(crate) trait EwmhVoidRequestChecked<'a> { /// > be sent to the event loop /// /// For [`xcb-wm`] this means a `GetProperty` requests. -pub(crate) trait EwmhPropertyRequestUnchecked { +pub trait EwmhPropertyRequestUnchecked { type EwmhCookie: EwmhPropertyCookieUnchecked; /// 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 /// may have no _response_ for void requests. -pub(crate) trait EwmhCookie { +pub trait EwmhCookie { /// The wrapped [`xcb::Cookie`] type XcbCookie: xcb::Cookie; } @@ -119,7 +119,7 @@ impl EwmhCookie for xcb::VoidCookie { /// This is needed for 2 purposes: /// - 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`] -pub(crate) trait EwmhPropertyCookieChecked { +pub trait EwmhPropertyCookieChecked { type Reply: EwmhPropertyReply; /// Retrieve the inner [`xcb::Cookie`] @@ -134,7 +134,7 @@ pub(crate) trait EwmhPropertyCookieChecked { /// This is needed for 2 purposes: /// - 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`] -pub(crate) trait EwmhPropertyCookieUnchecked { +pub trait EwmhPropertyCookieUnchecked { type Reply: EwmhPropertyReply; /// 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 /// ([`EwmhPropertyCookieChecked`] and [`EwmhPropertyCookieUnchecked`] -pub(crate) trait EwmhPropertyReply: From {} +pub trait EwmhPropertyReply: From {} impl EwmhPropertyReply for T where T: From {}