diff --git a/Cargo.toml b/Cargo.toml index b55e70f6..b352c9f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,15 @@ keywords = [ "x11", "xorg", "xproto", "xrs", "window" ] categories = [ "data-structures", "api-bindings", "encoding" ] [features] +default = ["big-requests"] try = [] +# Protocol Extentions + +# Big Requests +# https://www.x.org/releases/X11R7.7/doc/bigreqsproto/bigreq.html +big-requests = [] + [workspace] # XRB is defined as a workspace that automatically includes all its path # dependencies. Currently, that means `xrb-proc-macros` and `cornflakes`. diff --git a/src/big_requests.rs b/src/big_requests.rs new file mode 100644 index 00000000..739328a1 --- /dev/null +++ b/src/big_requests.rs @@ -0,0 +1,19 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Messages defined in the [Big Requests extension]. +//! +//! The [Big Requests extension] enables extended length field for large +//! requests. +//! +//! [Requests]: crate::message::Request +//! [Replies]: crate::message::Reply +//! [Big Requests extension]: https://www.x.org/releases/X11R7.7/doc/bigreqsproto/bigreq.html + +/// This extension's internal name. +/// This is used to retrieve the major opcode for this extension. +pub const EXTENSION_NAME: &str = "BIG-REQUESTS"; + +pub mod reply; +pub mod request; diff --git a/src/big_requests/reply.rs b/src/big_requests/reply.rs new file mode 100644 index 00000000..ff03e5a5 --- /dev/null +++ b/src/big_requests/reply.rs @@ -0,0 +1,51 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! [Replies] defined in the [Big Requests extension]. +//! +//! [Replies] are messages sent from the X server to an X client in response to +//! a [request]. +//! +//! [Replies]: crate::message::Reply +//! [request]: crate::message::Request +//! [Big Requests extension]: super + +extern crate self as xrb; + +use crate::{big_requests::request, message::Reply}; +use derivative::Derivative; +use xrbk_macro::derive_xrb; + +derive_xrb! { + /// The [reply] to a [`EnableBigRequests` request]. + /// + /// [reply]: Reply + /// + /// [`EnableBigRequests` request]: request::EnableBigRequests + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct EnableBigRequests: Reply for request::EnableBigRequests { + /// The sequence number identifying the [request] that generated this + /// [reply]. + /// + /// See [`Reply::sequence`] for more information. + /// + /// [request]: crate::message::Request + /// [reply]: Reply + /// + /// [`Reply::sequence`]: Reply + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The new maximum length for a [request]. + /// + /// This value will always be greater than the one returned in [initial setup]. + /// + /// [initial setup]: crate::connection + /// [request]: crate::message::Request + pub maximum_request_length: u32, + [_; 2], + } +} diff --git a/src/big_requests/request.rs b/src/big_requests/request.rs new file mode 100644 index 00000000..f734b3ba --- /dev/null +++ b/src/big_requests/request.rs @@ -0,0 +1,34 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! [Requests] defined in the [Big Requests extension]. +//! +//! [Requests] are messages sent from an X client to the X server. +//! +//! [Requests]: crate::message::Request +//! [Big Requests extension]: super + +extern crate self as xrb; + +use crate::{big_requests::reply, message::Request}; +use derivative::Derivative; +use xrbk_macro::derive_xrb; + +derive_xrb! { + /// A [request] that enables extended-length protocol requests for the requesting client. + /// + /// # Replies + /// This [request] generates a [`EnableBigRequests` reply]. + /// + /// # Events and Errors + /// This request does not generate any [Events] or [Errors]. + /// + /// [request]: Request + /// [Events]: crate::message::Event + /// [Errors]: crate::message::Error + /// [`EnableBigRequests` reply]: reply::EnableBigRequests + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct EnableBigRequests: Request(0 /* TODO: extensions use dynamic major opcodes */) -> reply::EnableBigRequests {} +} diff --git a/src/lib.rs b/src/lib.rs index c8e2e4ac..1bd347f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,3 +87,8 @@ pub mod connection; pub mod message; pub mod unit; pub mod x11; + +// Protocol Extensions + +#[cfg(feature = "big-requests")] +pub mod big_requests; diff --git a/src/message.rs b/src/message.rs index e09a1efa..80e24c1c 100644 --- a/src/message.rs +++ b/src/message.rs @@ -123,6 +123,7 @@ pub trait Request: X11Size + Writable { /// } /// } /// ``` + #[cfg(not(feature = "big-requests"))] #[allow(clippy::cast_possible_truncation)] fn length(&self) -> u16 { let size = self.x11_size(); @@ -135,6 +136,20 @@ pub trait Request: X11Size + Writable { (size / 4) as u16 } + + #[cfg(feature = "big-requests")] + #[allow(clippy::cast_possible_truncation)] + fn length(&self) -> u32 { + let size = self.x11_size(); + + assert_eq!( + size % 4, + 0, + "expected Request size to be a multiple of 4, found {size}" + ); + + (size / 4) as u32 + } } /// The result of sending a [request]. diff --git a/src/x11/request/graphics.rs b/src/x11/request/graphics.rs index 168309f3..0988f750 100644 --- a/src/x11/request/graphics.rs +++ b/src/x11/request/graphics.rs @@ -86,6 +86,24 @@ request_error! { } } +/// Writes the length of the request to the provided buffer. +fn write_size(request: &impl Request, buf: &mut impl BufMut) { + #[cfg(not(feature = "big-requests"))] + buf.put_u16(request.length()); + #[cfg(feature = "big-requests")] + { + let length = request.length(); + match u16::try_from(length) { + Ok(length) => buf.put_u16(length), + // length is too big for u16, using the extended length field + Err(_) => { + buf.put_u16(0); + buf.put_u32(length); + }, + } + } +} + derive_xrb! { /// A [request] that clears a particular area of a [window]. /// @@ -2102,7 +2120,7 @@ impl Writable for DrawText8 { buf.put_u8(Self::MAJOR_OPCODE); // Unused metabyte position. buf.put_u8(0); - buf.put_u16(self.length()); + write_size(self, buf); self.target.write_to(buf)?; self.graphics_context.write_to(buf)?; @@ -2470,7 +2488,7 @@ impl Writable for DrawText16 { buf.put_u8(Self::MAJOR_OPCODE); // Unused metabyte position. buf.put_u8(0); - buf.put_u16(self.length()); + write_size(self, buf); self.target.write_to(buf)?; self.graphics_context.write_to(buf)?; diff --git a/src/x11/request/input.rs b/src/x11/request/input.rs index efb6f949..40e7ff31 100644 --- a/src/x11/request/input.rs +++ b/src/x11/request/input.rs @@ -21,6 +21,7 @@ use xrbk::{ ReadResult, Readable, Writable, + WriteError, WriteResult, X11Size, }; @@ -1439,7 +1440,15 @@ impl Writable for ChangeKeyboardMapping WriteResult { // Limit `buf` by the length (converted to bytes). + #[cfg(not(feature = "big-requests"))] let buf = &mut buf.limit(usize::from(self.length()) * 4); + #[cfg(feature = "big-requests")] + let size = match usize::try_from(self.length()) { + Ok(size) => size * 4, + + Err(error) => return Err(WriteError::Other(Box::new(error))), + }; + let buf = &mut buf.limit(size); // The major opcode. Self::MAJOR_OPCODE.write_to(buf)?; diff --git a/xrbk_macro/src/definition/expansion/message_trait.rs b/xrbk_macro/src/definition/expansion/message_trait.rs index 393e6ba8..047e4005 100644 --- a/xrbk_macro/src/definition/expansion/message_trait.rs +++ b/xrbk_macro/src/definition/expansion/message_trait.rs @@ -55,10 +55,17 @@ impl Request { #minor_opcode }; + #[cfg(not(feature = "big-requests"))] #[allow(clippy::cast_possible_truncation)] fn length(&self) -> u16 { (::x11_size(self) / 4) as u16 } + + #[cfg(feature = "big-requests")] + #[allow(clippy::cast_possible_truncation)] + fn length(&self) -> u32 { + (::x11_size(self) / 4) as u32 + } } ) }); diff --git a/xrbk_macro/src/definition/expansion/readable.rs b/xrbk_macro/src/definition/expansion/readable.rs index 9f2187c1..17d54eb7 100644 --- a/xrbk_macro/src/definition/expansion/readable.rs +++ b/xrbk_macro/src/definition/expansion/readable.rs @@ -128,7 +128,14 @@ impl Request { // read. #metabyte // Read the request's length. + #[cfg(not(feature = "big-requests"))] let length = <_ as ::xrbk::Buf>::get_u16(buf); + #[cfg(feature = "big-requests")] + let length = match <_ as ::xrbk::Buf>::get_u16(buf) as u32 { + 0 => <_ as ::xrbk::Buf>::get_u32(buf), + length => length, + }; + let buf = &mut <_ as ::xrbk::Buf>::take( buf, ((length - 1) as usize) * 4, diff --git a/xrbk_macro/src/definition/expansion/writable.rs b/xrbk_macro/src/definition/expansion/writable.rs index 24a989ae..ef62584b 100644 --- a/xrbk_macro/src/definition/expansion/writable.rs +++ b/xrbk_macro/src/definition/expansion/writable.rs @@ -137,10 +137,32 @@ impl Request { // Metabyte position #metabyte // Length + #[cfg(not(feature = "big-requests"))] <_ as ::xrbk::BufMut>::put_u16( buf, ::length(&self), ); + #[cfg(feature = "big-requests")] + { + let length = ::length(&self); + match u16::try_from(length) { + Ok(length) => <_ as ::xrbk::BufMut>::put_u16( + buf, + length + ), + // length is too big for u16, using the extended length field + Err(_) => { + <_ as ::xrbk::BufMut>::put_u16( + buf, + 0, + ); + <_ as ::xrbk::BufMut>::put_u32( + buf, + length, + ); + }, + } + } // Other elements #writes