diff --git a/Cargo.toml b/Cargo.toml index 8f38e224..b55e70f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "xrbk", "xrbk_macro" ] [dependencies] xrbk = { path = "./xrbk" } # (de)serialization bitflags = "1.3" # bit masks - representations of masks +array-init = "2.1.0" # easy array initialization thiserror = "1" # error handling derive_more = "0.99" # derive more useful traits xrbk_macro = { path = "./xrbk_macro" } # generation of XRB structures diff --git a/src/common.rs b/src/common.rs index e19d8e3c..8d6ecaf7 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,13 +2,34 @@ // 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/. -use crate::unit::Px; -use xrbk::{Buf, ConstantX11Size, ReadError, ReadResult, ReadableWithContext, Wrap}; +extern crate self as xrb; +use array_init::array_init; use derive_more::{From, Into}; +use thiserror::Error; +pub use atom::Atom; +pub use mask::*; +pub use res_id::*; +pub use wrapper::*; + +use xrbk::{ + pad, + Buf, + BufMut, + ConstantX11Size, + ReadError, + ReadError::FailedConversion, + ReadResult, + ReadableWithContext, + Wrap, + Writable, + WriteResult, + X11Size, +}; use xrbk_macro::{derive_xrb, new, unwrap, ConstantX11Size, Readable, Wrap, Writable, X11Size}; -extern crate self as xrb; + +use crate::unit::Px; pub mod atom; pub mod set; @@ -18,11 +39,6 @@ mod mask; mod res_id; mod wrapper; -pub use atom::Atom; -pub use mask::*; -pub use res_id::*; -pub use wrapper::*; - /// Whether something is enabled or disabled. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, X11Size, Readable, Writable)] pub enum Toggle { @@ -165,6 +181,50 @@ pub enum GrabMode { Ungrab, } +/// Whether a grab causes a freeze in [event] processing. +/// +/// [event]: crate::message::Event +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, X11Size, Readable, Writable)] +pub enum FreezeMode { + /// [Event] processing is not frozen. + /// + /// [Event]: crate::message::Event + #[doc(alias = "Asynchronous")] + Unfrozen, + + /// [Event] processing is frozen. + /// + /// [Event]: crate::message::Event + #[doc(alias = "Synchronous")] + Frozen, +} + +/// The status of an attempted grab. +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, X11Size, Readable, Writable)] +pub enum GrabStatus { + /// The grab was successful. + Success, + + /// Another client already had a grab. + AlreadyGrabbed, + /// Another client already had an active grab and had frozen [event] + /// processing. + /// + /// [event]: crate::message::Event + Frozen, + /// The given time was either earlier than the previous grab, or later than + /// the X server's [current time]. + /// + /// [current time]: CurrentableTime::CurrentTime + InvalidTime, + /// The grabbed [window] or the [window] which the cursor was confined to is + /// not viewable, or the [window] which the cursor was confined to is + /// completely outside of the root [window]. + /// + /// [window]: Window + NotViewable, +} + #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, X11Size, Readable, Writable)] pub enum StackMode { Above, @@ -183,9 +243,8 @@ pub enum StackMode { Debug, From, Into, - // `new` and `unwrap` const fns + // `new` const fn new, - unwrap, // XRBK traits X11Size, ConstantX11Size, @@ -195,6 +254,17 @@ pub enum StackMode { )] pub struct Keysym(pub(crate) u32); +impl Keysym { + pub const NO_SYMBOL: Self = Self::new(0x0000_0000); + pub const VOID_SYMBOL: Self = Self::new(0x00ff_ffff); + + /// Returns the raw contained keysym value. + #[must_use] + pub const fn unwrap(&self) -> u32 { + self.0 + } +} + #[derive( Copy, Clone, @@ -204,9 +274,8 @@ pub struct Keysym(pub(crate) u32); Debug, From, Into, - // `new` and `unwrap` const fns + // `new` const fn new, - unwrap, // XRBK traits X11Size, ConstantX11Size, @@ -214,7 +283,15 @@ pub struct Keysym(pub(crate) u32); Writable, Wrap, )] -pub struct Keycode(pub u8); +pub struct Keycode(pub(crate) u8); + +impl Keycode { + /// Returns the contained `u8` keycode. + #[must_use] + pub const fn unwrap(&self) -> u8 { + self.0 + } +} #[derive( Copy, @@ -237,6 +314,12 @@ pub struct Keycode(pub u8); )] pub struct Button(pub(crate) u8); +impl Button { + pub const PRIMARY: Self = Self::new(1); + pub const MIDDLE: Self = Self::new(2); + pub const SECONDARY: Self = Self::new(3); +} + #[derive( Copy, Clone, @@ -284,6 +367,29 @@ impl ReadableWithContext for String8 { } } +derive_xrb! { + #[derive( + Clone, + Eq, + PartialEq, + Hash, + Debug, + From, + Into, + // XRBK traits + X11Size, + Readable, + Writable, + )] + pub struct LengthString8 { + #[allow(clippy::cast_possible_truncation)] + let len: u8 = string => string.len() as u8, + + #[context(len => usize::from(*len))] + string: String8, + } +} + #[derive( Copy, Clone, @@ -304,6 +410,22 @@ impl ReadableWithContext for String8 { )] pub struct Char16(pub(crate) u8, pub(crate) u8); +impl From for Char16 { + fn from(value: u16) -> Self { + let [byte1, byte2] = value.to_be_bytes(); + + Self::new(byte1, byte2) + } +} + +impl From for u16 { + fn from(char: Char16) -> Self { + let (byte1, byte2) = char.unwrap(); + + Self::from_be_bytes([byte1, byte2]) + } +} + #[derive(Clone, Eq, PartialEq, Hash, Debug, From, Into, X11Size, Writable)] pub struct String16(Vec); @@ -349,15 +471,43 @@ impl ReadableWithContext for String16 { Readable, Writable, )] -pub struct Point { - #[allow(missing_docs)] +pub struct Coords { + /// The x coordinate, measured in pixels. pub x: Px, - #[allow(missing_docs)] + /// The y coordinate, measured in pixels. pub y: Px, } +/// 2D dimensions (width and height), measured in pixels. +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Hash, + Debug, + From, + Into, + // `new` and `unwrap` const fns + new, + unwrap, + // XRBK traits + X11Size, + ConstantX11Size, + Readable, + Writable, +)] +pub struct Dimensions { + /// The width, measured in pixels. + pub width: Px, + /// The height, measured in pixels. + pub height: Px, +} + /// A rectangle with coordinates and dimensions. -#[derive(Clone, Eq, PartialEq, Hash, Debug, new, X11Size, ConstantX11Size, Readable, Writable)] +#[derive( + Copy, Clone, Eq, PartialEq, Hash, Debug, new, X11Size, ConstantX11Size, Readable, Writable, +)] pub struct Rectangle { /// The x-coordinate of the upper left corner of the `Rectangle`. pub x: Px, @@ -369,6 +519,20 @@ pub struct Rectangle { pub height: Px, } +impl Rectangle { + /// Returns the rectangle's `x` and `y` coordinates as [`Coords`]. + #[must_use] + pub const fn as_coords(&self) -> Coords { + Coords::new(self.x, self.y) + } + + /// Returns the rectangle's `width` and `height` as [`Dimensions`]. + #[must_use] + pub const fn as_dimensions(&self) -> Dimensions { + Dimensions::new(self.width, self.height) + } +} + /// Same as a [`Rectangle`], but with unsigned coordinates. #[derive(Clone, Eq, PartialEq, Hash, Debug, new, X11Size, ConstantX11Size, Readable, Writable)] pub struct Region { @@ -383,12 +547,18 @@ pub struct Region { pub height: Px, } +/// A circular or elliptical arc. #[derive(Clone, Eq, PartialEq, Hash, Debug, new, X11Size, ConstantX11Size, Readable, Writable)] pub struct Arc { - pub x: Px, - pub y: Px, - pub width: Px, - pub height: Px, + /// The [rectangle] which contains the arc. + /// + /// The center of the arc is the center of this rectangle. If the arc were + /// to form a full circle, it would touch this [rectangle] in four places: + /// the left side, the top side, the right side, and the bottom side. It is + /// fully contained within this [rectangle]. + /// + /// [rectangle]: Rectangle + pub bounds: Rectangle, /// Specifies the start of the `Arc`. /// @@ -404,26 +574,251 @@ pub struct Arc { pub end_angle: i16, } +/// The address family of a host. +/// +/// This is used in [`Host`]. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, X11Size, Readable, Writable)] pub enum HostFamily { - Internet, - Decnet, + /// An IPv4 address. + /// + /// See [`HostAddress::Ipv4`] for more information. + Ipv4, + /// A DECnet address. + /// + /// See [`HostAddress::DecNet`] for more information. + DecNet, + /// A chaos address. + /// + /// See [`HostAddress::Chaos`] for more information. Chaos, + /// A server-specific address interpreted by the X server. + /// + /// See [`HostAddress::ServerInterpreted`] for more information. ServerInterpreted = 5, - InternetV6, + /// An IPv6 address. + /// + /// See [`HostAddress::Ipv6`] for more information. + Ipv6, +} + +/// The string used to create an [`AsciiString`] was not encoded as ASCII. +#[derive(Error, Debug)] +#[error("the provided string was not encoded correctly in ASCII format")] +pub struct NonAsciiEncoding; + +/// A string comprised entirely of ASCII bytes. +/// +/// This is used for [`HostAddress::ServerInterpreted`]. +#[derive(Clone, Debug, Hash, PartialEq, Eq, X11Size, Writable)] +pub struct AsciiString(Vec); + +impl AsciiString { + /// Creates a new `AsciiString` with the given ASCII-encoded bytes. + /// + /// # Errors + /// Returns [`NonAsciiEncoding`] if the given `string` is not encoded + /// correctly as ASCII. + pub fn new(string: Vec) -> Result { + if string.is_ascii() { + Ok(Self(string)) + } else { + Err(NonAsciiEncoding) + } + } + + /// Returns a slice of the string as bytes. + #[must_use] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + /// Returns the length of the string. + #[must_use] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns whether the string is empty. + #[must_use] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl ReadableWithContext for AsciiString { + type Context = usize; + + fn read_with(buf: &mut impl Buf, length: &usize) -> ReadResult { + Ok(Self(>::read_with(buf, length)?)) + } +} + +/// The address used in a [host]. +/// +/// [host]: Host +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum HostAddress { + /// An IPv4 address. + Ipv4([u8; 4]), + /// A DECnet address. + /// + /// The first byte contains the least significant 8 bits of the node number. + /// + /// The second byte contains the most significant 2 bits of the node number + /// in its least significant 2 bits, and the area in the most significant 6 + /// bits of the byte. + DecNet([u8; 2]), + /// A chaos address. + /// + /// The first byte is the host number. + /// + /// The second byte is the subnet number. + Chaos([u8; 2]), + /// An address type interpreted by the X server. + ServerInterpreted { + /// The type of address. + /// + /// Address types and the syntax for their values are defined elsewhere. + address_type: AsciiString, + /// The value of the address. + /// + /// Address types and the syntax for their values are defined elsewhere. + address_value: AsciiString, + }, + /// An IPv6 address. + Ipv6([u8; 16]), +} + +impl HostAddress { + /// The [`HostFamily`] associated with this address. + #[must_use] + pub const fn family(&self) -> HostFamily { + match self { + Self::Ipv4(..) => HostFamily::Ipv4, + Self::DecNet(..) => HostFamily::DecNet, + Self::Chaos(..) => HostFamily::Chaos, + Self::ServerInterpreted { .. } => HostFamily::ServerInterpreted, + Self::Ipv6(..) => HostFamily::Ipv6, + } + } +} + +impl X11Size for HostAddress { + fn x11_size(&self) -> usize { + match self { + Self::Ipv4(address) => address.x11_size(), + Self::DecNet(address) | Self::Chaos(address) => address.x11_size(), + + Self::ServerInterpreted { + address_type, + address_value, + } => { + if address_value.is_empty() { + address_type.x11_size() + } else { + address_type.x11_size() + 1 + address_value.x11_size() + } + }, + + Self::Ipv6(address) => address.x11_size(), + } + } +} + +impl ReadableWithContext for HostAddress { + type Context = (HostFamily, usize); + + fn read_with(buf: &mut impl Buf, (family, length): &(HostFamily, usize)) -> ReadResult { + let buf = &mut buf.take(*length); + + match family { + HostFamily::Ipv4 => Ok(Self::Ipv4([ + buf.get_u8(), + buf.get_u8(), + buf.get_u8(), + buf.get_u8(), + ])), + HostFamily::DecNet => Ok(Self::DecNet([buf.get_u8(), buf.get_u8()])), + HostFamily::Chaos => Ok(Self::Chaos([buf.get_u8(), buf.get_u8()])), + + HostFamily::ServerInterpreted => { + let mut address_type = vec![]; + let mut address_value = vec![]; + + while buf.has_remaining() { + match buf.get_u8() { + 0 => { + buf.advance(1); + address_value = >::read_with(buf, &buf.remaining())?; + + break; + }, + + byte => address_type.push(byte), + } + } + + match ( + AsciiString::new(address_type), + AsciiString::new(address_value), + ) { + (Ok(address_type), Ok(address_value)) => Ok(Self::ServerInterpreted { + address_type, + address_value, + }), + + (Err(error), _) | (_, Err(error)) => Err(FailedConversion(Box::new(error))), + } + }, + + HostFamily::Ipv6 => Ok(Self::Ipv6(array_init(|_| buf.get_u8()))), + } + } +} + +impl Writable for HostAddress { + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + match self { + Self::Ipv4(address) => address.write_to(buf)?, + Self::DecNet(address) | Self::Chaos(address) => address.write_to(buf)?, + + Self::ServerInterpreted { + address_type, + address_value, + } => { + address_type.write_to(buf)?; + + if !address_value.is_empty() { + buf.put_u8(0); + address_value.write_to(buf)?; + } + }, + + Self::Ipv6(address) => address.write_to(buf)?, + } + + Ok(()) + } } derive_xrb! { + /// A host, as provided in a [`ChangeHosts` request]. + /// + /// [`ChangeHosts` request]: crate::x11::request::ChangeHosts #[derive(Clone, Eq, PartialEq, Hash, Debug, new, X11Size, Readable, Writable)] pub struct Host { - pub family: HostFamily, + // The `address`' family. + let family: HostFamily = address => address.family(), _, + // The size of `address` in bytes. #[allow(clippy::cast_possible_truncation)] - let address_len: u16 = address => address.len() as u16, - - #[context(address_len => *address_len as usize)] - pub address: Vec, - [_; ..], + let address_size: u16 = address => address.x11_size() as u16, + /// The host's address. + /// + /// See [`HostAddress`] for more information. + #[context(family, address_size => (*family, *address_size as usize))] + pub address: HostAddress, + [_; address => pad(address)], } } diff --git a/src/common/mask.rs b/src/common/mask.rs index 428bbdb7..8b80b4a9 100644 --- a/src/common/mask.rs +++ b/src/common/mask.rs @@ -74,7 +74,7 @@ bitflags! { /// /// [`EnterWindow`]: crate::x11::event::EnterWindow /// [`Focus`]: crate::x11::event::Focus - const KEYS_STATE = 0x0000_4000; + const KEYBOARD_STATE = 0x0000_4000; /// Events generated for arbitrary rectangular areas of windows that /// need to be rendered. diff --git a/src/common/set/attribute.rs b/src/common/set/attribute.rs index b6009ef1..c7b3cc8b 100644 --- a/src/common/set/attribute.rs +++ b/src/common/set/attribute.rs @@ -69,7 +69,7 @@ pub type ColormapAttribute = CopyableFromParent; /// not explicitly initialized in the [`CreateWindow` request], and the /// [window classes] that it can be set with. /// -/// [window]: Window +/// [window]: crate::Window /// [`CreateWindow` request]: crate::x11::request::CreateWindow /// [window classes]: crate::WindowClass /// @@ -116,8 +116,6 @@ pub type ColormapAttribute = CopyableFromParent; /// /// [`InputOutput`]: crate::WindowClass::InputOutput /// [`InputOnly`]: crate::WindowClass::InputOnly -/// -/// [window]: Window #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Attributes { /// Total [`X11Size`] of these `Attributes`. @@ -209,7 +207,7 @@ impl AttributesBuilder { /// methods on this builder. When the builder is configured, [`build()`] can /// be used to build the resulting [`Attributes`]. /// - /// [`build()`]: build + /// [`build()`]: AttributesBuilder::build #[must_use] pub const fn new() -> Self { Self { diff --git a/src/common/set/graphics_options.rs b/src/common/set/graphics_options.rs index fb383a26..78e016a5 100644 --- a/src/common/set/graphics_options.rs +++ b/src/common/set/graphics_options.rs @@ -12,6 +12,7 @@ use crate::{ Pixmap, }; use bitflags::bitflags; +use derivative::Derivative; use xrbk::{ Buf, BufMut, @@ -237,7 +238,7 @@ pub enum FillRule { /// /// [window]: crate::Window #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] -pub enum SubwindowMode { +pub enum ChildMode { /// Both source and destination [windows] are additionally clipped by all /// viewable [`InputOutput`] children. /// @@ -297,7 +298,7 @@ pub type ClipMask = Option; /// |[`tile_stipple_x`] |`0` | /// |[`tile_stipple_y`] |`0` | /// |[`font`] |Depends on the server | -/// |[`subwindow_mode`] |[`SubwindowMode::ClipByChildren`] | +/// |[`child_mode`] |[`ChildMode::ClipByChildren`] | /// |[`graphics_exposure`] |`true` | /// |[`clip_x`] |`0` | /// |[`clip_y`] |`0` | @@ -323,16 +324,20 @@ pub type ClipMask = Option; /// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x /// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y /// [`font`]: GraphicsOptions::font -/// [`subwindow_mode`]: GraphicsOptions::subwindow_mode +/// [`child_mode`]: GraphicsOptions::child_mode /// [`graphics_exposure`]: GraphicsOptions::graphics_exposure /// [`clip_x`]: GraphicsOptions::clip_x /// [`clip_y`]: GraphicsOptions::clip_y /// [`clip_mask`]: GraphicsOptions::clip_mask /// [`dash_offset`]: GraphicsOptions::dash_offset /// [`dashes`]: GraphicsOptions::dashes +#[derive(Derivative, Debug)] +#[derivative(Hash, PartialEq, Eq)] pub struct GraphicsOptions { + #[derivative(Hash = "ignore", PartialEq = "ignore")] x11_size: usize, + #[derivative(Hash = "ignore", PartialEq = "ignore")] mask: GraphicsOptionsMask, function: Option<__Function>, @@ -358,7 +363,7 @@ pub struct GraphicsOptions { font: Option, - subwindow_mode: Option<__SubwindowMode>, + child_mode: Option<__ChildMode>, graphics_exposures: Option<__bool>, @@ -418,7 +423,7 @@ pub struct GraphicsOptionsBuilder { font: Option, - subwindow_mode: Option, + child_mode: Option, graphics_exposures: Option, @@ -470,7 +475,7 @@ impl GraphicsOptionsBuilder { font: None, - subwindow_mode: None, + child_mode: None, graphics_exposures: None, @@ -519,7 +524,7 @@ impl GraphicsOptionsBuilder { font: self.font, - subwindow_mode: self.subwindow_mode.map(__SubwindowMode), + child_mode: self.child_mode.map(__ChildMode), graphics_exposures: self.graphics_exposures.map(__bool), @@ -776,16 +781,16 @@ impl GraphicsOptionsBuilder { /// Configures whether descendent [windows] are included or masked out when /// considering graphics operations. /// - /// See [`GraphicsOptions::subwindow_mode`] for more information. + /// See [`GraphicsOptions::child_mode`] for more information. /// /// [windows]: crate::Window - pub fn subwindow_mode(&mut self, subwindow_mode: SubwindowMode) -> &mut Self { - if self.subwindow_mode.is_none() { + pub fn child_mode(&mut self, child_mode: ChildMode) -> &mut Self { + if self.child_mode.is_none() { self.x11_size += 4; } - self.subwindow_mode = Some(subwindow_mode); - self.mask |= GraphicsOptionsMask::SUBWINDOW_MODE; + self.child_mode = Some(child_mode); + self.mask |= GraphicsOptionsMask::CHILD_MODE; self } @@ -1045,14 +1050,14 @@ impl GraphicsOptions { /// Whether descendent [windows] are included or masked out when applying /// graphics operations. /// - /// See [`SubwindowMode`] for more information. + /// See [`ChildMode`] for more information. /// /// [windows]: crate::Window #[must_use] - pub fn subwindow_mode(&self) -> Option<&SubwindowMode> { - self.subwindow_mode + pub fn child_mode(&self) -> Option<&ChildMode> { + self.child_mode .as_ref() - .map(|__SubwindowMode(subwindow_mode)| subwindow_mode) + .map(|__ChildMode(child_mode)| child_mode) } /// Whether [`GraphicsExposure` events] are generated. @@ -1106,7 +1111,7 @@ impl GraphicsOptions { self.dashes.as_ref().map(|__u8(dashes)| dashes) } - /// Specified the mode with which [arcs] are drawn in + /// Specifies the mode with which [arcs] are drawn in /// [`PolyFillArc` requests]. /// /// See [`ArcMode`] for more information. @@ -1255,14 +1260,14 @@ bitflags! { /// [`GraphicsContext`]: crate::GraphicsContext const FONT = 0x0000_4000; - /// Whether the [descendent windows being clipped when applying graphics options][subwindow] + /// Whether the[descendent windows being clipped when applying graphics options][child] /// is configured in a [`GraphicsContext`]. /// - /// See [`GraphicsOptions::subwindow_mode`] for more information. + /// See [`GraphicsOptions::child_mode`] for more information. /// - /// [subwindow]: GraphicsOptions::subwindow_mode + /// [child]: GraphicsOptions::child_mode /// [`GraphicsContext`]: crate::GraphicsContext - const SUBWINDOW_MODE = 0x0000_8000; + const CHILD_MODE = 0x0000_8000; /// Whether [`GraphicsExposure` events] are configured in a [`GraphicsContext`]. /// @@ -1417,10 +1422,10 @@ impl Readable for GraphicsOptions { let font = super::read_set_value(buf, &mut x11_size, mask.contains(GraphicsOptionsMask::FONT))?; - let subwindow_mode = super::read_set_value( + let child_mode = super::read_set_value( buf, &mut x11_size, - mask.contains(GraphicsOptionsMask::SUBWINDOW_MODE), + mask.contains(GraphicsOptionsMask::CHILD_MODE), )?; let graphics_exposures = super::read_set_value( @@ -1490,7 +1495,7 @@ impl Readable for GraphicsOptions { font, - subwindow_mode, + child_mode, graphics_exposures, @@ -1563,8 +1568,8 @@ impl Writable for GraphicsOptions { font.write_to(buf)?; } - if let Some(subwindow_mode) = &self.subwindow_mode { - subwindow_mode.write_to(buf)?; + if let Some(child_mode) = &self.child_mode { + child_mode.write_to(buf)?; } if let Some(graphics_exposures) = &self.graphics_exposures { @@ -1688,6 +1693,10 @@ impl X11Size for __LineWidth { } impl Readable for __LineWidth { + #[allow( + clippy::cast_possible_truncation, + reason = "truncation is intended behavior" + )] fn read_from(buf: &mut impl Buf) -> ReadResult where Self: Sized, @@ -1925,39 +1934,39 @@ impl Writable for __FillRule { } #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -struct __SubwindowMode(SubwindowMode); +struct __ChildMode(ChildMode); -impl ConstantX11Size for __SubwindowMode { +impl ConstantX11Size for __ChildMode { const X11_SIZE: usize = 4; } -impl X11Size for __SubwindowMode { +impl X11Size for __ChildMode { fn x11_size(&self) -> usize { Self::X11_SIZE } } -impl Readable for __SubwindowMode { +impl Readable for __ChildMode { fn read_from(buf: &mut impl Buf) -> ReadResult where Self: Sized, { Ok(Self(match buf.get_u32() { - discrim if discrim == 0 => SubwindowMode::ClipByChildren, - discrim if discrim == 1 => SubwindowMode::IncludeDescendents, + discrim if discrim == 0 => ChildMode::ClipByChildren, + discrim if discrim == 1 => ChildMode::IncludeDescendents, other_discrim => return Err(UnrecognizedDiscriminant(other_discrim as usize)), })) } } -impl Writable for __SubwindowMode { +impl Writable for __ChildMode { fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { - let Self(subwindow_mode) = self; + let Self(child_mode) = self; - match subwindow_mode { - SubwindowMode::ClipByChildren => buf.put_u32(0), - SubwindowMode::IncludeDescendents => buf.put_u32(1), + match child_mode { + ChildMode::ClipByChildren => buf.put_u32(0), + ChildMode::IncludeDescendents => buf.put_u32(1), } Ok(()) diff --git a/src/common/visual.rs b/src/common/visual.rs index 1b53e793..6bab1092 100644 --- a/src/common/visual.rs +++ b/src/common/visual.rs @@ -344,8 +344,8 @@ derive_xrb! { pub width_mm: Mm, pub height_mm: Mm, - pub min_installed_maps: u16, - pub max_installed_maps: u16, + pub min_installed_colormaps: u16, + pub max_installed_colormaps: u16, pub root_visual: VisualId, pub maintain_contents_mode: MaintainContents, diff --git a/src/common/wrapper.rs b/src/common/wrapper.rs index 69002f29..a5760ea7 100644 --- a/src/common/wrapper.rs +++ b/src/common/wrapper.rs @@ -167,6 +167,26 @@ impl_writable!(CopyableFromParent: &self, buf { Self::Other(val) => val.write_to(buf)?, } + Ok(()) +}); + +impl_constant_x11_size!(CopyableFromParent { + u8::X11_SIZE +}); + +impl_readable!(CopyableFromParent: buf { + Ok(match buf.get_u8() { + discrim if discrim == 0 => Self::CopyFromParent, + val => Self::Other(val), + }) +}); + +impl_writable!(CopyableFromParent: &self, buf { + match self { + Self::CopyFromParent => buf.put_u32(0), + Self::Other(val) => val.write_to(buf)?, + } + Ok(()) }); // }}} @@ -405,20 +425,28 @@ impl_writable!(FocusWindow: &self, buf { Ok(()) }); // }}} -/// The target client(s) of a [`KillClient` request]. +/// The target of a [`KillClient` request]. /// -/// [`KillClient` request]: crate::request::KillClient +/// [`KillClient` request]: crate::x11::request::KillClient #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub enum KillClientTarget { - /// Kill all clients with [`CloseDownMode::RetainTemporary`]. + /// Destroy all remaining resources retained from connections that ended + /// with [`RetainResourcesMode::RetainTemporarily`]. /// - /// [`CloseDownMode::RetainTemporary`]: crate::CloseDownMode::RetainTemporary - // FIXME: correct CloseDownMode link - AllTemporarilyRetainedClients, + /// [`RetainResourcesMode::RetainTemporarily`]: crate::x11::request::RetainResourcesMode::RetainTemporarily + DestroyTemporarilyRetainedResources, - /// Kill the client which created the resource specified by this resource - /// ID. - Other(u32), + /// Kill the client which created the specified `resource`. + /// + /// If the client's connection has already ended, all remaining retained + /// resources created by that client are destroyed (even if the client used + /// [`RetainResourcesMode::RetainPermanently`]). + /// + /// [`RetainResourcesMode::RetainPermanently`]: crate::x11::request::RetainResourcesMode::RetainPermanently + KillClient { + /// The resource whose client is to be killed. + resource: u32, + }, } impl_constant_x11_size!(KillClientTarget { // {{{ @@ -427,16 +455,15 @@ impl_constant_x11_size!(KillClientTarget { // {{{ impl_readable!(KillClientTarget: buf { Ok(match buf.get_u32() { - discrim if discrim == 0 => Self::AllTemporarilyRetainedClients, - - val => Self::Other(val), + 0 => Self::DestroyTemporarilyRetainedResources, + resource => Self::KillClient { resource }, }) }); impl_writable!(KillClientTarget: &self, buf { match self { - Self::AllTemporarilyRetainedClients => buf.put_u32(0), - Self::Other(val) => buf.put_u32(*val), + Self::DestroyTemporarilyRetainedResources => buf.put_u32(0), + Self::KillClient { resource } => buf.put_u32(*resource), } Ok(()) diff --git a/src/connection.rs b/src/connection.rs index 6063a7fa..e275de68 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -149,7 +149,7 @@ derive_xrb! { pub image_byte_order: ImageEndianness, pub bitmap_format_bit_order: ImageEndianness, pub bitmap_format_scanline_unit: u8, - pub bitmap_format_scanline_pad: u8, + pub bitmap_format_scanline_padding: u8, pub min_keycode: Keycode, pub max_keycode: Keycode, diff --git a/src/lib.rs b/src/lib.rs index 679c1967..c8e2e4ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,9 @@ #![warn(clippy::complexity)] #![warn(clippy::cargo)] #![warn(clippy::missing_const_for_fn)] +// Warn for these doc lints. #![warn(missing_docs)] +#![warn(rustdoc::broken_intra_doc_links)] // Allow these lints. #![allow(clippy::doc_markdown)] #![allow(clippy::wildcard_imports)] @@ -65,6 +67,8 @@ //! [X11]: https://x.org/releases/X11R7.7/doc/x11protocol.html //! [X.RS]: https://github.com/XdotRS/xrs/ +pub use common::*; + /// The major version of the X protocol used in XRB. /// /// The X protocol major version may increment if breaking changes are @@ -83,5 +87,3 @@ pub mod connection; pub mod message; pub mod unit; pub mod x11; - -pub use common::*; diff --git a/src/message.rs b/src/message.rs index 3064d850..e09a1efa 100644 --- a/src/message.rs +++ b/src/message.rs @@ -102,7 +102,7 @@ pub trait Request: X11Size + Writable { /// /// ``` /// use xrbk_macro::derive_xrb; - /// use xrb::{ColorChannelMask, Colormap, visual::ColorId, String8}; + /// use xrb::{ColorChannelMask, Colormap, visual::ColorId, message::Request, String8}; /// /// derive_xrb! { /// #[derive(Debug, Hash, PartialEq, Eq, Readable, Writable, X11Size)] @@ -158,7 +158,7 @@ pub enum RequestError { /// An X server may generate an [`Alloc`] event if it runs out of allocation /// space to allocate a requested resource. /// - /// An X server running out of allocation space is undefined behavior, but + /// An X server running out of allocation space is unspecified behavior, but /// it is nonetheless mentioned in the X11 protocol that a server may /// generate an [`Alloc`] error for any [request] for this reason. /// diff --git a/src/unit.rs b/src/unit.rs index 56fc15d8..6293031f 100644 --- a/src/unit.rs +++ b/src/unit.rs @@ -4,9 +4,37 @@ //! Types representing units like millimeters or hertz. -use std::fmt::{Display, Formatter}; +use std::{ + cmp::Ordering, + fmt::{Display, Formatter}, +}; + +use derive_more::{ + Add, + AddAssign, + Div, + DivAssign, + Mul, + MulAssign, + Rem, + RemAssign, + Sub, + SubAssign, + Sum, +}; use thiserror::Error; -use xrbk::{Buf, BufMut, ConstantX11Size, ReadResult, Readable, Writable, WriteResult, X11Size}; + +use xrbk::{ + Buf, + BufMut, + ConstantX11Size, + ReadResult, + Readable, + Wrap, + Writable, + WriteResult, + X11Size, +}; /// An error generated when a value is outside of the required bounds. #[derive(Clone, Debug, Hash, PartialEq, Eq, Error)] @@ -62,9 +90,42 @@ macro_rules! impl_xrbk_traits { } /// A value measured in pixels. -#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Debug, + Hash, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Add, + AddAssign, + Sub, + SubAssign, + Mul, + MulAssign, + Div, + DivAssign, + Rem, + RemAssign, + Sum, +)] pub struct Px(pub Num); +impl Px { + /// Maps a `Px` to `Px` by applying the provided closure to the + /// contained value. + pub fn map(self, map: impl FnOnce(Num) -> Output) -> Px { + Px(map(self.0)) + } + + /// Calls the provided closure with a reference to the contained value. + pub fn inspect(&self, inspect: impl FnOnce(&Num)) { + inspect(&self.0); + } +} + impl Display for Px where Num: Display, @@ -77,9 +138,42 @@ where impl_xrbk_traits!(Px(Num)); /// A value measured in millimeters. -#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Debug, + Hash, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Add, + AddAssign, + Sub, + SubAssign, + Mul, + MulAssign, + Div, + DivAssign, + Rem, + RemAssign, + Sum, +)] pub struct Mm(pub Num); +impl Mm { + /// Maps a `Mm` to `Mm` by applying the provided closure to the + /// contained value. + pub fn map(self, map: impl FnOnce(Num) -> Output) -> Mm { + Mm(map(self.0)) + } + + /// Calls the provided closure with a reference to the contained value. + pub fn inspect(&self, inspect: impl FnOnce(&Num)) { + inspect(&self.0); + } +} + impl Display for Mm where Num: Display, @@ -92,9 +186,42 @@ where impl_xrbk_traits!(Mm(Num)); /// A value measured in milliseconds. -#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Debug, + Hash, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Add, + AddAssign, + Sub, + SubAssign, + Mul, + MulAssign, + Div, + DivAssign, + Rem, + RemAssign, + Sum, +)] pub struct Ms(pub Num); +impl Ms { + /// Maps a `Ms` to `Ms` by applying the provided closure to the + /// contained value. + pub fn map(self, map: impl FnOnce(Num) -> Output) -> Ms { + Ms(map(self.0)) + } + + /// Calls the provided closure with a reference to the contained value. + pub fn inspect(&self, inspect: impl FnOnce(&Num)) { + inspect(&self.0); + } +} + impl Display for Ms where Num: Display, @@ -106,10 +233,112 @@ where impl_xrbk_traits!(Ms(Num)); +/// A value measured in seconds. +#[derive( + Debug, + Hash, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Add, + AddAssign, + Sub, + SubAssign, + Mul, + MulAssign, + Div, + DivAssign, + Rem, + RemAssign, + Sum, +)] +pub struct Sec(pub Num); + +impl Sec { + /// Maps a `Sec` to `Sec` by applying the provided closure to + /// the contained value. + pub fn map(self, map: impl FnOnce(Num) -> Output) -> Sec { + Sec(map(self.0)) + } + + /// Calls the provided closure with a reference to the contained value. + pub fn inspect(&self, inspect: impl FnOnce(&Num)) { + inspect(&self.0); + } +} + +impl Display for Sec +where + Num: Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{} s", self.0) + } +} + +impl Wrap for Sec +where + Num: Copy + Clone + Readable + Writable + ConstantX11Size + From + TryFrom, + Self: TryFrom, + u64: From, +{ + type Integer = Num; +} + +impl From for Sec { + fn from(num: Num) -> Self { + Self(num) + } +} + +impl From> for u16 { + fn from(Sec(num): Sec) -> Self { + num + } +} + +impl_xrbk_traits!(Sec(Num)); + /// A value measured in hertz. -#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive( + Debug, + Hash, + Copy, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + Add, + AddAssign, + Sub, + SubAssign, + Mul, + MulAssign, + Div, + DivAssign, + Rem, + RemAssign, + Sum, +)] pub struct Hz(pub Num); +impl Hz { + /// Maps a `Hz` to `Hz` by applying the provided closure to the + /// contained value. + pub fn map(self, map: impl FnOnce(Num) -> Output) -> Hz { + Hz(map(self.0)) + } + + /// Calls the provided closure with a reference to the contained value. + pub fn inspect(&self, inspect: impl FnOnce(&Num)) { + inspect(&self.0); + } +} + impl Display for Hz where Num: Display, @@ -125,6 +354,13 @@ impl_xrbk_traits!(Hz(Num)); #[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Percentage(u8); +impl Percentage { + /// Calls the provided closure with a reference to the contained value. + pub fn inspect(&self, inspect: impl FnOnce(u8)) { + inspect(self.0); + } +} + impl Display for Percentage { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}%", self.0) @@ -135,7 +371,7 @@ impl Percentage { /// Creates a new percentage. /// /// # Errors - /// Return a [`ValueOutOfBounds`] error if the `percentage > 100`. + /// Returns a [`ValueOutOfBounds`] error if the `percentage > 100`. pub const fn new(percentage: u8) -> Result> { match percentage { percentage if percentage <= 100 => Ok(Self(percentage)), @@ -161,9 +397,113 @@ impl Percentage { /// Returns the wrapped percentage value. #[must_use] - pub const fn unwrap(self) -> u8 { + pub const fn unwrap(&self) -> u8 { self.0 } } +impl PartialEq for Percentage { + fn eq(&self, other: &u8) -> bool { + self.0 == *other + } +} + +impl PartialEq for u8 { + fn eq(&self, other: &Percentage) -> bool { + *self == other.0 + } +} + +impl PartialOrd for Percentage { + fn partial_cmp(&self, other: &u8) -> Option { + self.0.partial_cmp(other) + } +} + +impl PartialOrd for u8 { + fn partial_cmp(&self, other: &Percentage) -> Option { + self.partial_cmp(&other.0) + } +} + impl_xrbk_traits!(Percentage(u8)); + +/// A value measured as a percentage from -100% to 100%. +#[derive(Debug, Hash, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct SignedPercentage(i8); + +impl SignedPercentage { + /// Calls the provided closure with a reference to the contained value. + pub fn inspect(&self, inspect: impl FnOnce(i8)) { + inspect(self.0); + } +} + +impl Display for SignedPercentage { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}%", self.0) + } +} + +impl SignedPercentage { + /// Creates a new signed percentage. + /// + /// # Errors + /// Returns a [`ValueOutOfBounds`] error if `percentage < -100` or + /// `percentage > 100`. + pub const fn new(percentage: i8) -> Result> { + match percentage { + percentage if percentage >= -100 && percentage <= 100 => Ok(Self(percentage)), + + other => Err(ValueOutOfBounds { + min: -100, + max: 100, + found: other, + }), + } + } + + /// Creates a new signed percentage without ensuring it has the right + /// bounds. + /// + /// # Safety + /// Callers of this function must ensure that the given percentage satisfies + /// the bounds `-100 <= percentage <= 100`. Creating a [`SignedPercentage`] + /// with a value less than -100 or greater than 100 is Undefined Behavior. + #[must_use] + pub const unsafe fn new_unchecked(percentage: i8) -> Self { + Self(percentage) + } + + /// Returns the wrapped percentage value. + #[must_use] + pub const fn unwrap(&self) -> i8 { + self.0 + } +} + +impl PartialEq for SignedPercentage { + fn eq(&self, other: &i8) -> bool { + self.0 == *other + } +} + +impl PartialEq for i8 { + fn eq(&self, other: &SignedPercentage) -> bool { + *self == other.0 + } +} + +impl PartialOrd for SignedPercentage { + fn partial_cmp(&self, other: &i8) -> Option { + self.0.partial_cmp(other) + } +} + +impl PartialOrd for i8 { + fn partial_cmp(&self, other: &SignedPercentage) -> Option { + self.partial_cmp(&other.0) + } +} + +impl_xrbk_traits!(SignedPercentage(i8)); diff --git a/src/x11.rs b/src/x11.rs index b74db23d..5afd28a7 100644 --- a/src/x11.rs +++ b/src/x11.rs @@ -5,12 +5,12 @@ //! Messages defined in the core X11 protocol: [requests], [replies], [events], //! and [errors]. //! -//! [requests]: x11::request -//! [replies]: x11::reply -//! [events]: x11::event -//! [errors]: x11::error +//! [requests]: request +//! [replies]: reply +//! [events]: event +//! [errors]: error pub mod error; pub mod event; -// pub mod reply; -// pub mod request; +pub mod reply; +pub mod request; diff --git a/src/x11/event.rs b/src/x11/event.rs index 2155c9a3..b5130dc6 100644 --- a/src/x11/event.rs +++ b/src/x11/event.rs @@ -6,20 +6,29 @@ //! //! [Events] are messages sent from the X server to an X client. //! -//! [Events]: crate::message::Event +//! [Events]: Event //! [core X11 protocol]: super +extern crate self as xrb; + +use bitflags::bitflags; +use derivative::Derivative; + +use xrbk::{Buf, ConstantX11Size, ReadResult, Readable, ReadableWithContext, X11Size}; +use xrbk_macro::{derive_xrb, ConstantX11Size, Readable, Writable, X11Size}; + use crate::{ atom::Atom, + message::Event, set::WindowConfigMask, unit::Px, Button, + Coords, CurrentableTime, Drawable, GrabMode, Keycode, ModifierMask, - Point, Rectangle, Region, StackMode, @@ -27,13 +36,6 @@ use crate::{ Window, }; -use bitflags::bitflags; -use derivative::Derivative; -use xrbk::{Buf, ConstantX11Size, ReadResult, Readable, ReadableWithContext, X11Size}; - -use xrbk_macro::{derive_xrb, ConstantX11Size, Readable, Writable, X11Size}; -extern crate self as xrb; - derive_xrb! { /// An [event] generated when a key is pressed. /// @@ -43,7 +45,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`KEY_PRESS`] on the /// `event_window`. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`KEY_PRESS`]: crate::EventMask::KEY_PRESS #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -52,9 +54,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -65,13 +67,13 @@ derive_xrb! { /// The time at which this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub time: Timestamp, /// The root window containing the window in which the cursor was /// located when this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub root: Window, /// The window which this [event] was generated in relation to. /// @@ -85,7 +87,7 @@ derive_xrb! { /// Active grabs or the currently focused window may modify how the /// `event_window` is chosen. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`do_not_propagate_mask`]: crate::Attributes::do_not_propagate_mask pub event_window: Window, /// If a child of the `event_window` contains the cursor, this is that @@ -97,19 +99,19 @@ derive_xrb! { /// The coordinates of the cursor at the `time` this [event] was /// generated, relative to the `root` [window]'s origin. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window - pub root_coords: Point, + pub root_coords: Coords, /// The coordinates of the cursor at the `time` this [event] was /// generated, relative to the `event_window`'s origin. /// - /// [event]: crate::message::Event - pub event_coords: Point, + /// [event]: Event + pub event_coords: Coords, /// The state of mouse buttons and modifier keys immediately /// before this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub modifiers: ModifierMask, /// Whether the cursor is on the same screen as the `event_window`. @@ -125,7 +127,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`KEY_RELEASE`] on the /// `event_window`. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`KEY_RELEASE`]: crate::EventMask::KEY_RELEASE #[derive(Debug, Derivative, X11Size, Readable, Writable)] #[derivative(Hash, PartialEq, Eq)] @@ -133,9 +135,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -146,14 +148,14 @@ derive_xrb! { /// The time at which this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub time: Timestamp, /// The root [window] containing the [window] in which the cursor was located /// within when this [event] was generated. /// /// [window]: Window - /// [event]: crate::message::Event + /// [event]: Event pub root: Window, /// The window which this [event] was generated in relation to. /// @@ -167,7 +169,7 @@ derive_xrb! { /// Active grabs or the currently focused window may modify how the /// `event_window` is chosen. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`do_not_propagate_mask`]: crate::Attributes::do_not_propagate_mask pub event_window: Window, /// If a child of the `event_window` contains the cursor, this is that @@ -180,18 +182,18 @@ derive_xrb! { /// generated, relative to the `root` [window]'s origin. /// /// [window]: Window - /// [event]: crate::message::Event - pub root_coords: Point, + /// [event]: Event + pub root_coords: Coords, /// The coordinates of the cursor at the `time` this [event] was /// generated, relative to the `event_window`'s origin. /// - /// [event]: crate::message::Event - pub event_coords: Point, + /// [event]: Event + pub event_coords: Coords, /// The state of [mouse buttons] and modifier keys immediately /// before this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event /// [mouse buttons]: Button pub modifiers: ModifierMask, @@ -208,7 +210,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`BUTTON_PRESS`] on the /// `event_window`. /// - /// [event]: crate::message::Event + /// [event]: Event /// [mouse button]: Button /// [`BUTTON_PRESS`]: crate::EventMask::BUTTON_PRESS #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -217,9 +219,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -230,13 +232,13 @@ derive_xrb! { /// The time at which this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub time: Timestamp, /// The root window containing the window in which the cursor was /// located when this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub root: Window, /// The window which this [event] was generated in relation to. /// @@ -249,7 +251,7 @@ derive_xrb! { /// /// Active grabs may modify how the `event_window` is chosen. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`do_not_propagate_mask`]: crate::Attributes::do_not_propagate_mask pub event_window: Window, /// If a child of the `event_window` contains the cursor, this is that @@ -261,20 +263,20 @@ derive_xrb! { /// The coordinates of the cursor at the `time` this [event] was generated, /// relative to the `root` [window]'s origin. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window - pub root_coords: Point, + pub root_coords: Coords, /// The coordinates of the cursor at the `time` this [event] was generated, /// relative to the `event_window`'s origin. /// - /// [event]: crate::message::Event - pub event_coords: Point, + /// [event]: Event + pub event_coords: Coords, /// The state of [mouse buttons] and modifier keys immediately /// before this [event] was generated. /// /// [mouse buttons]: Button - /// [event]: crate::message::Event + /// [event]: Event pub modifiers: ModifierMask, /// Whether the cursor is on the same [screen] as the `event_window`. @@ -290,7 +292,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`BUTTON_RELEASE`] on the /// `event_window`. /// - /// [event]: crate::message::Event + /// [event]: Event /// [mouse button]: Button /// [`BUTTON_RELEASE`]: crate::EventMask::BUTTON_RELEASE #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -299,9 +301,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -312,13 +314,13 @@ derive_xrb! { /// The time at which this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub time: Timestamp, /// The root window containing the window in which the cursor was /// located when this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub root: Window, /// The window which this [event] was generated in relation to. /// @@ -331,7 +333,7 @@ derive_xrb! { /// /// Active grabs may modify how the `event_window` is chosen. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`do_not_propagate_mask`]: crate::Attributes::do_not_propagate_mask pub event_window: Window, /// If a child of the `event_window` contains the cursor, this is that @@ -343,19 +345,19 @@ derive_xrb! { /// The coordinates of the cursor at the `time` this [event] was generated, /// relative to the `root` window's origin. /// - /// [event]: crate::message::Event - pub root_coords: Point, + /// [event]: Event + pub root_coords: Coords, /// The coordinates of the cursor at the `time` this [event] was generated, /// relative to the `event_window`'s origin. /// - /// [event]: crate::message::Event - pub event_coords: Point, + /// [event]: Event + pub event_coords: Coords, /// The state of [mouse buttons] and modifier keys immediately /// before this [event] was generated. /// /// [mouse buttons]: Button - /// [event]: crate::message::Event + /// [event]: Event pub modifiers: ModifierMask, /// Whether the cursor is on the same [screen] as the `event_window`. @@ -393,7 +395,7 @@ pub enum MotionNotificationType { /// [`MOTION_HINT`]: crate::EventMask::MOTION_HINT /// [mouse button]: Button /// - /// [`QueryCursor`]: super::request::QueryCursor + /// [`QueryCursor`]: super::request::QueryCursorLocation /// [`GetMotionEvents`]: super::request::GetMotionEvents Hint, } @@ -433,10 +435,10 @@ derive_xrb! { /// [`MOTION_HINT`]: crate::EventMask::MOTION_HINT /// /// [`Hint`]: MotionNotificationType::Hint - /// [`QueryCursor`]: super::request::QueryCursor + /// [`QueryCursor`]: super::request::QueryCursorLocation /// [`GetMotionEvents`]: super::request::GetMotionEvents /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window #[derive(Debug, Derivative, X11Size, Readable, Writable)] #[derivative(Hash, PartialEq, Eq)] @@ -444,9 +446,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -461,7 +463,7 @@ derive_xrb! { /// The root window containing the window in which the cursor was /// located when this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub root: Window, /// The window which this [event] was generated in relation to. /// @@ -474,7 +476,7 @@ derive_xrb! { /// /// Active grabs may modify how the `event_window` is chosen. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`do_not_propagate_mask`]: crate::Attributes::do_not_propagate_mask pub event_window: Window, /// If a child of the `event_window` contains the cursor, this is that @@ -486,20 +488,20 @@ derive_xrb! { /// The coordinates of the cursor at the `time` this [event] was generated, /// relative to the `root` [window]'s origin. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window - pub root_coords: Point, + pub root_coords: Coords, /// The coordinates of the cursor at the `time` this [event] was generated, /// relative to the `event_window`'s origin. /// - /// [event]: crate::message::Event - pub event_coords: Point, + /// [event]: Event + pub event_coords: Coords, /// The state of [mouse buttons] and modifier keys immediately /// before this [event] was generated. /// /// [mouse buttons]: Button - /// [event]: crate::message::Event + /// [event]: Event pub modifiers: ModifierMask, /// Whether the cursor is on the same [screen] as the `event_window`. @@ -556,7 +558,7 @@ derive_xrb! { /// [`Nonlinear`]: EnterLeaveDetail::Nonlinear /// [`NonlinearIntermediate`]: EnterLeaveDetail::NonlinearIntermediate /// -/// [event]: crate::message::Event +/// [event]: Event /// [window]: Window #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, X11Size, Readable, Writable)] pub enum EnterLeaveDetail { @@ -640,7 +642,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`ENTER_WINDOW`] on the /// [window]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`ENTER_WINDOW`]: crate::EventMask::ENTER_WINDOW #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -649,9 +651,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -660,19 +662,19 @@ derive_xrb! { /// /// See [`EnterLeaveDetail`] for more information. /// - /// [event]: crate::message::Event + /// [event]: Event #[metabyte] pub detail: EnterLeaveDetail, /// The time at which this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub time: Timestamp, /// The root window containing the window in which the cursor was /// located when this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub root: Window, /// The window that the cursor entered. pub event_window: Window, @@ -687,7 +689,7 @@ derive_xrb! { /// /// This is always the final position of the cursor, not its initial /// position. - pub root_coords: Point, + pub root_coords: Coords, /// The position of the cursor at the time this event was generated, /// relative to the `event_window`'s origin, if the `event_window` is on /// the [`SAME_SCREEN`]. @@ -699,7 +701,7 @@ derive_xrb! { /// position. /// /// [`SAME_SCREEN`]: EnterLeaveMask::SAME_SCREEN - pub event_coords: Point, + pub event_coords: Coords, /// The state of mouse buttons and modifier keys immediately /// before this event was generated. @@ -731,7 +733,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`LEAVE_WINDOW`] on the /// [window]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`LEAVE_WINDOW`]: crate::EventMask::LEAVE_WINDOW #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -740,9 +742,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -751,19 +753,19 @@ derive_xrb! { /// /// See [`EnterLeaveDetail`] for more information. /// - /// [event]: crate::message::Event + /// [event]: Event #[metabyte] pub detail: EnterLeaveDetail, /// The time at which this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub time: Timestamp, /// The root window containing the window in which the cursor was /// located when this [event] was generated. /// - /// [event]: crate::message::Event + /// [event]: Event pub root: Window, /// The window which the cursor left. pub event_window: Window, @@ -779,9 +781,9 @@ derive_xrb! { /// This is always the final position of the cursor, not its initial /// position. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window - pub root_coords: Point, + pub root_coords: Coords, /// The position of the cursor at the `time` this [event] was generated, /// relative to the `event_window`'s origin, if the `event_window` is on /// the [`SAME_SCREEN`]. @@ -792,16 +794,16 @@ derive_xrb! { /// This is always the final position of the cursor, not its initial /// position. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`SAME_SCREEN`]: EnterLeaveMask::SAME_SCREEN /// [screen]: crate::Screen - pub event_coords: Point, + pub event_coords: Coords, /// The state of [mouse buttons] and modifier keys immediately /// before this [event] was generated. /// /// [mouse buttons]: Button - /// [event]: crate::message::Event + /// [event]: Event pub modifiers: ModifierMask, /// [`Normal`] for normal `LeaveWindow` events, [`Grab`] and /// [`Ungrab`] for events generated by grabs and ungrabs. @@ -937,7 +939,7 @@ derive_xrb! { /// /// [`None`]: FocusDetail::None /// -/// [event]: crate::message::Event +/// [event]: Event /// [window]: Window #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash, X11Size, Readable, Writable)] pub enum FocusDetail { @@ -1020,7 +1022,7 @@ derive_xrb! { /// `Focus` events are reported to clients selecting [`FOCUS_CHANGE`] on the /// [window] that was focused. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`FOCUS_CHANGE`]: crate::EventMask::FOCUS_CHANGE #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -1029,9 +1031,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1040,7 +1042,7 @@ derive_xrb! { /// /// See [`FocusDetail`] for more information. /// - /// [event]: crate::message::Event + /// [event]: Event #[metabyte] pub detail: FocusDetail, @@ -1054,7 +1056,7 @@ derive_xrb! { /// generated by a [`SetInputFocus` request] while the keyboard is /// grabbed. /// - /// [event]: crate::message::Event + /// [event]: Event /// /// [`Normal`]: FocusGrabMode::Normal /// [`Grab`]: FocusGrabMode::Grab @@ -1079,7 +1081,7 @@ derive_xrb! { /// `Unfocus` events are reported to clients selecting [`FOCUS_CHANGE`] on the /// [window] that was unfocused. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`FOCUS_CHANGE`]: crate::EventMask::FOCUS_CHANGE #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -1088,9 +1090,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1099,7 +1101,7 @@ derive_xrb! { /// /// See [`FocusDetail`] for more information. /// - /// [event]: crate::message::Event + /// [event]: Event #[metabyte] pub detail: FocusDetail, @@ -1113,7 +1115,7 @@ derive_xrb! { /// generated by a [`SetInputFocus` request] while the keyboard is /// grabbed. /// - /// [event]: crate::message::Event + /// [event]: Event /// /// [`Normal`]: FocusGrabMode::Normal /// [`Grab`]: FocusGrabMode::Grab @@ -1128,14 +1130,14 @@ derive_xrb! { /// An [event] describing the current state of the keyboard. /// /// # Recipients - /// This [event] is reported to clients selecting [`KEYS_STATE`] on a [window] - /// immediately after every [`EnterWindow`] and [`Focus`] event. + /// This [event] is reported to clients selecting [`KEYBOARD_STATE`] on a + /// [window] immediately after every [`EnterWindow`] and [`Focus`] event. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window - /// [`KEYS_STATE`]: crate::EventMask::KEYS_STATE + /// [`KEYBOARD_STATE`]: crate::EventMask::KEYBOARD_STATE #[derive(Debug, Hash, X11Size, Readable, Writable)] - pub struct KeysState: Event(11) { + pub struct KeyboardState: Event(11) { /// A bit vector representing the current keyboard state. /// /// Each bit set to 1 indicates that the corresponding key is currently @@ -1167,7 +1169,7 @@ derive_xrb! { /// # Recipients /// This [event] is reported to clients selecting [`EXPOSURE`] on a [window]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// /// [`maintain_contents` attribute]: crate::set::Attributes::maintain_contents @@ -1182,8 +1184,8 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence - /// [event]: crate::message::Event + /// [sequence number]: Event::sequence + /// [event]: Event /// [request]: crate::message::Request #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] @@ -1212,7 +1214,7 @@ derive_xrb! { /// This [event] is reported to a client using a [`GraphicsContext`] with /// [`graphics_exposure`] enabled. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`GraphicsContext`]: crate::GraphicsContext /// [`graphics_exposure`]: crate::set::GraphicsOptions::graphics_exposure #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -1221,8 +1223,8 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence - /// [event]: crate::message::Event + /// [sequence number]: Event::sequence + /// [event]: Event /// [request]: crate::message::Request #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] @@ -1250,11 +1252,11 @@ derive_xrb! { /// The [major opcode] identifying the graphics request used. /// /// For the core protocol, this always refers to [`CopyArea`] or - /// [`CopyPlane`]. + /// [`CopyBitPlane`]. /// /// [major opcode]: crate::message::Request::MAJOR_OPCODE /// [`CopyArea`]: super::request::CopyArea - /// [`CopyPlane`]: super::request::CopyPlane + /// [`CopyBitPlane`]: super::request::CopyBitPlane pub major_opcode: u8, [_; ..], } @@ -1266,7 +1268,7 @@ derive_xrb! { /// This [event] is reported to a client using a [`GraphicsContext`] with /// [`graphics_exposure`] enabled. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`GraphicsExposure` events]: GraphicsExposure /// [`GraphicsContext`]: crate::GraphicsContext /// [`graphics_exposure`]: crate::set::GraphicsOptions::graphics_exposure @@ -1276,9 +1278,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1301,7 +1303,7 @@ derive_xrb! { /// /// [major opcode]: crate::message::Request::MAJOR_OPCODE /// [`CopyArea`]: super::request::CopyArea - /// [`CopyPlane`]: super::request::CopyPlane + /// [`CopyPlane`]: super::request::CopyBitPlane pub major_opcode: u8, [_; ..], } @@ -1366,7 +1368,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`VISIBILITY_CHANGE`] on /// the [window]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// /// [`Unobscured`]: VisibilityState::Unobscured @@ -1380,9 +1382,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1400,7 +1402,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`SUBSTRUCTURE_NOTIFY`] on /// the [window]'s parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`SUBSTRUCTURE_NOTIFY`]: crate::EventMask::SUBSTRUCTURE_NOTIFY #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -1409,9 +1411,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1456,7 +1458,7 @@ derive_xrb! { /// [window], as well as to clients selecting [`SUBSTRUCTURE_NOTIFY`] on its /// parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`STRUCTURE_NOTIFY`]: crate::EventMask::STRUCTURE_NOTIFY /// [`SUBSTRUCTURE_NOTIFY`]: crate::EventMask::SUBSTRUCTURE_NOTIFY @@ -1466,9 +1468,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1496,7 +1498,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`STRUCTURE_NOTIFY`] on the /// window, and to clients selecting [`SUBSTRUCTURE_NOTIFY`] on its parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`STRUCTURE_NOTIFY`]: crate::EventMask::STRUCTURE_NOTIFY /// [`SUBSTRUCTURE_NOTIFY`]: crate::EventMask::SUBSTRUCTURE_NOTIFY @@ -1506,9 +1508,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1528,7 +1530,7 @@ derive_xrb! { /// Whether this [event] was generated as a result of its parent being /// resized when the unmapped [window] had [`WindowGravity::Unmap`]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`WindowGravity::Unmap`]: crate::WindowGravity::Unmap pub from_configure: bool, @@ -1544,7 +1546,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`STRUCTURE_NOTIFY`] on the /// [window] and to clients selecting [`SUBSTRUCTURE_NOTIFY`] on the parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`STRUCTURE_NOTIFY`]: crate::EventMask::STRUCTURE_NOTIFY /// [`SUBSTRUCTURE_NOTIFY`]: crate::EventMask::SUBSTRUCTURE_NOTIFY @@ -1554,9 +1556,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1597,7 +1599,7 @@ derive_xrb! { /// the client selecting [`SUBSTRUCTURE_REDIRECT`] sends its own /// [`MapWindow` request] for the [window]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`override_redirect` attribute]: crate::Attributes::override_redirect /// [`MapWindow` request]: super::request::MapWindow @@ -1605,13 +1607,13 @@ derive_xrb! { /// [`SUBSTRUCTURE_REDIRECT`]: crate::EventMask::SUBSTRUCTURE_REDIRECT #[derive(Debug, Derivative, X11Size, Readable, Writable)] #[derivative(Hash, PartialEq, Eq)] - pub struct MapRequest: Event(20) { + pub struct MapWindowRequest: Event(20) { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1635,7 +1637,7 @@ derive_xrb! { /// either the old parent or the `new_parent`, and to clients selecting /// [`STRUCTURE_NOTIFY`] on the `window` itself. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`SUBSTRUCTURE_NOTIFY`]: crate::EventMask::SUBSTRUCTURE_NOTIFY /// [`STRUCTURE_NOTIFY`]: crate::EventMask::STRUCTURE_NOTIFY @@ -1645,9 +1647,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1668,7 +1670,7 @@ derive_xrb! { pub new_parent: Window, /// The `window`'s new coordinates relative to its `new_parent`'s origin. - pub coords: Point, + pub coords: Coords, /// Whether [`MapWindow`] and [`ConfigureWindow`] requests on the /// `window` should override a [`SUBSTRUCTURE_REDIRECT`] on the @@ -1692,7 +1694,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`STRUCTURE_NOTIFY`] on the /// window, and to clients selecting [`SUBSTRUCTURE_NOTIFY`] on its parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`ConfigureWindow` request]: super::request::ConfigureWindow /// [window]: Window /// [`STRUCTURE_NOTIFY`]: crate::EventMask::STRUCTURE_NOTIFY @@ -1703,9 +1705,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1774,7 +1776,7 @@ derive_xrb! { /// This [event] is reported to the client selecting [`SUBSTRUCTURE_REDIRECT`] /// on the window's parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`ConfigureWindow` request]: super::request::ConfigureWindow /// @@ -1785,9 +1787,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1840,7 +1842,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`STRUCTURE_NOTIFY`] on the /// window, and to clients selecting [`SUBSTRUCTURE_NOTIFY`] on its parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`STRUCTURE_NOTIFY`]: crate::EventMask::STRUCTURE_NOTIFY /// [`SUBSTRUCTURE_NOTIFY`]: crate::EventMask::SUBSTRUCTURE_NOTIFY @@ -1850,9 +1852,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1871,7 +1873,7 @@ derive_xrb! { /// The new coordinates of the `window`, relative to its parent's /// origin. - pub coords: Point, + pub coords: Coords, [_; ..], } @@ -1883,7 +1885,7 @@ derive_xrb! { /// This [event] is reported to the client selecting [`RESIZE_REDIRECT`] on /// the [window]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`RESIZE_REDIRECT`]: crate::EventMask::RESIZE_REDIRECT /// [`ConfigureWindow` request]: super::request::ConfigureWindow @@ -1893,9 +1895,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1943,7 +1945,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`STRUCTURE_NOTIFY`] on the /// [window], and to clients selecting [`SUBSTRUCTURE_NOTIFY`] on its parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`CirculateWindow` request]: super::request::CirculateWindow /// @@ -1955,9 +1957,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -1988,19 +1990,19 @@ derive_xrb! { /// This [event] is reported to the client selecting [`SUBSTRUCTURE_REDIRECT`] /// on the [window]'s parent. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`SUBSTRUCTURE_REDIRECT`]: crate::EventMask::SUBSTRUCTURE_REDIRECT /// [`CirculateWindow` request]: super::request::CirculateWindow #[derive(Debug, Derivative, X11Size, Readable, Writable)] #[derivative(Hash, PartialEq, Eq)] - pub struct CirculateRequest: Event(27) { + pub struct CirculateWindowRequest: Event(27) { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -2008,8 +2010,8 @@ derive_xrb! { /// The parent of the `window` the [`CirculateWindow` request] applies /// to. /// - /// This is the window that this `CirculateRequest` event was generated - /// on. + /// This is the window that this `CirculateWindowRequest` event was + /// generated on. /// /// [`CirculateWindow` request]: super::request::CirculateWindow pub parent: Window, @@ -2049,7 +2051,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`PROPERTY_CHANGE`] on the /// [window]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [`PROPERTY_CHANGE`]: crate::EventMask::PROPERTY_CHANGE #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -2058,9 +2060,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -2089,7 +2091,7 @@ derive_xrb! { /// # Recipients /// This [event] is reported to the current owner of a selection. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`SetSelectionOwner` request]: super::request::SetSelectionOwner #[derive(Debug, Derivative, X11Size, Readable, Writable)] #[derivative(Hash, PartialEq, Eq)] @@ -2097,9 +2099,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -2125,7 +2127,7 @@ derive_xrb! { /// # Recipients /// This [event] is reported to the owner of the selection. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`ConvertSelection` request]: super::request::ConvertSelection /// [`Selection` event]: Selection /// [`SendEvent` request]: super::request::SendEvent @@ -2135,9 +2137,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -2173,7 +2175,7 @@ derive_xrb! { /// This [event] is reported to the `requester` of a /// [`ConvertSelection` request]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`ConvertSelection` request]: super::request::ConvertSelection /// [`SendEvent` request]: super::request::SendEvent #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -2182,9 +2184,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -2250,7 +2252,7 @@ derive_xrb! { /// This [event] is reported to clients selecting [`COLORMAP_CHANGE`] on the /// window. /// - /// [event]: crate::message::Event + /// [event]: Event /// [window]: Window /// [colormap]: crate::Colormap /// [`colormap` attribute]: crate::Attributes::colormap @@ -2262,16 +2264,16 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, /// The window that this [event] relates to. /// - /// [event]: crate::message::Event + /// [event]: Event pub window: Window, /// The `window`'s [colormap]. /// @@ -2282,7 +2284,7 @@ derive_xrb! { /// [`colormap` attribute] was changed or because the `window`'s /// `colormap` was installed or uninstalled. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`colormap` attribute]: crate::Attributes::colormap pub detail: ColormapDetail, /// Whether the `window`'s `colormap` is currently installed. @@ -2351,7 +2353,7 @@ derive_xrb! { /// This [event] is reported to the [`SendEvent` request]'s `destination` /// [window]. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`SendEvent` request]: super::request::SendEvent /// [window]: Window #[derive(Debug, Derivative, X11Size, Readable, Writable)] @@ -2360,9 +2362,9 @@ derive_xrb! { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, @@ -2382,7 +2384,7 @@ derive_xrb! { /// The data contained in this [event]. /// - /// [event]: crate::message::Event + /// [event]: Event #[context(format => *format)] pub data: ClientMessageData, } @@ -2423,19 +2425,19 @@ derive_xrb! { /// # Recipients /// This [event] is reported to all clients. /// - /// [event]: crate::message::Event + /// [event]: Event /// [`SetModifierMapping`]: super::request::SetModifierMapping /// [`ChangeKeyboardMapping`]: super::request::ChangeKeyboardMapping - /// [`SetCursorMapping`]: super::request::SetCursorMapping + /// [`SetCursorMapping`]: super::request::SetButtonMapping #[derive(Debug, Derivative, X11Size, Readable, Writable)] #[derivative(Hash, PartialEq, Eq)] pub struct MappingChange: Event(34) { /// The [sequence number] associated with the last [request] related /// to this [event] that was received before this [event] was generated. /// - /// [sequence number]: crate::message::Event::sequence + /// [sequence number]: Event::sequence /// [request]: crate::message::Request - /// [event]: crate::message::Event + /// [event]: Event #[sequence] #[derivative(PartialEq = "ignore", Hash = "ignore")] pub sequence: u16, diff --git a/src/x11/reply.rs b/src/x11/reply.rs new file mode 100644 index 00000000..98330e3d --- /dev/null +++ b/src/x11/reply.rs @@ -0,0 +1,31 @@ +// 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 [core X11 protocol]. +//! +//! [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 +//! [core X11 protocol]: super + +// TODO: should these modules be private and re-exported, or public? +// or public and also re-exported? + +pub use color::*; +pub use font::*; +pub use graphics::*; +pub use input::*; +pub use meta::*; +pub use miscellaneous::*; +pub use window::*; + +pub mod color; +pub mod font; +pub mod graphics; +pub mod input; +pub mod meta; +pub mod miscellaneous; +pub mod window; diff --git a/src/x11/reply/color.rs b/src/x11/reply/color.rs new file mode 100644 index 00000000..250e5dd5 --- /dev/null +++ b/src/x11/reply/color.rs @@ -0,0 +1,374 @@ +// 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 [core X11 protocol] for +//! [requests that relate to colors]. +//! +//! [Replies] are messages sent from the X server to an X client in response to +//! a [request]. +//! +//! [Replies]: Reply +//! [request]: crate::message::Request +//! [core X11 protocol]: crate::x11 +//! +//! [requests that relate to colors]: request::color + +extern crate self as xrb; + +use derivative::Derivative; +use xrbk::{Buf, BufMut, ConstantX11Size, ReadResult, Readable, Writable, WriteResult, X11Size}; + +use xrbk_macro::derive_xrb; + +use crate::{ + message::Reply, + visual::{ColorId, RgbColor}, + x11::request, + Colormap, +}; + +derive_xrb! { + /// The [reply] to a [`ListInstalledColormaps` request]. + /// + /// [reply]: Reply + /// + /// [`ListInstalledColormaps` request]: request::ListInstalledColormaps + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct ListInstalledColormaps: Reply for request::ListInstalledColormaps { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `colormaps`. + #[allow(clippy::cast_possible_truncation)] + let colormaps_len: u16 = colormaps => colormaps.len() as u16, + [_; 22], + + /// The [colormaps] which are currently installed on the given + /// `target`'s [screen]. + /// + /// This list is in no particular order. + /// + /// This list has no indication as to which [colormaps] are contained in + /// the [screen]'s list of required [colormaps]. + /// + /// [colormaps]: Colormaps + /// [screen]: crate::visual::Screen + #[context(colormaps_len => usize::from(*colormaps_len))] + pub colormaps: Vec, + } + + /// The [reply] to an [`AllocateColor` request]. + /// + /// [reply]: Reply + /// + /// [`AllocateColor` request]: request::AllocateColor + #[doc(alias("AllocColor"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct AllocateColor: Reply for request::AllocateColor { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The actual RGB values that were allocated. + /// + /// These are the closest RGB values to those requested that the display + /// could provide. + pub actual_color: RgbColor, + [_; 2], + + /// The [`ColorId`] referring to the `actual_color`. + pub color_id: ColorId, + [_; ..], + } + + /// The [reply] to an [`AllocateNamedColor` request]. + /// + /// [reply]: Reply + /// + /// [`AllocateNamedColor` request]: request::AllocateNamedColor + #[doc(alias("AllocNamedColor"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct AllocateNamedColor: Reply for request::AllocateNamedColor { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The ideal or 'true' color which the name represents. + pub ideal_color: RgbColor, + /// The closest color that the display was able to provide. + pub actual_color: RgbColor, + [_; ..], + } + + /// The [reply] to an [`AllocateColorCells` request]. + /// + /// [reply]: Reply + /// + /// [`AllocateColorCells` request]: request::AllocateColorCells + #[doc(alias("AllocColorCells"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct AllocateColorCells: Reply for request::AllocateColorCells { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `colors`. + #[allow(clippy::cast_possible_truncation)] + let colors_len: u16 = colors => colors.len() as u16, + // The length of `plane_masks`. + #[allow(clippy::cast_possible_truncation)] + let plane_masks_len: u16 = plane_masks => plane_masks.len() as u16, + [_; 20], + + /// The colors that were used for the allocated [colormap] entries. + /// + /// [colormap]: Colormap + #[context(colors_len => usize::from(*colors_len))] + pub colors: Vec, + /// The bit plane masks that were used for the allocated [colormap] + /// entries. + /// + /// For [`VisualClass::GrayScale`] or [`VisualClass::PseudoColor`], each + /// plane mask will have one bit set to `1` (because there is only one + /// color channel). + /// + /// For [`VisualClass::DirectColor`], each plane mask will have 3 bits + /// sets to `1` (because there are three color channels: red, green, and + /// blue). + /// + /// No plane mask will have bits in common with any other plane mask, + /// nor with any of the `colors`. + /// + /// [colormap]: Colormap + /// + /// [`VisualClass::GrayScale`]: crate::visual::VisualClass::GrayScale + /// [`VisualClass::PseudoColor`]: crate::visual::VisualClass::PseudoColor + /// [`VisualClass::DirectColor`]: crate::visual::VisualClass::DirectColor + #[context(plane_masks_len => usize::from(*plane_masks_len))] + pub plane_masks: Vec, + } + + /// The [reply] to an [`AllocateColorPlanes` request]. + /// + /// [reply]: Reply + /// + /// [`AllocateColorPlanes` request]: request::AllocateColorPlanes + #[doc(alias("AllocColorPlanes"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct AllocateColorPlanes: Reply for request::AllocateColorPlanes { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `colors`. + #[allow(clippy::cast_possible_truncation)] + let colors_len: u16 = colors => colors.len() as u16, + [_; 2], + + /// The union of all the red bit plane masks which were applied to the + /// `colors` to produce the [colormap] entries which were allocated. + /// + /// [colormap]: Colormap + pub red_plane_mask: u32, + /// The union of all the green bit plane masks which were applied to the + /// `colors` to produce the [colormap] entries which were allocated. + /// + /// [colormap]: Colormap + pub green_plane_mask: u32, + /// The union of all the blue bit plane masks which were applied to the + /// `colors` to produce the [colormap] entries which were allocated. + /// + /// [colormap]: Colormap + pub blue_plane_mask: u32, + [_; 8], + + /// The colors that were combined with the plane masks to produce the + /// [colormap] entries which were allocated. + /// + /// [colormap]: Colormap + #[context(colors_len => usize::from(*colors_len))] + pub colors: Vec, + } +} + +/// The [reply] to a [`QueryColors` request]. +/// +/// [reply]: Reply +/// +/// [`QueryColors` request]: request::QueryColors +#[derive(Derivative, Debug)] +#[derivative(Hash, PartialEq, Eq)] +pub struct QueryColors { + /// 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 [RGB values] of the requested [colormap] entries. + /// + /// The [RGB values] returned for unallocated [colormap] entries is + /// undefined. + /// + /// [RGB values]: RgbColor + /// [colormap]: Colormap + pub colors: Vec, +} + +impl Reply for QueryColors { + type Request = request::QueryColors; + + fn sequence(&self) -> u16 { + self.sequence + } +} + +impl X11Size for QueryColors { + fn x11_size(&self) -> usize { + const HEADER: usize = 8; + + HEADER + u16::X11_SIZE + 22 + self.colors.x11_size() + } +} + +impl Readable for QueryColors { + fn read_from(buf: &mut impl Buf) -> ReadResult { + buf.advance(1); + let sequence = buf.get_u16(); + + let length = (buf.get_u32() as usize) * 4; + let buf = &mut buf.take(length - 8); + + let colors_len = buf.get_u16(); + buf.advance(22); + + let colors = { + let mut colors = vec![]; + + for _ in 0..colors_len { + colors.push(RgbColor::read_from(buf)?); + buf.advance(2); + } + + colors + }; + + Ok(Self { sequence, colors }) + } +} + +impl Writable for QueryColors { + #[allow(clippy::cast_possible_truncation)] + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + let buf = &mut buf.limit((self.length() as usize) * 4); + + buf.put_u8(1); + buf.put_u8(0); + self.sequence.write_to(buf)?; + buf.put_u32(self.length()); + + buf.put_u16(self.colors.len() as u16); + buf.put_bytes(0, 22); + + for color in &self.colors { + color.write_to(buf)?; + buf.put_bytes(0, 2); + } + + Ok(()) + } +} + +derive_xrb! { + /// The [reply] to a [`GetNamedColor` request]. + /// + /// [reply]: Reply + /// + /// [`GetNamedColor` request]: request::GetNamedColor + #[doc(alias("LookupColor"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetNamedColor: Reply for request::GetNamedColor { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The ideal [RGB values] of the color. + /// + /// [RGB values]: RgbColor + pub ideal_color: RgbColor, + /// The closest [RGB values] to the `ideal_color` that the display could + /// provide. + /// + /// [RGB values]: RgbColor + pub actual_color: RgbColor, + [_; ..], + } +} diff --git a/src/x11/reply/font.rs b/src/x11/reply/font.rs new file mode 100644 index 00000000..473888e4 --- /dev/null +++ b/src/x11/reply/font.rs @@ -0,0 +1,832 @@ +// 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 [core X11 protocol] for +//! [requests that relate to fonts]. +//! +//! [Replies] are messages sent from the X server to an X client in response to +//! a [request]. +//! +//! [Replies]: Reply +//! [request]: crate::message::Request +//! [core X11 protocol]: crate::x11 +//! +//! [requests that relate to fonts]: request::font + +extern crate self as xrb; + +use derivative::Derivative; + +use xrbk::{ + pad, + Buf, + BufMut, + ConstantX11Size, + ReadResult, + Readable, + ReadableWithContext, + Writable, + WriteResult, + X11Size, +}; +use xrbk_macro::{derive_xrb, Readable, Writable, X11Size}; + +use crate::{message::Reply, x11::request, Atom, LengthString8, String8}; + +/// A property of a font. +/// +/// The value of this property is uninterpreted by XRB. +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub struct FontProperty { + /// The name of the font property. + pub name: Atom, + /// The value of the property. + /// + /// This is represented as four individual `u8` values because it is not + /// necessarily one numerical value; it must not be subject to the byte + /// swapping that would occur for a `u32` value. + pub value: [u8; 4], +} + +/// Information about a particular character within a font. +/// +/// For a nonexistent character, all of these fields are zero. +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub struct CharacterInfo { + /// The extent of this character's appearance beyond its left edge. + /// + /// If this is negative, the character's appearance extends to the left of + /// its x coordinate. If this is positive, the character's appearance starts + /// after its x coordinate. + pub left_side_bearing: i16, + /// The extent of this character's appearance beyond its right edge. + /// + /// If this is negative, the character's appearance ends before its width. + /// If this is positive, the character's appearance extends beyond its + /// width. + pub right_side_bearing: i16, + + /// The width of this character - positive if it is read [`LeftToRight`], + /// negative if it is read [`RightToLeft`]. + /// + /// [`LeftToRight`]: DrawDirection::LeftToRight + /// [`RightToLeft`]: DrawDirection::RightToLeft + #[doc(alias = "character_width")] + pub width: i16, + + /// The extent of this character above the baseline. + pub ascent: i16, + /// The extent of this character at or below the baseline. + pub descent: i16, + + /// The interpretation of these character attributes depends on the X + /// server. + pub attributes: u16, +} + +impl ConstantX11Size for CharacterInfo { + const X11_SIZE: usize = 12; +} + +/// A hint as to whether most [`CharacterInfo`]s in a font have a positive or +/// negative width. +/// +/// A positive width means the character is [`LeftToRight`]. A negative width +/// means the character is [`RightToLeft`]. +/// +/// [`LeftToRight`]: DrawDirection::LeftToRight +/// [`RightToLeft`]: DrawDirection::RightToLeft +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum DrawDirection { + /// Most [`CharacterInfo`]s in the font have a positive width. + LeftToRight, + /// Most [`CharacterInfo`]s in the font have a negative width. + RightToLeft, +} + +impl ConstantX11Size for DrawDirection { + const X11_SIZE: usize = 1; +} + +derive_xrb! { + /// The [reply] to a [`QueryFont` request]. + /// + /// [reply]: Reply + /// + /// [`QueryFont` request]: request::QueryFont + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct QueryFont: Reply for request::QueryFont { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// A [`CharacterInfo`] representing the minimum bounds of all fields in + /// each [`CharacterInfo`] in `character_infos`. + pub min_bounds: CharacterInfo, + [_; 4], + + /// A [`CharacterInfo`] representing the maximum bounds of all fields in + /// each [`CharacterInfo`] in `character_infos`. + pub max_bounds: CharacterInfo, + [_; 4], + + /// If `min_major_index` and `max_major_index` are both zero, this is + /// the character index of the first element in `character_infos`. + /// Otherwise, this is a [`u8`] value used to index characters. + /// + /// If either `min_major_index` or `max_major_index` aren't zero, the + /// two indexes used to retrieve `character_infos` element `i` (counting + /// from `i = 0`) are: + /// ``` + /// # let i = 0; + /// # + /// # let first_character_or_min_minor_index = 0; + /// # let last_character_or_max_minor_index = 1; + /// # + /// # let min_major_index = 2; + /// # + /// let major_index_range = { + /// last_character_or_max_minor_index + /// - first_character_or_min_minor_index + /// + 1 + /// }; + /// + /// let major_index = i / major_index_range + min_major_index; + /// let minor_index = i % major_index_range + first_character_or_min_minor_index; + /// ``` + #[doc(alias = "min_char_or_byte2")] + pub first_character_or_min_minor_index: u16, + /// If `min_major_index` and `max_major_index` are both zero, this is + /// the character index of the last element in `character_infos`. + /// Otherwise, this is a [`u8`] value used to index characters. + /// + /// If either `min_major_index` or `max_major_index` aren't zero, the + /// two indexes used to retrieve `character_infos` element `i` (counting + /// from `i = 0`) are: + /// ``` + /// # let i = 0; + /// # + /// # let first_character_or_min_minor_index = 0; + /// # let last_character_or_max_minor_index = 1; + /// # + /// # let min_major_index = 2; + /// # + /// let major_index_range = { + /// last_character_or_max_minor_index + /// - first_character_or_min_minor_index + /// + 1 + /// }; + /// + /// let major_index = i / major_index_range + min_major_index; + /// let minor_index = i % major_index_range + first_character_or_min_minor_index; + /// ``` + #[doc(alias = "max_char_or_byte2")] + pub last_character_or_max_minor_index: u16, + + /// The character used when an undefined or nonexistent character is + /// used. + /// + /// If a font uses two bytes to index its characters (such as that used + /// for [`Char16`]), the first of the two bytes is found in the most + /// significant byte of this `fallback_character`, and the second of the + /// two bytes if found in the least significant byte. + /// + /// [`Char16`]: crate::Char16 + #[doc(alias("default_char", "default_character", "fallback_char"))] + pub fallback_character: u16, + + // The length of `properties`. + #[allow(clippy::cast_possible_truncation)] + let properties_len: u16 = properties => properties.len() as u16, + + /// A hint as to whether most [`CharacterInfo`s] in a font have a + /// positive or negative width. + /// + /// See [`DrawDirection`] for more information. + /// + /// [`CharacterInfo`s]: CharacterInfo + pub draw_direction: DrawDirection, + + /// The value of the major index used to retrieve the first element in + /// `character_infos`. + #[doc(alias = "min_byte1")] + pub min_major_index: u8, + /// The value of the major index used to retrieve the last element in + /// `character_infos`. + #[doc(alias = "max_byte1")] + pub max_major_index: u8, + + /// Whether all of the [`CharacterInfo`s] in `character_infos` have + /// nonzero bounds. + /// + /// [`CharacterInfo`s]: CharacterInfo + pub all_characters_exist: bool, + + /// The extent of the font above the baseline, used for determining line + /// spacing. + /// + /// Some specific characters may extend above this. + pub font_ascent: i16, + /// The extent of the font at or below the baseline, used for + /// determining line spacing. + /// + /// Some specific characters may extend below this. + pub font_descent: i16, + + // The length of `character_infos`. + #[allow(clippy::cast_possible_truncation)] + let character_infos_len: u32 = character_infos => character_infos.len() as u32, + + /// A list of [`FontProperty`s] associated with the font. + /// + /// [`FontProperty`s]: FontProperty + #[context(properties_len => usize::from(*properties_len))] + pub properties: Vec, + /// A list of the characters associated with the font, represented by + /// [`CharacterInfo`s]. + /// + /// [`CharacterInfo`s]: CharacterInfo + #[doc(alias = "char_infos")] + #[context(character_infos_len => *character_infos_len as usize)] + pub character_infos: Vec, + } + + /// The [reply] to a [`QueryTextExtents` request]. + /// + /// [reply]: Reply + /// + /// [`QueryTextExtents` request]: request::QueryTextExtents + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct QueryTextExtents: Reply for request::QueryTextExtents { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// A hint as to whether most characters in a font have a positive + /// `width` or a negative `width`. + /// + /// See [`DrawDirection`] for more information. + #[metabyte] + pub draw_direction: DrawDirection, + + /// The extent of the font above the baseline, used for determining line + /// spacing. + /// + /// Some specific characters may extend above this. + pub font_ascent: i16, + /// The extent of the font at or below the baseline, used for + /// determining line spacing. + /// + /// Some specific characters may extend below this. + pub font_descent: i16, + + /// The highest individual `ascent` of any character in `text`. + pub overall_ascent: i16, + /// The lowest individual `descent` of any character in `text`. + pub overall_descent: i16, + + /// The sum of the `width`s of each character in the `text`. + pub overall_width: i32, + + /// If the 'left side' of each character is the sum of the `width`s of + /// all characters before it plus its `left_side_bearing`, this is the + /// leftmost left side. + pub overall_left: i32, + /// If the 'right side' of each character is the sum of the `width`s of + /// all characters before it, plus its `width` and `right_side_bearing`, + /// this is the rightmost right side. + pub overall_right: i32, + [_; ..], + } + + /// The [reply] to a [`ListFonts` request]. + /// + /// [reply]: Reply + /// + /// [`ListFonts` request]: request::ListFonts + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct ListFonts: Reply for request::ListFonts { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `names`. + #[allow(clippy::cast_possible_truncation)] + let names_len: u16 = names => names.len() as u16, + [_; 22], + + /// The names of available fonts (no more than the given + /// `max_names_count` will appear here, though). + #[context(names_len => usize::from(*names_len))] + pub names: Vec, + [_; names => pad(names)], + } +} + +/// The [reply] to a [`ListFontsWithInfo` request]. +/// +/// The [`ListFontsWithInfo` request] is unique in that it has a series of +/// multiple replies, followed by a reply to terminate that series. +/// +/// [reply]: Reply +/// +/// [`ListFontsWithInfo` request]: request::ListFontsWithInfo +pub enum ListFontsWithInfo { + /// Information about one of the available fonts. + Font(FontWithInfo), + /// Indicates the end of the series of replies to the + /// [`ListFontsWithInfo` request]. + /// + /// [`ListFontsWithInfo` request]: request::ListFontsWithInfo + Terminate(TerminateListFontsWithInfo), +} + +impl Reply for ListFontsWithInfo { + type Request = request::ListFontsWithInfo; + + fn sequence(&self) -> u16 { + match self { + Self::Font(FontWithInfo { sequence, .. }) + | Self::Terminate(TerminateListFontsWithInfo { sequence, .. }) => *sequence, + } + } +} + +impl X11Size for ListFontsWithInfo { + fn x11_size(&self) -> usize { + match self { + Self::Font(reply) => reply.x11_size(), + Self::Terminate(last) => last.x11_size(), + } + } +} + +impl Readable for ListFontsWithInfo { + fn read_from(buf: &mut impl Buf) -> ReadResult + where + Self: Sized, + { + let name_len = buf.get_u8(); + let sequence = buf.get_u16(); + + Ok(match name_len { + zero if zero == 0 => Self::Terminate(<_>::read_with(buf, &sequence)?), + + other => Self::Font(<_>::read_with(buf, &(other, sequence))?), + }) + } +} + +impl Writable for ListFontsWithInfo { + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + match self { + Self::Font(reply) => reply.write_to(buf)?, + + Self::Terminate(last) => last.write_to(buf)?, + } + + Ok(()) + } +} + +/// A [reply] to a [`ListFontsWithInfo` request] that provides information about +/// one of the available fonts. +/// +/// A `FontWithInfo` [reply] is sent for every available font. A +/// [`TerminateListFontsWithInfo` reply] terminates the series. +/// +/// [reply]: Reply +/// +/// [`ListFontsWithInfo` request]: request::ListFontsWithInfo +/// [`TerminateListFontsWithInfo` reply]: TerminateListFontsWithInfo +#[derive(Derivative, Debug)] +#[derivative(Hash, PartialEq, Eq)] +pub struct FontWithInfo { + /// 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, + + /// A [`CharacterInfo`] representing the minimum bounds of all fields in + /// each [`CharacterInfo`] in `character_infos`. + pub min_bounds: CharacterInfo, + /// A [`CharacterInfo`] representing the maximum bounds of all fields in + /// each [`CharacterInfo`] in `character_infos`. + pub max_bounds: CharacterInfo, + + /// If `min_major_index` and `max_major_index` are both zero, this is the + /// character index of the first element in `character_infos`. Otherwise, + /// this is a [`u8`] value used to index characters. + /// + /// If either `min_major_index` or `max_major_index` aren't zero, the + /// two indexes used to retrieve `character_infos` element `i` (counting + /// from `i = 0`) are: + /// ``` + /// # let i = 0; + /// # + /// # let first_character_or_min_minor_index = 0; + /// # let last_character_or_max_minor_index = 1; + /// # + /// # let min_major_index = 2; + /// # + /// let major_index_range = { + /// last_character_or_max_minor_index + /// - first_character_or_min_minor_index + /// + 1 + /// }; + /// + /// let major_index = i / major_index_range + min_major_index; + /// let minor_index = i % major_index_range + first_character_or_min_minor_index; + /// ``` + #[doc(alias = "min_char_or_byte2")] + pub first_character_or_min_minor_index: u16, + /// If `min_major_index` and `max_major_index` are both zero, this is + /// the character index of the last element in `character_infos`. + /// Otherwise, this is a [`u8`] value used to index characters. + /// + /// If either `min_major_index` or `max_major_index` aren't zero, the + /// two indexes used to retrieve `character_infos` element `i` (counting + /// from `i = 0`) are: + /// ``` + /// # let i = 0; + /// # + /// # let first_character_or_min_minor_index = 0; + /// # let last_character_or_max_minor_index = 1; + /// # + /// # let min_major_index = 2; + /// # + /// let major_index_range = { + /// last_character_or_max_minor_index + /// - first_character_or_min_minor_index + /// + 1 + /// }; + /// + /// let major_index = i / major_index_range + min_major_index; + /// let minor_index = i % major_index_range + first_character_or_min_minor_index; + /// ``` + #[doc(alias = "max_char_or_byte2")] + pub last_character_or_max_minor_index: u16, + + /// The character used when an undefined or nonexistent character is + /// used. + /// + /// If a font uses two bytes to index its characters (such as that used + /// for [`Char16`]), the first of the two bytes is found in the most + /// significant byte of this `fallback_character`, and the second of the + /// two bytes if found in the least significant byte. + /// + /// [`Char16`]: crate::Char16 + #[doc(alias = "default_char")] + pub fallback_character: u16, + + /// A hint as to whether most [`CharacterInfo`s] in a font have a + /// positive or negative width. + /// + /// See [`DrawDirection`] for more information. + /// + /// [`CharacterInfo`s]: CharacterInfo + pub draw_direction: DrawDirection, + + /// The value of the major index used to retrieve the first character in the + /// font. + #[doc(alias = "min_byte1")] + pub min_major_index: u8, + /// The value of the major index used to retrieve the last character in the + /// font. + #[doc(alias = "max_byte1")] + pub max_major_index: u8, + + /// Whether all of the characters in the font have nonzero bounds. + pub all_chars_exist: bool, + + /// The extent of the font above the baseline, used for determining line + /// spacing. + /// + /// Some specific characters may extend above this. + pub font_ascent: i16, + /// The extent of the font at or below the baseline, used for + /// determining line spacing. + /// + /// Some specific characters may extend below this. + pub font_descent: i16, + + /// A hint as to how many more [`FontWithInfo` replies] there will be. + /// + /// Note that this is only a hint: there may be more or less replies than + /// this number. A `replies_hint` of zero does not guarantee that there will + /// be no more [`FontWithInfo` replies]: the only way to know that is to + /// receive a [`TerminateListFontsWithInfo` reply]. + /// + /// [`FontWithInfo` replies]: FontWithInfo + /// [`TerminateListFontsWithInfo` reply]: TerminateListFontsWithInfo + pub replies_hint: u32, + + /// A list of [`FontProperty`s] associated with the font. + /// + /// [`FontProperty`s]: FontProperty + pub properties: Vec, + + /// The name of this font. + pub name: String8, +} + +impl X11Size for FontWithInfo { + fn x11_size(&self) -> usize { + const CONSTANT_SIZES: usize = u8::X11_SIZE // `1` + + u8::X11_SIZE // length of `name` + + u16::X11_SIZE // `sequence` + + u32::X11_SIZE // length + + CharacterInfo::X11_SIZE // `min_bounds` + + 4 // 4 unused bytes + + CharacterInfo::X11_SIZE // `max_bounds` + + 4 // 4 unused bytes + + u16::X11_SIZE // `first_character_or_min_minor_index` + + u16::X11_SIZE // `last_character_or_max_minor_index` + + u16::X11_SIZE // `fallback_character` + + u16::X11_SIZE // length of `properties` + + DrawDirection::X11_SIZE // `draw_direction` + + u8::X11_SIZE // `min_major_index` + + u8::X11_SIZE // `max_major_index` + + bool::X11_SIZE // `all_chars_exist` + + i16::X11_SIZE // `font_ascent` + + i16::X11_SIZE // `font_descent` + + u32::X11_SIZE; // `replies_hint` + + CONSTANT_SIZES + self.properties.x11_size() + self.name.x11_size() + pad(&self.name) + } +} + +impl ReadableWithContext for FontWithInfo { + type Context = (u8, u16); + + fn read_with(buf: &mut impl Buf, (name_len, sequence): &(u8, u16)) -> ReadResult { + let name_len = usize::from(*name_len); + + // We skip the first 4 bytes because: + // - the first, `1`, was required to know this is a reply + // - the second was required to know the `name_len` + // - the third and fourth - the sequence - were required to know that this is a + // `ListFontsWithInfo` reply + + // Read the length - take away the 8 bytes we've already read. + let length = ((buf.get_u32() as usize) * 4) + (32 - 8); + // Limit `buf` by the read `length`. + let buf = &mut buf.take(length); + + let min_bounds = CharacterInfo::read_from(buf)?; + buf.advance(4); // 4 unused bytes + + let max_bounds = CharacterInfo::read_from(buf)?; + buf.advance(4); // 4 unused bytes + + let first_character_or_min_minor_index = u16::read_from(buf)?; + let last_character_or_max_minor_index = u16::read_from(buf)?; + + let fallback_character = u16::read_from(buf)?; + + let properties_len = usize::from(u16::read_from(buf)?); + + let draw_direction = DrawDirection::read_from(buf)?; + + let min_major_index = u8::read_from(buf)?; + let max_major_index = u8::read_from(buf)?; + + let all_chars_exist = bool::read_from(buf)?; + + let font_ascent = i16::read_from(buf)?; + let font_descent = i16::read_from(buf)?; + + let replies_hint = u32::read_from(buf)?; + + let properties = >::read_with(buf, &properties_len)?; + + let name = String8::read_with(buf, &name_len)?; + buf.advance(pad(&name)); + + Ok(Self { + sequence: *sequence, + + min_bounds, + max_bounds, + + first_character_or_min_minor_index, + last_character_or_max_minor_index, + + fallback_character, + + draw_direction, + + min_major_index, + max_major_index, + + all_chars_exist, + + font_ascent, + font_descent, + + replies_hint, + + properties, + + name, + }) + } +} + +impl Writable for FontWithInfo { + #[allow(clippy::cast_possible_truncation)] + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + buf.put_u8(1); + buf.put_u8(self.name.len() as u8); + self.sequence.write_to(buf)?; + + buf.put_u32(((self.x11_size() - 32) / 4) as u32); + + self.min_bounds.write_to(buf)?; + // 4 unused bytes. + buf.put_bytes(0, 4); + + self.max_bounds.write_to(buf)?; + // 4 unused bytes. + buf.put_bytes(0, 4); + + self.first_character_or_min_minor_index.write_to(buf)?; + self.last_character_or_max_minor_index.write_to(buf)?; + + self.fallback_character.write_to(buf)?; + + buf.put_u16(self.properties.len() as u16); + + self.draw_direction.write_to(buf)?; + + self.min_major_index.write_to(buf)?; + self.max_major_index.write_to(buf)?; + + self.all_chars_exist.write_to(buf)?; + + self.font_ascent.write_to(buf)?; + self.font_descent.write_to(buf)?; + + self.replies_hint.write_to(buf)?; + + self.properties.write_to(buf)?; + + self.name.write_to(buf)?; + // Padding bytes for `name`. + buf.put_bytes(0, pad(&self.name)); + + Ok(()) + } +} + +/// A [reply] to a [`ListFontsWithInfo` request] that represents the final +/// [reply] sent for that [request]. +/// +/// [reply]: Reply +/// [request]: crate::message::Request +/// +/// [`ListFontsWithInfo` request]: request::ListFontsWithInfo +#[derive(Derivative, Debug)] +#[derivative(Hash, PartialEq, Eq)] +pub struct TerminateListFontsWithInfo { + /// 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, +} + +impl ConstantX11Size for TerminateListFontsWithInfo { + const X11_SIZE: usize = 60; +} + +impl X11Size for TerminateListFontsWithInfo { + fn x11_size(&self) -> usize { + Self::X11_SIZE + } +} + +impl Writable for TerminateListFontsWithInfo { + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + // Indicates that this is a reply. + buf.put_u8(1); + // Indicates that this is the last `ListFontsWithInfo` reply. + buf.put_u8(0); + + // The sequence number. + buf.put_u16(self.sequence); + + // Length - the number of 4-byte units after the 32nd byte in this + // reply. + buf.put_u32(7); + + // 52 unused bytes. + buf.put_bytes(0, 52); + + Ok(()) + } +} + +impl ReadableWithContext for TerminateListFontsWithInfo { + type Context = u16; + + fn read_with(buf: &mut impl Buf, sequence: &u16) -> ReadResult { + // We skip the first 4 bytes because: + // - the first, `1`, was required to know this is a reply + // - the second was required to know this is the last reply for + // ListFontsWithInfo + // - the third and fourth - the sequence - were required to know that this is a + // `ListFontsWithInfo` reply + + // Then we skip the length because we know what it is meant to be... should + // probably verify that... + buf.advance(4); + + // And then skip the 52 remaining unused bytes. + buf.advance(52); + + Ok(Self { + sequence: *sequence, + }) + } +} + +derive_xrb! { + /// The [reply] to a [`GetFontSearchDirectories` request]. + /// + /// [reply]: Reply + /// + /// [`GetFontSearchDirectories` request]: request::GetFontSearchDirectories + #[doc(alias = "GetFontPath")] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetFontSearchDirectories: Reply for request::GetFontSearchDirectories { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `directories`. + #[allow(clippy::cast_possible_truncation)] + let directories_len: u16 = directories => directories.len() as u16, + [_; 22], + + /// The directories that are searched in the order listed. + #[doc(alias = "path")] + #[context(directories_len => usize::from(*directories_len))] + pub directories: Vec, + [_; directories => pad(directories)], + } +} diff --git a/src/x11/reply/graphics.rs b/src/x11/reply/graphics.rs new file mode 100644 index 00000000..7a245a2b --- /dev/null +++ b/src/x11/reply/graphics.rs @@ -0,0 +1,72 @@ +// 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 [core X11 protocol] for +//! [requests that relate to graphics operations]. +//! +//! [Replies] are messages sent from the X server to an X client in response to +//! a [request]. +//! +//! [Replies]: Reply +//! [request]: crate::message::Request +//! [core X11 protocol]: crate::x11 +//! +//! [requests that relate to graphics operations]: request::graphics + +mod config; +pub use config::*; + +extern crate self as xrb; + +use derivative::Derivative; + +use xrbk::pad; +use xrbk_macro::derive_xrb; + +use crate::{message::Reply, visual::VisualId, x11::request}; + +derive_xrb! { + /// The [reply] to a [`CaptureImage` request]. + /// + /// [reply]: Reply + /// + /// [`CaptureImage` request]: request::CaptureImage + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct CaptureImage: Reply for request::CaptureImage { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The depth of the `target` [drawable] when it was created. + /// + /// [drawable]: crate::Drawable + #[metabyte] + pub depth: u8, + + /// The visual type of the `target` if it is a [window]. + /// + /// If the `target` is a [pixmap], this is [`None`]. + /// + /// [window]: crate::Window + /// [pixmap]: crate::Pixmap + pub visual: Option, + [_; 20], + + // FIXME: how do we know what is padding and what is data????? + /// The image's data. + #[context(self::remaining => remaining)] + pub data: Vec, + [_; data => pad(data)], + } +} diff --git a/src/x11/reply/graphics/config.rs b/src/x11/reply/graphics/config.rs new file mode 100644 index 00000000..a2356cfa --- /dev/null +++ b/src/x11/reply/graphics/config.rs @@ -0,0 +1,45 @@ +// 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/. + +extern crate self as xrb; + +use crate::{message::Reply, x11::request, Dimensions}; + +use derivative::Derivative; +use xrbk_macro::derive_xrb; + +derive_xrb! { + /// The [reply] to a [`QueryIdealDimensions` request]. + /// + /// [reply]: Reply + /// + /// [`QueryIdealDimensions` request]: request::QueryIdealDimensions + #[doc(alias("QueryBestSize"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct QueryIdealDimensions: Reply for request::QueryIdealDimensions { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The ideal [dimensions], as described in the + /// [`QueryIdealDimensions` request]. + /// + /// [dimensions]: Dimensions + /// + /// [`QueryIdealDimensions` request]: request::QueryIdealDimensions + #[doc(alias("width", "height", "dimensions", "ideal_width", "ideal_height"))] + pub ideal_dimensions: Dimensions, + [_; ..], + } +} diff --git a/src/x11/reply/input.rs b/src/x11/reply/input.rs new file mode 100644 index 00000000..4701b594 --- /dev/null +++ b/src/x11/reply/input.rs @@ -0,0 +1,903 @@ +// 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 [core X11 protocol] for +//! [requests that relate to input devices, grabs, and coordinates]. +//! +//! [Replies] are messages sent from the X server to an X client in response to +//! a [request]. +//! +//! [Replies]: Reply +//! [request]: crate::message::Request +//! [core X11 protocol]: crate::x11 +//! +//! [requests that relate to input devices, grabs, and coordinates]: request::input + +extern crate self as xrb; + +use array_init::array_init; +use derivative::Derivative; +use xrbk::{Buf, BufMut, ConstantX11Size, ReadResult, Readable, Writable, WriteResult, X11Size}; + +use xrbk_macro::{derive_xrb, Readable, Writable, X11Size}; + +use crate::{ + message::Reply, + unit::{Hz, Ms, Percentage, Px}, + x11::{ + request, + request::{Fraction, RevertFocus}, + }, + Button, + Coords, + FocusWindow, + GrabStatus, + Keycode, + Keysym, + ModifierMask, + Timestamp, + Toggle, + Window, +}; + +derive_xrb! { + /// The [reply] to a [`GrabCursor` request]. + /// + /// [reply]: Reply + /// + /// [`GrabCursor` request]: request::GrabCursor + #[doc(alias = "GrabPointer")] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GrabCursor: Reply for request::GrabCursor { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The status of the attempted grab. + /// + /// See [`GrabStatus`] for more information. + #[doc(alias = "status")] + #[metabyte] + pub grab_status: GrabStatus, + + [_; ..], + } + + /// The [reply] to a [`GrabKeyboard` request]. + /// + /// [reply]: Reply + /// + /// [`GrabKeyboard` request]: request::GrabKeyboard + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GrabKeyboard: Reply for request::GrabKeyboard { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The status of the attempted grab. + /// + /// See [`GrabStatus`] for more information. + #[doc(alias = "status")] + #[metabyte] + pub grab_status: GrabStatus, + + [_; ..], + } + + + /// The [reply] to a [`QueryCursorLocation` request]. + /// + /// [reply]: Reply + /// + /// [`QueryCursorLocation` request]: request::QueryCursorLocation + #[doc(alias("QueryPointer, QueryCursor, GetCursorPos, GetCursorLocation"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct QueryCursorLocation: Reply for request::QueryCursorLocation { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// Whether the cursor is on the `same_screen` as the given `target` + /// [window]. + /// + /// [window]: Window + #[metabyte] + pub same_screen: bool, + + /// The root [window] which the cursor is located within. + /// + /// [window]: Window + pub root: Window, + /// The child [window] containing the cursor, if any. + /// + /// If the cursor is not on the `same_screen` (i.e., `same_screen` is + /// `false`), this will always be [`None`]. + /// + /// [window]: Window + // TODO: should always be [`None`] if `same_screen` is false + pub child: Option, + + /// The coordinates of the cursor relative to the top-left corner of the + /// `root` [window]. + /// + /// [window]: Window + pub root_coords: Coords, + /// The coordinates of the cursor relative to the top-left corner of the + /// given `target` [window]. + /// + /// [window]: Window + // TODO: should always be [`None`] if `same_screen` is false + pub target_coords: Coords, + + /// The currently held mouse buttons and modifier keys. + pub modifiers: ModifierMask, + [_; ..], + } +} + +/// The coordinates of the cursor at a certain [time]. +/// +/// This is used in the [`GetMotionHistory` reply]. +/// +/// [time]: Timestamp +/// +/// [`GetMotionHistory` reply]: GetMotionHistory +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub struct TimeCoords { + /// The [time] at which the cursor was at the `coords`. + /// + /// [time]: Timestamp + pub time: Timestamp, + /// The coordinates of the cursor at the `time`. + pub coords: Coords, +} + +derive_xrb! { + /// The [reply] to a [`GetMotionHistory` request]. + /// + /// [reply]: Reply + /// + /// [`GetMotionHistory` request]: request::GetMotionHistory + #[doc(alias = "GetMotionEvents")] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetMotionHistory: Reply for request::GetMotionHistory { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `motion_history`. + #[allow(clippy::cast_possible_truncation)] + let motion_history_len: u32 = motion_history => motion_history.len() as u32, + [_; 20], + + /// The recorded cursor motion between the `start` and `end` times + /// (inclusive) for the given `target` [window]. + /// + /// [window]: Window + #[context(motion_history_len => *motion_history_len as usize)] + pub motion_history: Vec, + } + + /// The [reply] to a [`ConvertCoordinates` request]. + /// + /// [reply]: Reply + /// + /// [`ConvertCoordinates` request]: request::ConvertCoordinates + #[doc(alias = "TranslateCoordinates")] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct ConvertCoordinates: Reply for request::ConvertCoordinates { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// Whether the `original` [window] and the `output` [window] are on the + /// same [screen]. + /// + /// [window]: Window + /// [screen]: crate::visual::Screen + #[metabyte] + pub same_screen: bool, + + /// If the `output_coords` are contained within a mapped child of the + /// `output` [window], this is that child. + /// + /// If `same_screen` is `false`, this is always [`None`]. + /// + /// [window]: Window + // TODO: should always be [`None`] if `same_screen` is false + pub child: Option, + + /// The converted coordinates which are now relative to the top-left + /// corner of the `output` [window]. + /// + // FIXME: should always be [`None`], but requires overriding the reading + // behavior here + /// If `same_screen` is `false`, these are always zero. + /// + /// [window]: Window + #[doc(alias("dst_x", "dst_y", "dst_coords", "destination_coords"))] + pub output_coords: Coords, + [_; ..], + } + + /// The [reply] to a [`GetFocus` request]. + /// + /// [reply]: Reply + /// + /// [`GetFocus` request]: request::GetFocus + #[doc(alias = "GetInputFocus")] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetFocus: Reply for request::GetFocus { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// What the focus will retain to if the focused [window] becomes + /// unviewable. + /// + /// [window]: Window + #[metabyte] + pub revert_to: RevertFocus, + + /// The current focus. + pub focus: FocusWindow, + [_; ..], + } + + /// The [reply] to a [`QueryKeyboard` request]. + /// + /// [reply]: Reply + /// + /// [`QueryKeyboard` request]: request::QueryKeyboard + #[doc(alias = "QueryKeymap")] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct QueryKeyboard: Reply for request::QueryKeyboard { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// A bit vector representing the currently held keys of the keyboard. + /// + /// A bit is `0` if the key is not held, and `1` if it is held. Byte + /// `N`, starting at `0`, contains the bits for keys `8N` to `8N + 7`. + /// The least significant bit in the byte represents key `8N`. + pub keys: [u8; 32], + } +} + +/// The [keysyms] mapped to a particular [keycode]. +/// +/// [keysyms]: Keysym +/// [keycode]: Keycode +pub type KeyMapping = Vec; + +/// The [reply] to a [`GetKeyboardMapping` request]. +/// +/// [reply]: Reply +/// +/// [`GetKeyboardMapping` request]: request::GetKeyboardMapping +#[derive(Derivative, Debug)] +#[derivative(Hash, PartialEq, Eq)] +pub struct GetKeyboardMapping { + /// 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 mapping of [keysyms] for each [keycode] in the specified `range`. + /// + /// [keycode]: Keycode + /// [keysyms]: Keysym + pub mappings: Vec, +} + +impl Reply for GetKeyboardMapping { + type Request = request::GetKeyboardMapping; + + fn sequence(&self) -> u16 { + self.sequence + } +} + +impl X11Size for GetKeyboardMapping { + fn x11_size(&self) -> usize { + const HEADER: usize = 8; + const CONSTANT_SIZES: usize = HEADER + 24; + + CONSTANT_SIZES + self.mappings.x11_size() + } +} + +impl Readable for GetKeyboardMapping { + fn read_from(buf: &mut impl Buf) -> ReadResult + where + Self: Sized, + { + const HEADER: usize = 8; + + // Header {{{ + + // FIXME: actually, replies need to have their first 4 bytes read before + // the type of reply can be determined, so `keysyms_per_keycode` + // and `sequence` should be context for `ReadableWithContext`. + // + // FIXME: This is a change that needs to be done for all replies... + buf.advance(1); + let keysyms_per_keycode = buf.get_u8(); + let sequence = buf.get_u16(); + + let length = (buf.get_u32() as usize) * 4; + let buf = &mut buf.take(length - HEADER); + + // }}} + + // 24 unused bytes. + buf.advance(24); + + let mappings = { + let mapping_size = usize::from(keysyms_per_keycode) * Keysym::X11_SIZE; + let mappings_len = buf.remaining() / mapping_size; + + let mut mappings = vec![]; + + for _ in 0..mappings_len { + let mut keysyms = vec![]; + + for _ in 0..keysyms_per_keycode { + keysyms.push(Keysym::read_from(buf)?); + } + + mappings.push(keysyms); + } + + mappings + }; + + Ok(Self { sequence, mappings }) + } +} + +impl Writable for GetKeyboardMapping { + #[allow(clippy::cast_possible_truncation)] + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + let buf = &mut buf.limit(self.x11_size()); + + // Header {{{ + + // Indicates that this is a reply. + buf.put_u8(1); + // The number of keysyms in each mapping. + let mapping_size = self.mappings.x11_size() / self.mappings.len(); + let keysyms_per_keycode = (mapping_size / Keysym::X11_SIZE) as u8; + keysyms_per_keycode.write_to(buf)?; + // The sequence number. + self.sequence.write_to(buf)?; + + // The message length. + self.length().write_to(buf)?; + + // }}} + + // 24 unused bytes. + buf.put_bytes(0, 24); + + self.mappings.write_to(buf)?; + + Ok(()) + } +} + +derive_xrb! { + /// The [reply] to a [`GetKeyboardOptions` request]. + /// + /// [reply]: Reply + /// + /// [`GetKeyboardOptions` request]: request::GetKeyboardOptions + #[doc(alias("GetKeyboardControl"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetKeyboardOptions: Reply for request::GetKeyboardOptions { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// Whether the global [auto repeat mode] is enabled. + /// + /// If the global auto repeat mode is disabled, no keys can have auto + /// repeat applied. If the global auto repeat mode is enabled, keys + /// which have their own auto repeat enabled will be repeated. + /// + /// [auto repeat mode]: crate::set::KeyboardOptions::auto_repeat_mode + #[doc(alias("global_auto_repeat"))] + #[metabyte] + pub global_auto_repeat_mode: Toggle, + + /// A bitmask representing whether each [LED] is lit. + /// + /// The least significant bit represents the state of [LED] 1. The most + /// significant bit represents the state of [LED] 32. + /// + /// [LED]: crate::set::Led + pub led_mask: u32, + + /// The volume of key clicks. + /// + /// See [`KeyboardOptions::key_click_volume`] for more information. + /// + /// [`KeyboardOptions::key_click_volume`]: crate::set::KeyboardOptions::key_click_volume + #[doc(alias("key_click_percent"))] + pub key_click_volume: Percentage, + + /// The volume of the bell. + /// + /// See [`KeyboardOptions::bell_volume`] for more information. + /// + /// [`KeyboardOptions::bell_volume`]: crate::set::KeyboardOptions::bell_volume + #[doc(alias("bell_percent"))] + pub bell_volume: Percentage, + /// The pitch of the bell. + /// + /// See [`KeyboardOptions::bell_pitch`] for more information. + /// + /// [`KeyboardOptions::bell_pitch`]: crate::set::KeyboardOptions::bell_pitch + pub bell_pitch: Hz, + /// The duration for which the bell rings. + /// + /// See [`KeyboardOptions::bell_duration`] for more information. + /// + /// [`KeyboardOptions::bell_duration`]: crate::set::KeyboardOptions::bell_duration + pub bell_duration: Ms, + [_; 2], + + /// A bit vector representing whether each key has [auto repeat mode] + /// enabled. + /// + /// Byte `N`, starting at `0`, contains the bits for [keycodes] `8N` to + /// `8N + 7`. The least significant bit in each byte represents key + /// `8N`. + /// + /// See [`KeyboardOptions::auto_repeat_mode`] for more information. + /// + /// [keycodes]: Keycode + /// [auto repeat mode]: crate::set::KeyboardOptions::auto_repeat_mode + /// + /// [`KeyboardOptions::auto_repeat_mode`]: crate::set::KeyboardOptions::auto_repeat_mode + #[doc(alias("auto_repeats"))] + pub auto_repeat_modes: [u8; 32], + } + + /// The [reply] to a [`GetCursorOptions` request]. + /// + /// [reply]: Reply + /// + /// [`GetCursorOptions` request]: request::GetCursorOptions + #[doc(alias("GetPointerControl", "GetPointerOptions", "GetCursorControl"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetCursorOptions: Reply for request::GetCursorOptions { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The multiplier applied to the acceleration of the cursor when the + /// [`threshold`] is exceeded. + /// + /// [`threshold`]: GetCursorOptions::threshold + pub acceleration: Fraction>, + /// The threshold speed which the cursor must exceed for the + /// [`acceleration`] multiplier to be applied. + /// + /// [`acceleration`]: GetCursorOptions::acceleration + pub threshold: Px, + [_; ..], + } +} + +/// Whether a [`SetButtonMapping` request] was successful. +/// +/// This is used in the [`SetButtonMapping` reply]. +/// +/// [`SetButtonMapping` request]: request::SetButtonMapping +/// [`SetButtonMapping` reply]: SetButtonMapping +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum SetButtonMappingStatus { + /// The [`SetButtonMapping` request] was successful. + /// + /// [`SetButtonMapping` request]: request::SetButtonMapping + Success, + + /// The [`SetButtonMapping` request] was unsuccessful because it specified + /// buttons which are currently held. + /// + /// The mapping of mouse buttons cannot be changed while they are held. + /// + /// [`SetButtonMapping` request]: request::SetButtonMapping + Busy, +} + +derive_xrb! { + /// The [reply] to a [`SetButtonMapping` request]. + /// + /// [reply]: Reply + /// + /// [`SetButtonMapping` request]: request::SetButtonMapping + #[doc(alias("SetPointerMapping", "SetCursorMapping"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct SetButtonMapping: Reply for request::SetButtonMapping { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// Whether the [`SetButtonMapping` request] was successful. + /// + /// See [`SetButtonMappingStatus`] for more information. + /// + /// [`SetButtonMapping` request]: request::SetButtonMapping + pub status: SetButtonMappingStatus, + [_; ..], + } + + /// The [reply] to a [`GetButtonMapping` request]. + /// + /// [reply]: Reply + /// + /// [`GetButtonMapping` request]: request::GetButtonMapping + #[doc(alias("GetPointerMapping", "GetCursorMapping"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetButtonMapping: Reply for request::GetButtonMapping { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `mappings`. + #[metabyte] + #[allow(clippy::cast_possible_truncation)] + let mappings_len: u8 = mappings => mappings.len() as u8, + [_; 24], + + /// The mapping of the [mouse buttons]. + /// + /// [`None`] means the [mouse button] at that particular position, + /// starting with position 1 (at index 0), is disabled. [`Some`] means + /// it is remapped to the given other [button]. + /// + /// [mouse buttons]: Button + /// [mouse button]: Button + /// [button]: Button + #[context(mappings_len => usize::from(*mappings_len))] + pub mappings: Vec>, + } +} + +/// Whether a [`SetModifierMapping` request] was [successful]. +/// +/// This is used in the [`SetModifierMapping` reply]. +/// +/// [successful]: SetModifierMappingStatus::Success +/// +/// [`SetModifierMapping` request]: request::SetModifierMapping +/// [`SetModifierMapping` reply]: SetModifierMapping +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum SetModifierMappingStatus { + /// The [`SetModifierMapping` request] was successful. + /// + /// [`SetModifierMapping` request]: request::SetModifierMapping + Success, + + /// The [`SetModifierMapping` request] failed because either the currently + /// mapped modifier keys or specified new modifier keys are currently held. + /// + /// A [`SetModifierMapping` request] cannot be applied unless both the + /// currently mapped modifier keys and the keys which are specified to be + /// the new modifier keys are not held. + /// + /// [`SetModifierMapping` request]: request::SetModifierMapping + Busy, + /// The [`SetModifierMapping` request] failed because the X server rejected + /// it. + /// + /// The X server rejects a [`SetModifierMapping` request] if it has placed + /// some additional bounds on the modifiers which it is enforcing. For + /// example, this could be because multiple keys per modifier are not + /// supported or because auto-repeat cannot be disabled for certain keys. + /// + /// [`SetModifierMapping` request]: request::SetModifierMapping + Rejected, +} + +derive_xrb! { + /// The [reply] to a [`SetModifierMapping` request]. + /// + /// [reply]: Reply + /// + /// [`SetModifierMapping` request]: request::SetModifierMapping + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct SetModifierMapping: Reply for request::SetModifierMapping { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// Whether the [`SetModifierMapping` request] was [successful]. + /// + /// See [`SetModifierMappingStatus`] for more information. + /// + /// [successful]: SetModifierMappingStatus::Success + /// + /// [`SetModifierMapping` request]: request::SetModifierMapping + #[metabyte] + pub status: SetModifierMappingStatus, + + [_; 24], + } +} + +/// The [reply] to a [`GetModifierMapping` request]. +/// +/// [reply]: Reply +/// +/// [`GetModifierMapping` request]: request::GetModifierMapping +#[derive(Derivative, Debug)] +#[derivative(Hash, PartialEq, Eq)] +pub struct GetModifierMapping { + /// 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 [keycodes] mapped to the shift modifier. + /// + /// [keycodes]: Keycode + pub shift_keycodes: Vec, + /// The [keycodes] mapped to the caps lock modifier. + /// + /// [keycodes]: Keycode + pub capslock_keycodes: Vec, + /// The [keycodes] mapped to the control modifier. + /// + /// [keycodes]: Keycode + pub ctrl_keycodes: Vec, + + /// The [keycodes] mapped to the Mod1 modifier. + /// + /// [keycodes]: Keycode + pub mod1_keycodes: Vec, + /// The [keycodes] mapped to the Mod2 modifier. + /// + /// [keycodes]: Keycode + pub mod2_keycodes: Vec, + /// The [keycodes] mapped to the Mod3 modifier. + /// + /// [keycodes]: Keycode + pub mod3_keycodes: Vec, + /// The [keycodes] mapped to the Mod4 modifier. + /// + /// This is typically the key variously called 'super', 'meta', 'windows + /// key', 'cmd', etc. + /// + /// [keycodes]: Keycode + pub mod4_keycodes: Vec, + /// The [keycodes] mapped to the Mod5 modifier. + /// + /// [keycodes]: Keycode + pub mod5_keycodes: Vec, +} + +impl GetModifierMapping { + fn max_keycodes_len(&self) -> usize { + [ + &self.shift_keycodes, + &self.capslock_keycodes, + &self.ctrl_keycodes, + &self.mod1_keycodes, + &self.mod2_keycodes, + &self.mod3_keycodes, + &self.mod4_keycodes, + &self.mod5_keycodes, + ] + .into_iter() + .map(Vec::len) + .max() + .expect("there's definitely more than one element") + } +} + +impl Reply for GetModifierMapping { + type Request = request::GetModifierMapping; + + fn sequence(&self) -> u16 { + self.sequence + } +} + +impl X11Size for GetModifierMapping { + fn x11_size(&self) -> usize { + const HEADER: usize = 8; + const CONSTANT_SIZES: usize = HEADER + 24; + + let keycodes_size = self.max_keycodes_len() * Keycode::X11_SIZE; + + CONSTANT_SIZES + (8 * keycodes_size) + } +} + +impl Readable for GetModifierMapping { + fn read_from(buf: &mut impl Buf) -> ReadResult + where + Self: Sized, + { + const HEADER: usize = 8; + const ALIGNMENT: usize = 4; + + // FIXME: the first 4 bytes of the header should be read separately, with the + // metabyte position and sequence being given as context. That applies to + // all replies. + buf.advance(1); + + let keycodes_per_modifier = buf.get_u8(); + let sequence = buf.get_u16(); + + let total_size = ((buf.get_u32() as usize) * ALIGNMENT) - HEADER; + let buf = &mut buf.take(total_size); + + let [shift_keycodes, capslock_keycodes, ctrl_keycodes, mod1_keycodes, mod2_keycodes, mod3_keycodes, mod4_keycodes, mod5_keycodes] = + array_init(|_| { + let mut keycodes = vec![]; + + for _ in 0..keycodes_per_modifier { + match buf.get_u8() { + 0 => {}, + code => keycodes.push(Keycode(code)), + } + } + + keycodes + }); + + Ok(Self { + sequence, + + shift_keycodes, + capslock_keycodes, + ctrl_keycodes, + + mod1_keycodes, + mod2_keycodes, + mod3_keycodes, + mod4_keycodes, + mod5_keycodes, + }) + } +} diff --git a/src/x11/reply/meta.rs b/src/x11/reply/meta.rs new file mode 100644 index 00000000..debc6a08 --- /dev/null +++ b/src/x11/reply/meta.rs @@ -0,0 +1,204 @@ +// 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 [core X11 protocol] for +//! [requests that relate to an X client or the X server]. +//! +//! [Replies] are messages sent from the X server to an X client in response to +//! a [request]. +//! +//! [Replies]: Reply +//! [request]: crate::message::Request +//! [core X11 protocol]: crate::x11 +//! +//! [requests that relate to an X client or the X server]: request::meta + +extern crate self as xrb; + +use derivative::Derivative; +use xrbk::pad; +use xrbk_macro::derive_xrb; + +use crate::{message::Reply, unit::Sec, x11::request, Host, LengthString8, Toggle}; + +derive_xrb! { + /// The [reply] to a [`QueryExtension` request]. + /// + /// [reply]: Reply + /// + /// [`QueryExtension` request]: request::QueryExtension + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct QueryExtension: Reply for request::QueryExtension { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// Whether the specified extension is present. + pub present: bool, + + /// The [major opcode] of the specified extension if the extension is + /// present and it has has a [major opcode]. + /// + /// [major opcode]: crate::message::Request::MAJOR_OPCODE + pub major_opcode: Option, + /// The first [event code] defined by the specified extension if the + /// extension is present and it defines any [events]. + /// + /// [events]: crate::message::Event + /// [event code]: crate::message::Event::CODE + pub first_event_code: Option, + /// The first [error code] defined by the specified extension if the + /// extension is present and it defines any [errors]. + /// + /// [errors]: crate::message::Error + /// [event code]: crate::message::Event::CODE + pub first_error_code: Option, + } + + /// The [reply] to a [`ListExtensions` request]. + /// + /// [reply]: Reply + /// + /// [`ListExtensions` request]: request::ListExtensions + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct ListExtensions: Reply for request::ListExtensions { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `names`. + #[metabyte] + #[allow(clippy::cast_possible_truncation)] + let names_len: u8 = names => names.len() as u8, + + [_; 24], + + /// The names of all extensions supported by the X server. + #[context(names_len => usize::from(*names_len))] + pub names: Vec, + [_; names => pad(names)], + } + + /// The [reply] to a [`GetScreenSaver` request]. + /// + /// [reply]: Reply + /// + /// [`GetScreenSaver` request]: request::GetScreenSaver + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetScreenSaver: Reply for request::GetScreenSaver { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// Whether the screensaver is enabled and, if so, how long without + /// input before it is activated. + /// + /// This is [`Some`] if the screensaver is enabled, and [`None`] if it + /// is not. + /// + /// See [`SetScreenSaver::timeout`] for more information. + /// + /// [`SetScreenSaver::timeout`]: request::SetScreenSaver::timeout + pub timeout: Option>, + /// A hint for screensavers with periodic changes as to the interval + /// between those changes. + /// + /// If this is [`None`], this hints that no periodic change should be + /// made. + /// + /// See [`SetScreenSaver::interval`] for more information. + /// + /// [`SetScreenSaver::interval`]: request::SetScreenSaver::interval + pub interval: Option>, + + /// Whether it is preferred that displays that support blanking go blank + /// when the screensaver is activated. + /// + /// See [`SetScreenSaver::prefer_blanking`] for more information. + /// + /// [`SetScreenSaver::prefer_blanking`]: request::SetScreenSaver::prefer_blanking + pub prefer_blanking: Toggle, + /// Whether screensavers which generate [`Expose` events] are allowed. + /// + /// See [`SetScreenSaver::allow_expose_events`] for more information. + /// + /// [`SetScreenSaver::allow_expose_events`]: request::SetScreenSaver::allow_expose_events + /// + /// [`Expose` events]: crate::x11::event::Expose + pub allow_expose_events: Toggle, + [_; ..], + } + + /// The [reply] to a [`QueryAccessControl` request]. + /// + /// [reply]: Reply + /// + /// [`QueryAccessControl` request]: request::QueryAccessControl + #[doc(alias("ListHosts"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct QueryAccessControl: Reply for request::QueryAccessControl { + /// 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, + + /// Whether access control is [enabled]. + /// + /// [enabled]: Toggle::Enabled + #[metabyte] + pub access_control: Toggle, + + // The length of `hosts`. + #[allow(clippy::cast_possible_truncation)] + let hosts_len: u16 = hosts => hosts.len() as u16, + [_; 22], + + /// The [hosts] that are on the access control list. + /// + /// [hosts]: Host + #[context(hosts_len => usize::from(*hosts_len))] + pub hosts: Vec, + // Since `Host`s already contain padding, no extra padding needs to be + // added at the end here. + } +} diff --git a/src/x11/reply/miscellaneous.rs b/src/x11/reply/miscellaneous.rs new file mode 100644 index 00000000..4b2dcc6f --- /dev/null +++ b/src/x11/reply/miscellaneous.rs @@ -0,0 +1,220 @@ +// 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 [core X11 protocol] for +//! [requests that relate to atoms, properties, selections, and sending events]. +//! +//! [Replies] are messages sent from the X server to an X client in response to +//! a [request]. +//! +//! [Replies]: Reply +//! [request]: crate::message::Request +//! [core X11 protocol]: crate::x11 +//! +//! [requests that relate to atoms, properties, selections, and sending events]: request::miscellaneous + +extern crate self as xrb; + +use derivative::Derivative; + +use xrbk::pad; +use xrbk_macro::derive_xrb; + +use crate::{ + message::Reply, + x11::request::{self, DataFormat, DataList}, + Atom, + String8, + Window, +}; + +derive_xrb! { + /// The [reply] to a [`GetAtom` request]. + /// + /// [reply]: Reply + /// + /// [`GetAtom` request]: request::GetAtom + #[doc(alias("InternAtom", "CreateAtom"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetAtom: Reply for request::GetAtom { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The returned [atom]. + /// + /// If `no_creation` was set to `true` and an [atom] by the given `name` + /// didn't already exist, this will be [`None`]. + /// + /// [atom]: Atom + pub atom: Option, + [_; ..], + } + + /// The [reply] to a [`GetAtomName` request]. + /// + /// [reply]: crate::message + /// + /// [`GetAtomName` request]: request::GetAtomName + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetAtomName: Reply for request::GetAtomName { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `name`. + #[allow(clippy::cast_possible_truncation)] + let name_len: u16 = name => name.len() as u16, + [_; 22], + + /// The name of the [atom]. + /// + /// [atom]: Atom + #[context(name_len => usize::from(*name_len))] + pub name: String8, + [_; name => pad(name)], + } + + /// The [reply] to a [`GetProperty` request]. + /// + /// [reply]: Reply + /// + /// [`GetProperty` request]: request::GetProperty + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetProperty: Reply for request::GetProperty { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// Whether the `value` is empty ([`None`]), or made up of `i8` values, + /// `i16` values, or `i32` values. + #[metabyte] + pub format: Option, + + /// The actual type of the property. + pub r#type: Option, + /// The number of bytes remaining in the `property`'s data. + /// + /// If the specified `property` does not exist for the `target` + /// [window], this is zero. + /// + /// If the specified `property` exists but its `type` does not match the + /// specified type, this is the size of the property's data in bytes. + /// + /// If the specified `property` exists and the type is either [`Any`] or + /// matches the actual `type` of the property, this is the number of + /// bytes remaining in the `property`'s data after the end of the + /// returned `value`. + /// + /// [window]: Window + /// + /// [`Any`]: crate::Any::Any + #[doc(alias = "bytes_after")] + pub bytes_remaining: u32, + + // The length of `value`. + #[allow(clippy::cast_possible_truncation)] + let value_len: u32 = value => value.len() as u32, + [_; 12], + + /// The property's value. + /// + /// If `format` is [`None`], this will be [`DataList::I8`], but with an + /// empty list. + #[context(format, value_len => (format.unwrap_or(DataFormat::I8), *value_len))] + pub value: DataList, + } + + /// The [reply] for a [`ListProperties` request]. + /// + /// [reply]: Reply + /// + /// [`ListProperties` request]: request::ListProperties + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct ListProperties: Reply for request::ListProperties { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + // The length of `properties`. + #[allow(clippy::cast_possible_truncation)] + let properties_len: u16 = properties => properties.len() as u16, + [_; 22], + + /// The properties defined for the given [window]. + /// + /// [window]: Window + #[doc(alias = "atoms")] + #[context(properties_len => usize::from(*properties_len))] + pub properties: Vec, + } + + /// The [reply] to a [`GetSelectionOwner` request]. + /// + /// [reply]: Reply + /// + /// [`GetSelectionOwner` request]: request::GetSelectionOwner + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetSelectionOwner: Reply for request::GetSelectionOwner { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The owner of the given `selection`. + /// + /// If this is [`None`], then the selection has no owner. + pub owner: Option, + [_; ..], + } +} diff --git a/src/x11/reply/window.rs b/src/x11/reply/window.rs new file mode 100644 index 00000000..fe65f49a --- /dev/null +++ b/src/x11/reply/window.rs @@ -0,0 +1,338 @@ +// 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 [core X11 protocol] for +//! [requests that relate to windows and their management]. +//! +//! [Replies] are messages sent from the X server to an X client in response to +//! a [request]. +//! +//! [Replies]: Reply +//! [request]: crate::message::Request +//! [core X11 protocol]: crate::x11 +//! +//! [requests that relate to windows and their management]: request::window + +extern crate self as xrb; + +use derivative::Derivative; + +use xrbk_macro::{derive_xrb, Readable, Writable, X11Size}; + +use crate::{ + message::Reply, + unit::Px, + visual::{ColorId, VisualId}, + x11::request, + BitGravity, + Colormap, + DeviceEventMask, + EventMask, + MaintainContents, + Rectangle, + Window, + WindowClass, + WindowGravity, +}; + +/// The state of the [window] regarding how it is mapped. +/// +/// [window]: Window +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum MapState { + /// The [window] is not mapped. + /// + /// [window]: Window + Unmapped, + + /// The [window] is mapped but one of its ancestors is unmapped. + /// + /// [window]: Window + Unviewable, + + /// The [window] is mapped and all of its ancestors are mapped. + /// + /// [window]: Window + Viewable, +} + +derive_xrb! { + /// The [reply] to a [`GetWindowAttributes` request]. + /// + /// [reply]: Reply + /// + /// [`GetWindowAttributes` request]: request::GetWindowAttributes + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetWindowAttributes: Reply for request::GetWindowAttributes { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The conditions under which the X server should maintain the obscured + /// [regions] of the [window]. + /// + /// See [`Attributes::maintain_contents`] for more information. + /// + /// [regions]: crate::Region + /// [window]: Window + /// + /// [`Attributes::maintain_contents`]: crate::set::Attributes::maintain_contents + #[doc(alias = "backing_store")] + #[metabyte] + pub maintain_contents: MaintainContents, + + /// The visual used by the [window]. + /// + /// See [`VisualType`] for more information. + /// + /// [window]: Window + /// + /// [`VisualType`]: crate::visual::VisualType + pub visual: VisualId, + /// The [window]'s [class]. + /// + /// [window]: Window + /// [class]: WindowClass + pub class: WindowClass, + + /// Defines the [region] of the [window] which is retained when the + /// [window] is resized. + /// + /// See [`Attributes::bit_gravity`] for more information. + /// + /// [region]: crate::Region + /// [window]: Window + /// + /// [`Attributes::bit_gravity`]: crate::set::Attributes::bit_gravity + pub bit_gravity: BitGravity, + /// Defines how the [window] is repositioned if its parent is resized. + /// + /// See [`Attributes::window_gravity`] for more information. + /// + /// [window]: Window + /// + /// [`Attributes::window_gravity`]: crate::set::Attributes::window_gravity + #[doc(alias = "win_gravity")] + pub window_graivty: WindowGravity, + + /// Defines which bit planes of the [window] hold dynamic data which is + /// maintained for `maintain_contents` and `maintain_windows_under`. + /// + /// See [`Attributes::maintained_planes`] for more information. + /// + /// [window]: Window + /// + /// [`Attributes::maintained_planes`]: crate::set::Attributes::maintained_planes + #[doc(alias = "backing_planes")] + pub maintained_planes: u32, + /// Defines the [color] used for bit planes which are not preserved for + /// `maintain_contents` and `maintain_windows_under` (see + /// `maintained_planes`). + /// + /// See [`Attributes::maintenance_fallback_color`] for more information. + /// + /// [color]: ColorId + /// + /// [`Attributes::maintenance_fallback_color`]: crate::set::Attributes::maintenance_fallback_color + #[doc(alias = "backing_pixel")] + pub maintenance_fallback_color: ColorId, + /// Whether the X server should maintain the contents of + /// [windows][window] under this [window]. + /// + /// See [`Attributes::maintain_windows_under`] for more information. + /// + /// [window]: Window + /// + /// [`Attributes::maintain_windows_under`]: crate::set::Attributes::maintain_windows_under + #[doc(alias = "save_under")] + pub maintain_windows_under: bool, + + /// Whether the [window]'s `colormap` is an installed [colormap] for + /// the [screen]. + /// + /// [window]: Window + /// [colormap]: Colormap + /// [screen]: crate::visual::Screen + pub map_installed: bool, + /// The [window]'s [map state]. + /// + /// See [`MapState`] for more information. + /// + /// [window]: Window + /// [map state]: MapState + pub map_state: MapState, + + /// Whether [`MapWindow`] and [`ConfigureWindow`] requests on the + /// [window] override a [`SUBSTRUCTURE_REDIRECT`] selection on its + /// parent. + /// + /// This is typically used to inform a window manager not to tamper with + /// the [window]. + /// + /// See [`Attributes::override_redirect`] for more information. + /// + /// [window]: Window + /// + /// [`MapWindow`]: request::MapWindow + /// [`ConfigureWindow`]: request::ConfigureWindow + /// + /// [`SUBSTRUCTURE_REDIRECT`]: EventMask::SUBSTRUCTURE_REDIRECT + /// + /// [`Attributes::override_redirect`]: crate::set::Attributes::override_redirect + pub override_redirect: bool, + + /// The [colormap] which best reflects the true colors of the [window]. + /// + /// See [`Attributes::colormap`] for more information. + /// + /// [window]: Window + /// [colormap]: Colormap + /// + /// [`Attributes::colormap`]: crate::set::Attributes::colormap + pub colormap: Option, + + /// All of the [events] selected by all clients on the [window]. + /// + /// This is the bitwise OR of every client's [`event_mask`] on the + /// [window]. + /// + /// [window]: Window + /// [events]: crate::message::Event + /// + /// [`event_mask`]: crate::set::Attributes::event_mask + pub all_event_masks: EventMask, + /// The [events] selected by you on the [window]. + /// + /// This is your [`event_mask`] on the [window]. + /// + /// [window]: Window + /// [events]: crate::message::Event + /// + /// [`event_mask`]: crate::set::Attributes::event_mask + pub your_event_mask: EventMask, + /// Defines the [events][event] which should not be propagated to + /// ancestors of the [window] if no client has selected the [event] on + /// the [window]. + /// + /// See [`Attributes::do_not_propagate_mask`] for more information. + /// + /// [event]: crate::message::Event + /// [window]: Window + /// + /// [`Attributes::do_not_propagate_mask`]: crate::set::Attributes::do_not_propagate_mask + pub do_not_propagate_mask: DeviceEventMask, + [_; ..], + } + + /// The [reply] to a [`GetGeometry` request]. + /// + /// [reply]: Reply + /// + /// [`GetGeometry` request]: request::GetGeometry + #[doc(alias("GetX", "GetY", "GetWidth", "GetHeight", "GetBorderWidth"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct GetGeometry: Reply for request::GetGeometry { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The number of bits per pixel for the [drawable]. + /// + /// [drawable]: crate::Drawable + #[metabyte] + pub depth: u8, + + /// The [drawable]'s root [window]. + /// + /// [window]: Window + /// [drawable]: crate::Drawable + pub root: Window, + + /// The [drawable]'s geometry. + /// + /// For a [pixmap], the `x` and `y` coordinates will always be zero. + /// + /// For a [window], the coordinates are relative to the top-left corner + /// of the [window]'s parent. + /// + /// [window]: Window + /// [pixmap]: crate::Pixmap + /// [drawable]: crate::Drawable + #[doc(alias("x", "y", "width", "height"))] + pub geometry: Rectangle, + /// The width of the [drawable]'s border. + /// + /// For a [pixmap], this will always be zero. + /// + /// [pixmap]: crate::Pixmap + /// [drawable]: crate::Drawable + pub border_width: Px, + [_; ..], + } + + /// The [reply] to a [`QueryWindowTree` request]. + /// + /// [reply]: Reply + /// + /// [`QueryWindowTree` request]: request::QueryWindowTree + #[doc(alias("QueryTree", "GetTree", "GetWindowTree"))] + #[doc(alias("QueryParent", "QueryChildren", "QueryRoot"))] + #[doc(alias("GetParent", "GetChildren", "GetRoot"))] + #[derive(Derivative, Debug, X11Size, Readable, Writable)] + #[derivative(Hash, PartialEq, Eq)] + pub struct QueryWindowTree: Reply for request::QueryWindowTree { + /// 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 + #[sequence] + #[derivative(Hash = "ignore", PartialEq = "ignore")] + pub sequence: u16, + + /// The `target` [window]'s root [window]. + /// + /// [window]: Window + pub root: Window, + /// The `target` [window]'s parent. + /// + /// [window]: Window + pub parent: Option, + + // The length of `children`. + #[allow(clippy::cast_possible_truncation)] + let children_len: u16 = children => children.len() as u16, + [_; 14], + + /// The `target` [window]'s children. + /// + /// [window]: Window + #[context(children_len => usize::from(*children_len))] + pub children: Vec, + } +} diff --git a/src/x11/request.rs b/src/x11/request.rs new file mode 100644 index 00000000..eb42991a --- /dev/null +++ b/src/x11/request.rs @@ -0,0 +1,29 @@ +// 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 [core X11 protocol]. +//! +//! [Requests] are messages sent from an X client to the X server. +//! +//! [Requests]: crate::message::Request +//! [core X11 protocol]: super + +// TODO: should these modules be private and re-exported, or public? +// or public and also re-exported? + +pub use color::*; +pub use font::*; +pub use graphics::*; +pub use input::*; +pub use meta::*; +pub use miscellaneous::*; +pub use window::*; + +pub mod color; +pub mod font; +pub mod graphics; +pub mod input; +pub mod meta; +pub mod miscellaneous; +pub mod window; diff --git a/src/x11/request/color.rs b/src/x11/request/color.rs new file mode 100644 index 00000000..a3bdf685 --- /dev/null +++ b/src/x11/request/color.rs @@ -0,0 +1,1135 @@ +// 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 [core X11 protocol] that relate to colors. +//! +//! [Requests] are messages sent from an X client to the X server. +//! +//! [Requests]: Request +//! [core X11 protocol]: crate::x11 + +extern crate self as xrb; + +use xrbk::{pad, ConstantX11Size}; +use xrbk_macro::{derive_xrb, Readable, Writable, X11Size}; + +use crate::{ + message::Request, + visual::{ColorId, RgbColor, VisualId}, + x11::{error, reply}, + ColorChannelMask, + Colormap, + String8, + Window, +}; + +macro_rules! request_error { + ( + $(#[$meta:meta])* + $vis:vis enum $Name:ident for $Request:ty { + $($($Error:ident),+$(,)?)? + } + ) => { + #[doc = concat!( + "An [error](crate::message::Error) generated because of a failed [`", + stringify!($Request), + "` request](", + stringify!($Request), + ")." + )] + #[doc = ""] + $(#[$meta])* + $vis enum $Name { + $($( + #[doc = concat!( + "A [`", + stringify!($Error), + "` error](error::", + stringify!($Error), + ")." + )] + $Error(error::$Error) + ),+)? + } + }; +} + +request_error! { + pub enum CreateColormapError for CreateColormap { + ResourceIdChoice, + Match, + Value, + Window, + } +} + +// FIXME: docs for `InitialColormapAllocation` and `CreateColormap` need to be +// rewritten when it comes to the visual classes and what they do. + +/// Whether a [colormap] initially has [no entries allocated] or +/// [all entries allocated]. +/// +/// [no entries allocated]: InitialColormapAllocation::None +/// [all entries allocated]: InitialColormapAllocation::All +/// +/// [colormap]: Colormap +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum InitialColormapAllocation { + /// The [colormap] initially has no entries, or those initial entries are + /// defined elsewhere. + /// + /// For [`VisualClass::StaticGray`], [`VisualClass::StaticColor`], and + /// [`VisualClass::TrueColor`], the initial entries are defined, but by the + /// specific visual, not by the [core X11 protocol]. + /// + /// For [`VisualClass::GrayScale`], [`VisualClass::PseudoColor`], and + /// [`VisualClass::DirectColor`], the initial entries are undefined. + /// + /// Clients can allocate entries after the [colormap] is created. + /// + /// [colormap]: Colormap + /// [core X11 protocol]: crate::x11 + /// + /// [`VisualClass::StaticGray`]: crate::visual::VisualClass::StaticGray + /// [`VisualClass::StaticColor`]: crate::visual::VisualClass::StaticColor + /// [`VisualClass::TrueColor`]: crate::visual::VisualClass::TrueColor + /// + /// [`VisualClass::GrayScale`]: crate::visual::VisualClass::GrayScale + /// [`VisualClass::PseudoColor`]: crate::visual::VisualClass::PseudoColor + /// [`VisualClass::DirectColor`]: crate::visual::VisualClass::DirectColor + None, + + /// The entire [colormap] is allocated as writable. + /// + /// None of these entries can be removed with [`DeleteColormapEntry`]. + /// + /// [colormap]: Colormap + All, +} + +derive_xrb! { + /// A [request] that creates a [colormap] for the given [window]'s [screen]. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if the given `colormap_id` is + /// already in use or if it is not allocated to your client. + /// + /// A [`Window` error] is generated if `window` does not refer to a defined + /// [window]. + /// + /// A [`Match` error] is generated if the given `visual` does not match the + /// [visual type] of the `target` [window]'s [screen]. + /// + /// A [`Match` error] is generated the `visual` is + /// [`VisualClass::StaticGray`], [`VisualClass::StaticColor`], or + /// [`VisualClass::TrueColor`] and `alloc` is not + /// [`InitialColormapAllocation::None`]. + /// + /// [colormap]: Colormap + /// [window]: Window + /// [screen]: crate::visual::Screen + /// [visual type]: crate::visual::VisualType + /// [request]: Request + /// + /// [`VisualClass::StaticGray`]: crate::visual::VisualClass::StaticGray + /// [`VisualClass::StaticColor`]: crate::visual::VisualClass::StaticColor + /// [`VisualClass::TrueColor`]: crate::visual::VisualClass::TrueColor + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + /// [`Window` error]: error::Window + /// [`Match` error]: error::Match + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct CreateColormap: Request(78, CreateColormapError) { + /// Whether this [colormap] begins with [no entries allocated] or + /// [all entries allocated]. + /// + /// See [`InitialColormapAllocation`] for more information. + /// + /// [no entries allocated]: InitialColormapAllocation::None + /// [all entries allocated]: InitialColormapAllocation::All + /// + /// [colormap]: Colormap + #[metabyte] + pub initial_allocation: InitialColormapAllocation, + + /// The [`Colormap` ID] that will be associated with the [colormap]. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if this [`Colormap` ID] is + /// already in use or if it is not allocated to your client. + /// + /// [colormap]: Colormap + /// [`Colormap` ID]: Colormap + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + pub colormap_id: Colormap, + + /// The [window] for which this [colormap] is created. + /// + /// # Errors + /// A [`Window` error] is generated if this does not refer to a defined + /// [window]. + /// + /// [window]: Window + /// [colormap]: Colormap + /// + /// [`Window` error]: error::Window + pub window: Window, + + /// The [colormap]'s [visual type]. + /// + /// # Errors + /// A [`Match` error] is generated if the [visual type] is not one + /// supported by [window]'s [screen]. + /// + /// A [`Match` error] is generated if [visual class] is [`StaticGray`], + /// [`StaticColor`], or [`TrueColor`] but `alloc` is not + /// [`InitialColormapAllocation::None`]. + /// + /// [colormap]: Colormap + /// [window]: Window + /// [screen]: crate::visual::Screen + /// [visual type]: crate::visual::VisualType + /// [visual class]: crate::visual::VisualClass + /// + /// [`StaticGray`]: crate::visual::VisualClass::StaticGray + /// [`StaticColor`]: crate::visual::VisualClass::StaticColor + /// [`TrueColor`]: crate::visual::VisualClass::TrueColor + /// + /// [`Match` error]: error::Match + pub visual: VisualId, + } + + /// A [request] that deletes the given [colormap]. + /// + /// The association between the [`Colormap` ID] and the [colormap] itself is + /// removed in the process. + /// + /// If the [colormap] is installed on a [screen], it is [uninstalled]. If + /// the [colormap] is a [window]'s [`colormap` attribute], the + /// [`colormap` attribute] is set to [`None`] and a [`Colormap` event] is + /// generated. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// [window]: Window + /// [screen]: crate::visual::Screen + /// [request]: Request + /// + /// [uninstalled]: UninstallColormap + /// + /// [`Colormap` ID]: Colormap + /// [`colormap` attribute]: crate::set::Attributes::colormap + /// + /// [`Colormap` event]: crate::x11::event::Colormap + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("FreeColormap"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct DestroyColormap: Request(79, error::Colormap) { + /// The [colormap] which is to be deleted. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("colormap", "cmap", "map"))] + pub target: Colormap, + } +} + +request_error! { + #[doc(alias("CopyColormapAndFreeError"))] + pub enum MoveColormapError for MoveColormap { + Colormap, + ResourceIdChoice, + } +} + +derive_xrb! { + /// A [request] that moves all of the values of a `source` [colormap] into a + /// new [colormap], then destroys the `source` [colormap]. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if `colormap_id` is already in + /// use or if it is not allocated to your client. + /// + /// A [`Colormap` error] is generated if `source` does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// [request]: Request + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + /// [`Colormap` error]: error::Colormap + #[doc(alias("CopyColormapAndFree"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct MoveColormap: Request(80, MoveColormapError) { + /// The [`Colormap` ID] that will be associated with the new [colormap]. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if this [`Colormap` ID] is + /// already in use or if it is not allocated to your client. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` ID]: Colormap + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + pub colormap_id: Colormap, + + /// The [colormap] which is copied to create the new [colormap], then + /// destroyed. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + pub source: Colormap, + } + + /// A [request] that installs the given [colormap] on its [screen]. + /// + /// All [windows][window] associated with the given [colormap] will + /// immediately switch to their true colors. + /// + /// If the given `target` [colormap] was not already installed, + /// a [`Colormap` event] is generated for every [window] which specifies the + /// [colormap] in its [`colormap` attribute]. + /// + /// As a side effect of this [request], additional [colormaps][colormap] may + /// be implicitly installed or uninstalled by the X server. For each + /// [colormap] that is implicitly installed or uninstalled as a result of + /// this [request], a [`Colormap` event] is generated for every [window] + /// which specifies that [colormap] in its [`colormap` attribute]. + /// + /// ## Required colormaps list + /// When a [colormap] is explicitly installed (that is, it is installed with + /// this [request]), it is added as the head of the [screen]'s required + /// [colormaps][colormap] list. + /// + /// The length of the required [colormaps][colormap] list is no more than + /// the [screen]'s [`min_installed_colormaps`]: if installing this + /// [colormap] causes the list to exceed that limit, the list is truncated + /// at the tail to make room. + /// + /// [Explicitly uninstalling](UninstallColormap) a [colormap] means to + /// remove it from the list of required [colormaps][colormap]. It may or may + /// not actually be uninstalled as a result. + /// + /// The X server may implicitly uninstall any [colormap] _not_ in the list + /// of required [colormaps][colormap] at any time. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// [window]: Window + /// [screen]: crate::visual::Screen + /// [request]: Request + /// + /// [`colormap` attribute]: crate::set::Attributes::colormap + /// [`min_installed_colormaps`]: crate::visual::Screen::min_installed_colormaps + /// + /// [`Colormap` event]: crate::x11::event::Colormap + /// + /// [`Colormap` error]: error::Colormap + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct InstallColormap: Request(81, error::Colormap) { + /// The [colormap] that is to be installed. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + pub target: Colormap, + } + + /// A [request] that removes the given [colormap] from the + /// list of required colormaps for its [screen]. + /// + /// If the given [colormap] is uninstalled as a result of this [request], a + /// [`Colormap` event] is generated for every [window] which specifies the + /// [colormap] in its [`colormap` attribute]. + /// + /// As a side effect of this [request], additional [colormaps][colormap] may + /// be implicitly installed or uninstalled by the X server. For each + /// [colormap] that is implicitly installed or uninstalled as a result of + /// this [request], a [`Colormap` event] is generated for every [window] + /// which specifies that [colormap] in its [`colormap` attribute]. + /// + /// ## Required colormaps list + /// When a [colormap] is [explicitly installed](InstallColormap), it is + /// added as the head of the [screen]'s required [colormaps][colormap] list. + /// + /// The length of the required [colormaps][colormap] list is no more than + /// the [screen]'s [`min_installed_colormaps`]. + /// + /// Explicitly uninstalling a [colormap] (that is, it is uninstalled with + /// this [request]) actually removes that [colormap] from the [screen]'s + /// list of required [colormaps][colormap]. As a result, it may or may not + /// be uninstalled by the X server. + /// + /// The X server may implicitly uninstall any [colormap] _not_ in the list + /// of required [colormaps][colormap] at any time. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// [window]: Window + /// [screen]: crate::visual::Screen + /// [request]: Request + /// + /// [`colormap` attribute]: crate::set::Attributes::colormap + /// [`min_installed_colormaps`]: crate::visual::Screen::min_installed_colormaps + /// + /// [`Colormap` event]: crate::x11::event::Colormap + /// + /// [`Colormap` error]: error::Colormap + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct UninstallColormap: Request(82, error::Colormap) { + /// The [colormap] that is to be uninstalled. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + pub target: Colormap, + } + + /// A [request] that returns a list of the given [window]'s [screen]'s + /// currently installed [colormaps]. + /// + /// # Replies + /// This [request] generates a [`ListInstalledColormaps` reply]. + /// + /// # Errors + /// A [`Window` error] is generated if `target` does not refer to a defined + /// [window]. + /// + /// [colormaps]: Colormap + /// [window]: Window + /// [screen]: crate::visual::Screen + /// [request]: Request + /// + /// [`ListInstalledColormaps` reply]: reply::ListInstalledColormaps + /// + /// [`Window` error]: error::Window + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct ListInstalledColormaps: Request(83, error::Window) -> reply::ListInstalledColormaps { + /// The [window] for which this [request] returns its installed + /// [colormaps]. + /// + /// # Errors + /// A [`Window` error] is generated if this does not refer to a defined + /// [window]. + /// + /// [colormaps]: Colormap + /// [window]: Window + /// [request]: Request + /// + /// [`Window` error]: error::Window + pub target: Window, + } + + /// A [request] that allocates a read-only [colormap] entry for the given + /// color on the given [colormap]. + /// + /// The closest [RGB values] provided by the display are chosen. + /// + /// Multiple clients may be assigned the same read-only [colormap] entry if + /// they request the same closest [RGB values]. + /// + /// The [`ColorId`] and [RGB values] that were actually used are returned. + /// + /// # Replies + /// This [request] generates an [`AllocateColor` reply]. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// [RGB values]: RgbColor + /// [colormap]: Colormap + /// [request]: Request + /// + /// [`ColorId`]: ColorId + /// + /// [`AllocateColor` reply]: reply::AllocateColor + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("AllocColor"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct AllocateColor: Request(84, error::Colormap) -> reply::AllocateColor { + /// The [colormap] for which the [colormap] entry is allocated. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + pub target: Colormap, + + /// The color which is to be allocated. + pub color: RgbColor, + [_; 2], + } +} + +request_error! { + #[doc(alias("AllocNamedColor"))] + pub enum AllocateNamedColorError for AllocateNamedColor { + Colormap, + Name, + } +} + +derive_xrb! { + /// A [request] that allocates a read-only [colormap] entry on the given + /// [colormap] for the color by the given `name`. + /// + /// The `name` is looked up on the [screen] associated with the [colormap]. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// A [`Name` error] is generated if no color by the given `name` exists for + /// the [screen] associated with the `target` [colormap]. + /// + /// [colormap]: Colormap + /// [screen]: crate::visual::Screen + /// [request]: Request + /// + /// [`Colormap` error]: error::Colormap + /// [`Name` error]: error::Name + #[doc(alias("AllocNamedColor"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct AllocateNamedColor: Request( + 85, + AllocateNamedColorError, + ) -> reply::AllocateNamedColor { + /// The [colormap] for which a [colormap] entry for the color by the + /// given `name` is allocated. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + pub target: Colormap, + + // The length of `name`. + #[allow(clippy::cast_possible_truncation)] + let name_len: u16 = name => name.len() as u16, + [_; 2], + + /// The name of the color that is looked up on the [screen] associated + /// with the [colormap]. + /// + /// The name should be specified in ISO Latin-1 encoding. Which case (e.g. + /// uppercase, lowercase) the name is specified in does not matter. + /// + /// [colormap]: Colormap + /// [screen]: crate::visual::Screen + #[context(name_len => usize::from(*name_len))] + pub name: String8, + [_; name => pad(name)], + } +} + +request_error! { + #[doc(alias("AllocColorCellsError"))] + pub enum AllocateColorCellsError for AllocateColorCells { + Colormap, + Value, + } +} + +derive_xrb! { + // TODO: improve docs + /// A [request] that allocates a [colormap] entry for every color and plane + /// mask combination up to the given `color_count` and `plane_mask_count`. + /// + /// By applying each plane mask to each color, + /// color_count * 2plane_count distinct [colormap] + /// entries are allocated. + /// + /// Each [colormap] entry is allocated as writable. + /// + /// The initial RGB values of the allocated [colormap] entries are + /// undefined. + /// + /// # Replies + /// This [request] generates an [`AllocateColorCells` reply]. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// A [`Value` error] is generated if `color_count` is not greater than + /// zero. + /// + /// [colormap]: Colormap + /// [request]: Request + /// + /// [`AllocateColorCells` reply]: reply::AllocateColorCells + /// + /// [`Colormap` error]: error::Colormap + /// [`Value` error]: error::Value + #[doc(alias("AllocColorCells"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct AllocateColorCells: Request( + 86, + AllocateColorCellsError, + ) -> reply::AllocateColorCells { + /// Whether the plane masks should be combined (i.e. bitwise OR) to form + /// a contiguous set of bits for each color channel. + /// + /// For [`VisualClass::GrayScale`] or [`VisualClass::PseudoColor`], one + /// contiguous set of bits are formed by bitwise OR-ing all of the plane + /// masks. + /// + /// For [`VisualClass::DirectColor`], three (one for each color channel: + /// red, green, and blue) contiguous sets of bits are formed by bitwise + /// OR-ing all of the plane masks. + /// + /// [`VisualClass::GrayScale`]: crate::visual::VisualClass::GrayScale + /// [`VisualClass::PseudoColor`]: crate::visual::VisualClass::PseudoColor + /// [`VisualClass::DirectColor`]: crate::visual::VisualClass::DirectColor + #[metabyte] + pub contiguous: bool, + + /// The [colormap] for which the [colormap] entries are allocated. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("colormap"))] + pub target: Colormap, + + /// The number of colors that are to be allocated. + /// + /// # Errors + /// A [`Value` error] is generated if this is not greater than zero. + /// + /// [`Value` error]: error::Value + pub color_count: u16, + /// The number of bit plane masks that are to be allocated. + pub plane_count: u16, + } +} + +request_error! { + #[doc(alias("AllocColorPlanesError"))] + pub enum AllocateColorPlanesError for AllocateColorPlanes { + Colormap, + Value, + } +} + +derive_xrb! { + // TODO: improve docs + /// A [request] that allocates a [colormap] entry for every color and bit + /// plane combination up to the given `color_count` and plane counts. + /// + /// By combining subsets of plane masks with colors, a total of + /// color_count * 2plane_count (where `plane_count` + /// represents `red_plane_count + green_plane_count + blue_plane_count`) + /// [colormap] entries are allocated: + /// color_count * 2red_plane_count independent red + /// entries, color_count * 2green_plane_count + /// independent green entries, and + /// color_count * 2blue_plane_count independent blue + /// entries. + /// + /// Each [colormap] entry is allocated as writable. + /// + /// The initial RGB values of the allocated [colormap] entries are + /// undefined. + /// + /// # Replies + /// This [request] generates an [`AllocateColorPlanes` reply]. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// A [`Value` error] is generated if `color_count` is not greater than + /// zero. + /// + /// An [`Alloc` error] is generated if the X server failed to allocate the + /// requested [colormap] entries; see [`RequestError::Alloc`]. + /// + /// [colormap]: Colormap + /// [request]: Request + /// + /// [`AllocateColorPlanes` reply]: reply::AllocateColorPlanes + /// + /// [`Colormap` error]: error::Colormap + /// [`Value` error]: error::Value + /// [`Alloc` error]: error::Alloc + /// + /// [`RequestError::Alloc`]: crate::message::RequestError::Alloc + #[doc(alias("AllocColorPlanes"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct AllocateColorPlanes: Request( + 87, + AllocateColorPlanesError, + ) -> reply::AllocateColorPlanes { + /// Whether the returned plane masks will have a contiguous set of bits. + #[metabyte] + pub contiguous: bool, + + /// The [colormap] for which the [colormap] entries are allocated. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("colormap"))] + pub target: Colormap, + + /// The number of colors that will be returned. + /// + /// # Errors + /// A [`Value` error] is generated if this is not greater than zero. + /// + /// [`Value` error]: error::Value + pub color_count: u16, + + /// The number of bits set in the returned `red_plane_mask`. + pub red_plane_count: u16, + /// The number of bits set in the returned `green_plane_mask`. + pub green_plane_count: u16, + /// The number of bits set in the returned `blue_plane_mask`. + pub blue_plane_count: u16, + } +} + +request_error! { + #[doc(alias("FreeColorsError"))] + pub enum DestroyColormapEntriesError for DestroyColormapEntries { + Access, + Colormap, + Value, + } +} + +derive_xrb! { + /// A [request] that deletes every requested [colormap] entry that was + /// allocated by your client in the given [colormap]. + /// + /// The [colormap] entries to be deleted are found by applying every subset + /// of the given `plane_mask` with each of the given `colors` - the entries + /// which were allocated by your client are deleted. + /// + /// The requested [colormap] entries are only actually deleted under the + /// following conditions: + /// - If you have allocated a particular [colormap] entry multiple times, + /// you must send a `DestroyColormapEntries` [request] for each time you + /// allocated it before it is actually deleted. + /// - A read-only [colormap] entry is not actually deleted until all clients + /// have deleted it. + /// + /// A particular [`ColorId`] allocated by an [`AllocateColorPlanes` request] + /// may not be reused until all of its related [`ColorId`s] have been + /// deleted too. + /// + /// Even if an [error] is generated because of one of the requested + /// [colormap] entries, all other [colormap] entries which do not generate + /// [errors][error] will still be deleted. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// An [`Access` error] is generated if a requested [colormap] entry is not + /// actually allocated, or it is not allocated by your client. + /// + /// A [`Value` error] is generated if a requested [`ColorId`] is not a valid + /// index into the `target` [colormap]. + /// + /// [colormap]: Colormap + /// [colors]: ColorId + /// [request]: Request + /// [error]: crate::message::Error + /// + /// [`ColorId`s]: ColorId + /// + /// [`AllocateColorPlanes` request]: AllocateColorPlanes + /// + /// [`Access` error]: error::Access + /// [`Colormap` error]: error::Colormap + /// [`Value` error]: error::Value + // TODO: rename all Destroy* requests to Delete* + #[doc(alias("FreeColors"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct DestroyColormapEntries: Request(88, DestroyColormapEntriesError) { + /// The [colormap] for which the [colormap] entries are deleted. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("colormap"))] + pub target: Colormap, + + /// The bit plane mask every subset of which is combined with each + /// [`ColorId`] in `colors` to produce the requested [colormap] entries. + /// + /// [colormap]: Colormap + pub plane_mask: u32, + /// The [`ColorId`s] which are combined with every subset of the + /// `plane_mask` to produce the requested [colormap] entries. + /// + /// [colormap]: Colormap + /// + /// [`ColorId`s]: ColorId + #[context(self::remaining => remaining / ColorId::X11_SIZE)] + pub colors: Vec, + } +} + +request_error! { + pub enum StoreColorsError for StoreColors { + Access, + Colormap, + Value, + } +} + +derive_xrb! { + /// A change to a [colormap] entry made in a [`StoreColors` request]. + /// + /// [colormap]: Colormap + /// + /// [`StoreColors` request]: StoreColors + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct ColormapEntryChange { + /// The [`ColorId`] of the changed [colormap] entry. + /// + /// [colormap]: Colormap + pub id: ColorId, + + /// The new color. + pub color: RgbColor, + /// The mask for which of the [colormap] entry's color channels are + /// changed. + /// + /// [colormap]: Colormap + pub mask: ColorChannelMask, + _, + } + + impl ConstantX11Size for ColormapEntryChange { + const X11_SIZE: usize = { + ColorId::X11_SIZE + + RgbColor::X11_SIZE + + ColorChannelMask::X11_SIZE + + 1 + }; + } + + /// A [request] that changes the [RGB values] of the given [colormap] + /// entries. + /// + /// See also: [`StoreNamedColor`]. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// An [`Access` error] is generated if a requested [colormap] entry is + /// read-only or it is not allocated. + /// + /// A [`Value` error] is generated if a requested [`ColorId`] is not a valid + /// index into the `target` [colormap]. + /// + /// [RGB values]: RgbColor + /// [colormap]: Colormap + /// [request]: Request + /// + /// [`Access` error]: error::Access + /// [`Colormap` error]: error::Colormap + /// [`Value` error]: error::Value + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct StoreColors: Request(89, StoreColorsError) { + /// The [colormap] for which the [colormap] entries are changed. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("colormap"))] + pub target: Colormap, + + /// The requested [colormap] entry changes. + /// + /// # Errors + /// An [`Access` error] is generated if a requested [colormap] entry is + /// read-only or it is not allocated. + /// + /// A [`Value` error] is generated if a requested [`ColorId`] is not a + /// valid index into the `target` [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Access` error]: error::Access + /// [`Value` error]: error::Value + #[doc(alias("items"))] + #[context(self::remaining => remaining / ColormapEntryChange::X11_SIZE)] + pub changes: Vec, + } +} + +request_error! { + pub enum StoreNamedColorError for StoreNamedColor { + Access, + Colormap, + Name, + Value, + } +} + +derive_xrb! { + /// A [request] that changes the [RGB values] of the given [colormap] entry + /// to the color of the given `name` in the given [colormap]'s [screen]. + /// + /// See also: [`StoreColors`], [`ColormapEntryChange`]. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// A [`Name` error] is generated if `name` does not refer to a defined + /// color in the `target` [colormap]'s [screen]. + /// + /// An [`Access` error] is generated if the requested [colormap] entry is + /// read-only or it is not allocated. + /// + /// A [`Value` error] is generated if the requested [`ColorId`] is not a + /// valid into into the `target` [colormap]. + /// + /// [RGB values]: RgbColor + /// [colormap]: Colormap + /// [screen]: crate::visual::Screen + /// [request]: Request + /// + /// [`Access` error]: error::Access + /// [`Colormap` error]: error::Colormap + /// [`Value` error]: error::Value + /// [`Name` error]: error::Name + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct StoreNamedColor: Request(90, StoreNamedColorError) { + /// The mask for which of the [colormap] entry's color channels are + /// changed. + /// + /// [colormap]: Colormap + pub mask: ColorChannelMask, + + /// The [colormap] for which the [colormap] entry is changed. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("colormap"))] + pub target: Colormap, + /// The [`ColorId`] of the [colormap] entry which is to be changed. + /// + /// [colormap]: Colormap + pub id: ColorId, + + // The length of `name`. + #[allow(clippy::cast_possible_truncation)] + let name_len: u16 = name => name.len() as u16, + [_; 2], + + /// The name of the color in the `target`'s [screen] which the requested + /// [colormap] entry is changed to. + /// + /// # Errors + /// A [`Name` error] is generated if this does not refer to a defined + /// color in the `target` [colormap]'s [screen]. + /// + /// [colormap]: Colormap + /// [screen]: crate::visual::Screen + /// + /// [`Name` error]: error::Name + #[context(name_len => usize::from(*name_len))] + pub name: String8, + [_; name => pad(name)], + } +} + +request_error! { + pub enum QueryColorsError for QueryColors { + Colormap, + Value, + } +} + +derive_xrb! { + /// A [request] that returns the [RGB values] stored for the given + /// [colormap] entries in the given [colormap]. + /// + /// # Replies + /// This [request] generates a [`QueryColors` reply]. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// A [`Value` error] is generated if a given [`ColorId`] is not a valid + /// index into the `target` [colormap]. + /// + /// [RGB values]: RgbColor + /// [colormap]: Colormap + /// [request]: Request + /// + /// [`QueryColors` reply]: reply::QueryColors + /// + /// [`Colormap` error]: error::Colormap + /// [`Value` error]: error::Value + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct QueryColors: Request(91, QueryColorsError) -> reply::QueryColors { + /// The [colormap] on which the [RGB values] of the given [colormap] + /// entries are queried. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [RGB values]: RgbColor + /// [colormap]: Colormap + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("colormap"))] + pub target: Colormap, + + /// The [`ColorId`s] of the requested [colormap] entries. + /// + /// # Errors + /// A [`Value` error] is generated if a given [`ColorId`] is not a valid + /// index into the given `target` [colormap]. + /// + /// [colormap]: Colormap + /// + /// [`ColorId`s]: ColorId + /// + /// [`Value` error]: error::Value + #[context(self::remaining => remaining / ColorId::X11_SIZE)] + pub colors: Vec, + } +} + +request_error! { + #[doc(alias("LookupColorError"))] + pub enum GetNamedColorError for GetNamedColor { + Colormap, + Name, + } +} + +derive_xrb! { + /// A [request] that returns the [RGB values] for the color by the given + /// `name` in the given [colormap]'s [screen]. + /// + /// # Replies + /// This [request] generates a [`GetNamedColor` reply]. + /// + /// # Errors + /// A [`Colormap` error] is generated if `target` does not refer to a + /// defined [colormap]. + /// + /// A [`Name` error] is generated if `name` does not refer to a defined + /// color for the given `target` [colormap]'s [screen]. + /// + /// [RGB values]: RgbColor + /// [colormap]: Colormap + /// [screen]: crate::visual::Screen + /// [request]: Request + /// + /// [`GetNamedColor` reply]: reply::GetNamedColor + /// + /// [`Colormap` error]: error::Colormap + /// [`Name` error]: error::Name + #[doc(alias("LookupColor"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct GetNamedColor: Request(92, GetNamedColorError) -> reply::GetNamedColor { + /// The [colormap] whose [screen] defines the requested color. + /// + /// # Errors + /// A [`Colormap` error] is generated if this does not refer to a + /// defined [colormap]. + /// + /// [colormap]: Colormap + /// [screen]: crate::visual::Screen + /// + /// [`Colormap` error]: error::Colormap + #[doc(alias("colormap"))] + pub target: Colormap, + + // The length of `name`. + #[allow(clippy::cast_possible_truncation)] + let name_len: u16 = name => name.len() as u16, + [_; 22], + + /// The name of the color which this [request] gets the [RGB values] of. + /// + /// This name should use ISO Latin-1 encoding. The case of the name is + /// ignored: whether it is uppercase or lowercase does not matter. + /// + /// # Errors + /// A [`Name` error] is generated if this does not refer to a color + /// defined for the `target` [colormap]'s [screen]. + /// + /// [RGB values]: RgbColor + /// [colormap]: Colormap + /// [screen]: crate::visual::Screen + /// [request]: Request + #[context(name_len => usize::from(*name_len))] + pub name: String8, + [_; name => pad(name)], + } +} diff --git a/src/x11/request/font.rs b/src/x11/request/font.rs new file mode 100644 index 00000000..66c32739 --- /dev/null +++ b/src/x11/request/font.rs @@ -0,0 +1,321 @@ +// 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 [core X11 protocol] that relate to fonts. +//! +//! [Requests] are messages sent from an X client to the X server. +//! +//! [Requests]: Request +//! [core X11 protocol]: crate::x11 + +extern crate self as xrb; + +use xrbk::{pad, ConstantX11Size}; +use xrbk_macro::derive_xrb; + +use crate::{ + message::Request, + x11::{error, reply}, + Char16, + Font, + Fontable, + LengthString8, + String16, + String8, +}; + +macro_rules! request_error { + ( + $(#[$meta:meta])* + $vis:vis enum $Name:ident for $Request:ty { + $($($Error:ident),+$(,)?)? + } + ) => { + #[doc = concat!( + "An [error](crate::message::Error) generated because of a failed [`", + stringify!($Request), + "` request](", + stringify!($Request), + ")." + )] + #[doc = ""] + $(#[$meta])* + $vis enum $Name { + $($( + #[doc = concat!( + "A [`", + stringify!($Error), + "` error](error::", + stringify!($Error), + ")." + )] + $Error(error::$Error) + ),+)? + } + }; +} + +request_error! { + pub enum AssignFontError for AssignFont { + ResourceIdChoice, + Name, + } +} + +derive_xrb! { + /// A [request] that associates the font by the given `name` with the given + /// `font_id`. + /// + /// [request]: Request + #[doc(alias("OpenFont", "CreateFont", "LoadFont", "AddFont"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct AssignFont: Request(45, AssignFontError) { + /// The [`Font` ID] to associate with the font specified by `name`. + /// + /// [`Font` ID]: Font + pub font_id: Font, + + // The length of `name`. + #[allow(clippy::cast_possible_truncation)] + let name_len: u16 = name => name.len() as u16, + [_; 2], + + /// A pattern match against the name of the font. + /// + /// The name uses ISO Latin-1 encoding. + /// + /// The character `?` matches against any single character (equivalent + /// to `.` in regular expressions) and `*` matches against any number of + /// characters (like `.*` in regular expressions). + #[context(name_len => usize::from(*name_len))] + pub name: String8, + [_; name => pad(name)], + } + + /// A [request] that removes the association between a given [`Font` ID] and + /// the font it is associated with. + /// + /// [request]: Request + /// [`Font` ID]: Font + #[doc(alias("CloseFont", "DeleteFont", "UnloadFont", "RemoveFont"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct UnassignFont: Request(46) { + /// The [`Font` ID] which is having its association with a font removed. + /// + /// [`Font` ID]: Font + pub target: Font, + } + + /// A [request] that returns information about the given `target` + /// font. + /// + /// # Replies + /// This [request] generates a [`QueryFont` reply]. + /// + /// # Errors + /// A [`Font` error] is generated if the `target` does not refer to a + /// defined [`Font`] nor [`GraphicsContext`]. + /// + /// [request]: Request + /// + /// [`GraphicsContext`]: crate::GraphicsContext + /// + /// [`QueryFont` reply]: reply::QueryFont + /// + /// [`Font` error]: error::Font + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct QueryFont: Request(47, error::Font) -> reply::QueryFont { + /// The font which this [request] returns information about. + /// + /// # Errors + /// A [`Font` error] is generated if this does not refer to a defined + /// [`Font`] nor [`GraphicsContext`]. + /// + /// [request]: Request + /// + /// [`GraphicsContext`]: crate::GraphicsContext + /// + /// [`Font` error]: error::Font + pub target: Fontable, + } +} + +/// A private function used in [`QueryTextExtents`] to +/// determine padding. +#[inline] +const fn query_text_extents_padding(odd_length: bool) -> usize { + if odd_length { + 2 // Char16::X11_SIZE + } else { + 0 + } +} + +derive_xrb! { + /// A [request] that returns the extents of the given `text` when displayed + /// with the given `font`. + /// + /// If the font has no specified `fallback_character`, undefined characters + /// in the `text` are ignored. + /// + /// # Replies + /// This [request] generates a [`QueryTextExtents` reply]. + /// + /// # Errors + /// A [`Font` error] is generated if `font` does not refer to a defined + /// [`Font`] nor [`GraphicsContext`]. + /// + /// [request]: Request + /// + /// [`GraphicsContext`]: crate::GraphicsContext + /// + /// [`QueryTextExtents` reply]: reply::QueryTextExtents + /// + /// [`Font` error]: error::Font + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct QueryTextExtents: Request(48, error::Font) -> reply::QueryTextExtents { + // Whether `text` is of odd length. Is it is, it has 2 bytes of padding + // following it. + #[metabyte] + let odd_length: bool = text => text.len() % 2 != 0, + + /// The font used in the `text`. + /// + /// # Errors + /// A [`Font` error] is generated if this does not refer to a defined + /// [`Font`] nor [`GraphicsContext`]. + /// + /// [`GraphicsContext`]: crate::GraphicsContext + /// + /// [`Font` error]: error::Font + pub font: Fontable, + + /// The text for which this [request] gets the extents when displayed + /// with `font`. + /// + /// [request]: Request + #[context(self::remaining, odd_length => { + // We remove the padding at the end, which can be determined from `odd_length`. + let remaining = remaining - query_text_extents_padding(*odd_length); + + // We then divide the length, which is the number of bytes, by the number of bytes + // per character. + remaining / Char16::X11_SIZE + })] + pub text: String16, + [_; odd_length => query_text_extents_padding(*odd_length)] + } + + /// A [request] that lists the names of available fonts (as controlled by + /// the [font search path]). + /// + /// # Replies + /// This [request] generates a [`ListFonts` reply]. + /// + /// [request]: Request + /// + /// [font search path]: SetFontSearchDirectories + /// + /// [`ListFonts` reply]: reply::ListFonts + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct ListFonts: Request(49) -> reply::ListFonts { + /// The maximum number of names that will appear in the returned font + /// `names`. + #[doc(alias("max_names", "max_names_len"))] + pub max_names_count: u16, + + #[allow(clippy::cast_possible_truncation)] + let pattern_len: u16 = pattern => pattern.len() as u16, + /// A pattern match against the font names. + /// + /// The case (uppercase or lowercase) of the pattern does not matter: + /// font names are converted to lowercase, as is the pattern. + /// + /// Font names use ISO Latin-1 encoding. + /// + /// The character `?` matches against any single character (equivalent + /// to `.` in regular expressions) and `*` matches against any number of + /// characters (like `.*` in regular expressions). + #[context(pattern_len => usize::from(*pattern_len))] + pub pattern: String8, + [_; pattern => pad(pattern)], + } + + /// A [request] that lists available fonts (as controlled by the + /// [font search path]) with information about them. + /// + /// The information returned for each font almost entirely matches that + /// returned in a [`QueryFont` reply]. + /// + /// # Replies + /// This [request] generates [`ListFontsWithInfo` replies]. + /// + /// [request]: Request + /// + /// [font search path]: SetFontSearchDirectories + /// + /// [`ListFontsWithInfo` replies]: reply::ListFontsWithInfo + /// [`QueryFont` reply]: reply::QueryFont + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct ListFontsWithInfo: Request(50) -> reply::ListFontsWithInfo { + /// The maximum number of [`FontWithInfo` replies] that will be returned. + /// + /// [`FontWithInfo` replies]: reply::FontWithInfo + #[doc(alias("max_names", "max_names_len"))] + pub max_fonts_count: u16, + + #[allow(clippy::cast_possible_truncation)] + let pattern_len: u16 = pattern => pattern.len() as u16, + /// A pattern match against the font names. + /// + /// The case (uppercase or lowercase) of the pattern does not matter: + /// font names are converted to lowercase, as is the pattern. + /// + /// Font names use ISO Latin-1 encoding. + /// + /// The character `?` matches against any single character (equivalent + /// to `.` in regular expressions) and `*` matches against any number of + /// characters (like `.*` in regular expressions). + #[context(pattern_len => usize::from(*pattern_len))] + pub pattern: String8, + [_; pattern => pad(pattern)], + } + + /// A [request] that defines the directories which are searched for + /// available fonts. + /// + /// # Errors + /// A [`Value` error] is generated if the operating system rejects the given + /// paths for whatever reason. + /// + /// [request]: Request + /// + /// [`Value` error]: error::Value + #[doc(alias = "SetFontPath")] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct SetFontSearchDirectories: Request(51, error::Value) { + // The length of `directories`. + #[allow(clippy::cast_possible_truncation)] + let directories_len: u16 = directories => directories.len() as u16, + [_; 2], + + /// The directories to be searched in the order listed. + /// + /// Specifying an empty list here restores the default font search + /// directories defined for the X server. + #[doc(alias = "path")] + #[context(directories_len => usize::from(*directories_len))] + pub directories: Vec, + [_; directories => pad(directories)], + } + + /// A [request] that returns the current directories which are searched to + /// find available fonts. + /// + /// See also: [`SetFontSearchDirectories`]. + /// + /// [request]: Request + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct GetFontSearchDirectories: Request(52) -> reply::GetFontSearchDirectories; +} diff --git a/src/x11/request/graphics.rs b/src/x11/request/graphics.rs new file mode 100644 index 00000000..168309f3 --- /dev/null +++ b/src/x11/request/graphics.rs @@ -0,0 +1,2763 @@ +// 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 [core X11 protocol] that relate to graphics +//! operations. +//! +//! [Requests] are messages sent from an X client to the X server. +//! +//! [Requests]: Request +//! [core X11 protocol]: crate::x11 + +mod config; +pub use config::*; + +extern crate self as xrb; + +use thiserror::Error; + +use xrbk::{ + pad, + Buf, + BufMut, + ConstantX11Size, + ReadResult, + Readable, + ReadableWithContext, + Writable, + WriteResult, + X11Size, +}; +use xrbk_macro::{derive_xrb, ConstantX11Size, Readable, Writable, X11Size}; + +use crate::{ + message::Request, + unit::Px, + x11::{error, reply}, + Arc, + Coords, + Dimensions, + Drawable, + Font, + GraphicsContext, + Rectangle, + String16, + String8, + Window, +}; + +macro_rules! request_error { + ( + $(#[$meta:meta])* + $vis:vis enum $Name:ident for $Request:ty { + $($($Error:ident),+$(,)?)? + } + ) => { + #[doc = concat!( + "An [error](crate::message::Error) generated because of a failed [`", + stringify!($Request), + "` request](", + stringify!($Request), + ")." + )] + #[doc = ""] + $(#[$meta])* + $vis enum $Name { + $($( + #[doc = concat!( + "A [`", + stringify!($Error), + "` error](error::", + stringify!($Error), + ")." + )] + $Error(error::$Error) + ),+)? + } + }; +} + +request_error! { + pub enum ClearAreaError for ClearArea { + Match, + Value, + Window, + } +} + +derive_xrb! { + /// A [request] that clears a particular area of a [window]. + /// + /// If the [window] has a defined background ([`background_pixmap`] or + /// [`background_color`], the `area` is replaced by that background. + /// Otherwise, in the background is [`None`], the contents are not changed. + /// + /// # Errors + /// A [`Window` error] is generated if `target` does not refer to a defined + /// [window]. + /// + /// A [`Match` error] is generated if the `target` is an [`InputOnly`] + /// [window]. + /// + /// [window]: Window + /// [request]: Request + /// + /// [`background_pixmap`]: crate::set::Attributes::background_pixmap + /// [`background_color`]: crate::set::Attributes::background_color + /// + /// [`InputOnly`]: crate::WindowClass::InputOnly + /// + /// [`Window` error]: error::Window + /// [`Match` error]: error::Match + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct ClearArea: Request(61, ClearAreaError) { + /// Whether [`GraphicsExposure` events] should be generated for regions + /// of the `area` which are visible or maintained. + /// + /// [`GraphicsExposure` events]: crate::x11::event::GraphicsExposure + #[metabyte] + pub graphics_exposure: bool, + + /// The [window] which this [request] clears an area of. + /// + /// # Errors + /// A [`Window` error] is generated if this does not refer to a defined + /// [window]. + /// + /// A [`Match` error] is generated if this is an [`InputOnly`] [window]. + /// + /// [window]: Window + /// [request]: Request + /// + /// [`InputOnly`]: crate::WindowClass::InputOnly + /// + /// [`Window` error]: error::Window + /// [`Match` error]: error::Match + pub target: Window, + + /// The area of the `target` [window] which is cleared. + /// + /// The `x` and `y` coordinates are relative to the top-left corner of + /// the `target` [window]. + /// + /// [window]: Window + pub area: Rectangle, + } +} + +request_error! { + pub enum CopyAreaError for CopyArea { + Drawable, + GraphicsContext, + Match, + } +} + +derive_xrb! { + /// A [request] that copies an area of the given `source` [drawable] into + /// the given `destination` [drawable]. + /// + /// [Regions][regions] of the `source` that are obscured and have not been + /// [maintained], as well as [regions] specified by `source_coords` and + /// `dimensions` fall outside of the `source` itself, are not copied. If the + /// `destination` has a background, however, and those [regions] of the + /// `source` which are not copied correspond to [regions] of the + /// `destination` which are visible or [maintained], those [regions] will be + /// filled with the `destination`'s background. If the `graphics_context`'s + /// [`graphics_exposure`] is `true`, [`GraphicsExposure` events] will be + /// generated for those [regions] (or a [`NoExposure` event] if none are + /// generated). + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`child_mode`] + /// - [`graphics_exposure`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// # Errors + /// A [`Drawable` error] is generated if either `source` or `destination` do + /// not refer to defined [windows] nor [pixmaps]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [drawable]: Drawable + /// [windows]: Window + /// [pixmaps]: Pixmap + /// [regions]: crate::Region + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [maintained]: crate::MaintainContents + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`graphics_exposure`]: GraphicsOptions::graphics_exposure + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_y + /// + /// [`GraphicsExposure` events]: crate::x11::event::GraphicsExposure + /// [`NoExposure` event]: crate::x11::event::NoExposure + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + /// [`Match` error]: error::Match + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct CopyArea: Request(62, CopyAreaError) { + /// The [drawable] from which the area is copied. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`Match` error] is generated if this does not have the same root + /// [window] and depth as the `destination`. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + /// [`Match` error]: error::Match + pub source: Drawable, + /// The [drawable] into which the area is copied. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`Match` error] is generated if this does not have the same root + /// [window] and depth as the `destination`. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + /// [`Match` error]: error::Match + pub destination: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + pub graphics_context: GraphicsContext, + + /// The coordinates of the area which is copied from the `source` + /// [drawable]. + /// + /// These coordinates are relative to the top-left corner of the + /// `source`, and specify the top-left corner of the area which is + /// copied. + /// + /// [drawable]: Drawable + pub source_coords: Coords, + /// The coordinates at which the copied area will be placed within the + /// `destination` [drawable]. + /// + /// These coordinates are relative to the top-left corner of the + /// `destination`, and specify what the top-left corner of the copied + /// area will be when it has been copied. + /// + /// [drawable]: Drawable + pub destination_coords: Coords, + + /// The dimensions of the area that is copied. + pub dimensions: Dimensions, + } +} + +request_error! { + #[doc(alias("CopyPlaneError"))] + pub enum CopyBitPlaneError for CopyBitPlane { + Drawable, + GraphicsContext, + Match, + Value, + } +} + +derive_xrb! { + /// A [request] that copies a [region] of the `source` [drawable], masked by the given + /// `bit_plane`, and filled according to the [`foreground_color`] and [`background_color`] in + /// the `graphics_context`. + /// + /// Effectively, a [pixmap] with the given `dimensions` and the same depth + /// as the `destination` [drawable] is created. It is filled with the + /// [`foreground_color`] where the `bit_plane` in the `source` [drawable] + /// contains a bit set to 1, and [`background_color`] where the `bit_plane` + /// in the `source` [drawable] contains a bit set to 0. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`child_mode`] + /// - [`graphics_exposure`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// # Errors + /// A [`Drawable` error] is generated if either the `source` or the + /// `destination` do not refer to defined [windows][window] nor + /// [pixmaps][pixmap]. + /// + /// A [`GraphicsContext` error] is generated if the `graphics_context` does + /// not refer to a defined [`GraphicsContext`]. + /// + /// A [`Match` error] is generated if the `source` [drawable] does not have + /// the same root [window] as the `destination` [drawable]. + /// + /// A [`Value` error] is generated if the `bit_plane` does not have exactly + /// one bit set to 1, or if the value of the `bit_plane` is not less than + /// 2`depth`, where `depth` is the `source` [drawable]'s depth. + /// + /// [drawable]: Drawable + /// [pixmap]: Pixmap + /// [window]: Window + /// [region]: crate::Region + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`graphics_exposure`]: GraphicsOptions::graphics_exposure + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + /// [`Match` error]: error::Match + /// [`Value` error]: error::Value + #[doc(alias("CopyPlane"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct CopyBitPlane: Request(63, CopyBitPlaneError) { + /// The [drawable] used as the source in this graphics operation. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`Match` error] is generated if this does not have the same root + /// [window] as the `destination`. + /// + /// [drawable]: Drawable + /// [pixmap]: Pixmap + /// [window]: Window + /// + /// [`Drawable` error]: error::Drawable + /// [`Match` error]: error::Match + #[doc(alias("src", "src_drawable", "source_drawable"))] + pub source: Drawable, + /// The [drawable] which the filled [region] is copied into. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`Match` error] is generated if this does not have the same root + /// [window] as the `source`. + /// + /// [region]: crate::Region + /// [drawable]: Drawable + /// [pixmap]: Pixmap + /// [window]: Window + /// + /// [`Drawable` error]: error::Drawable + /// [`Match` error]: error::Match + #[doc(alias("dst", "dst_drawable", "destination_drawable"))] + pub destination: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The coordinates of the [region] within the `source` [drawable] which + /// is used. + /// + /// These coordinates are relative to the top-left corner of the + /// `source`, and specify the top-left corner of the [region] which is + /// copied. + /// + /// [region]: crate::Region + /// [drawable]: Drawable + #[doc(alias("src_x", "src_y", "source_x", "source_y", "src_coords"))] + pub source_coords: Coords, + /// The coordinates at which the copied [region] will be placed within + /// the `destination` [drawable]. + /// + /// These coordinates are relative to the top-left corner of the + /// `destination`, and specify what the top-left corner of the copied + /// [region] will be when it has been copied. + /// + /// [region]: crate::Region + /// [drawable]: Drawable + #[doc(alias("dst_x", "dst_y", "destination_x", "destination_y", "dst_coords"))] + pub destination_coords: Coords, + + /// The dimensions of the [region] that is copied. + /// + /// [region]: crate::Region + #[doc(alias("width", "height"))] + pub dimensions: Dimensions, + + /// The bit plane that is copied. + /// + /// Exactly one bit must be set and the value must be less than + /// 2`depth`, where `depth` is the depth of the `source` + /// [drawable]. + /// + /// # Errors + /// A [`Value` error] is generated if this does not have exactly one bit + /// set to 1, or if this value is not less than 2`depth`, + /// where `depth` is the depth of the `source` [drawable]. + /// + /// [drawable]: Drawable + /// + /// [`Value` error]: error::Value + pub bit_plane: u32, + } +} + +request_error! { + #[doc(alias("PolyPointError", "DrawPointError"))] + pub enum DrawPointsError for DrawPoints { + Drawable, + GraphicsContext, + Match, + Value, + } +} + +/// Whether [coordinates] of elements to be drawn in graphics operations are +/// relative to the [drawable] or the previous element. +/// +/// The first element is always relative to the [drawable]. +/// +/// [coordinates]: Coords +/// [drawable]: Drawable +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum CoordinateMode { + /// [Coordinates] are relative to the top-left corner of the [drawable]. + /// + /// [Coordinates]: Coords + /// [drawable]: Drawable + Drawable, + + /// [Coordinates][coords] are relative to the [coordinates][coords] of the + /// previous element. + /// + /// [coords]: Coords + Previous, +} + +derive_xrb! { + /// A [request] that draws the given `points` on the `target` [drawable]. + /// + /// The points are drawn by combining the `graphics_context`'s + /// [`foreground_color`] with the existing color at those coordinates in the + /// [drawable]. They are drawn in the order that they are specified in the + /// list. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`foreground_color`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + /// [`Match` error]: error::Match + #[doc(alias("PolyPoint", "DrawPoint"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct DrawPoints: Request(64, DrawPointsError) { + /// Whether the `points` are drawn relative to the `target` or the + /// previously drawn point. + /// + /// The first point is always drawn relative to the `target`. + /// + /// See [`CoordinateMode`] for more information. + #[metabyte] + pub coordinate_mode: CoordinateMode, + + /// The [drawable] on which the given `points` are drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The points which are to be drawn. + /// + /// Each point is represented by its [coordinates]. + /// + /// The points are drawn in the order that they appear in the list. + /// + /// Each point is drawn by combining the [`foreground_color`] with the + /// existing color of the pixel at the point's [coordinates]. + /// + /// [coordinates]: Coords + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + #[context(self::remaining => remaining / Coords::X11_SIZE)] + pub points: Vec, + } +} + +request_error! { + #[doc(alias("PolyLineError", "DrawLinesError", "DrawLineError"))] + pub enum DrawPathError for DrawPath { + Drawable, + GraphicsContext, + Match, + Value, + } +} + +derive_xrb! { + /// A [request] that draws a path comprised of lines that join the given + /// `points`. + /// + /// The lines are drawn in the order that the points appear in `points`. + /// They join at each intermediate point. If the first and last points are + /// in the same location, they are also joined to create a closed path (with + /// no endpoints). + /// + /// Intersecting [thin] lines have their intersecting pixels drawn multiple + /// times. Intersecting [thick] lines, however, only have their intersecting + /// pixels drawn once. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`line_width`] + /// - [`line_style`] + /// - [`cap_style`] + /// - [`join_style`] + /// - [`fill_style`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// This [request] may also use these [options], depending on the + /// configuration of the `graphics_context`: + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`tile`] + /// - [`stipple`] + /// - [`tile_stipple_x`] + /// - [`tile_stipple_y`] + /// - [`dash_offset`] + /// - [`dashes`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [thin]: crate::set::LineWidth::Thin + /// [thick]: crate::set::LineWidth::Thick + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`line_width`]: GraphicsOptions::line_width + /// [`line_style`]: GraphicsOptions::line_style + /// [`cap_style`]: GraphicsOptions::cap_style + /// [`join_style`]: GraphicsOptions::join_style + /// [`fill_style`]: GraphicsOptions::fill_style + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`tile`]: GraphicsOptions::tile + /// [`stipple`]: GraphicsOptions::stipple + /// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x + /// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y + /// [`dash_offset`]: GraphicsOptions::dash_offset + /// [`dashes`]: GraphicsOptions::dashes + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("PolyLine", "DrawLines", "DrawLine"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct DrawPath: Request(65, DrawPathError) { + /// Whether the [coordinates] of each point in `points` are relative to + /// the `target` or to the previous point. + /// + /// The first point is always relative to the `target` [drawable]. + /// + /// [coordinates]: Coords + /// [drawable]: Drawable + #[metabyte] + pub coordinate_mode: CoordinateMode, + + /// The [drawable] on which the path is drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The points which are to be connected by lines. + /// + /// Each point is represented by its [coordinates]. + /// + /// The points are connected by lines in the order that they appear in + /// this list. + /// + /// [coordinates]: Coords + #[context(self::remaining => remaining / Coords::X11_SIZE)] + pub points: Vec, + } +} + +request_error! { + #[doc(alias("PolySegmentError", "DrawSegmentError"))] + pub enum DrawLinesError for DrawLines { + Drawable, + GraphicsContext, + Match, + } +} + +/// A line from the given `start` point to the given `end` point. +#[doc(alias("Segment"))] +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] +pub struct Line { + /// The start of the line. + pub start: Coords, + /// The end of the line. + pub end: Coords, +} + +derive_xrb! { + /// A [request] that draws the given `lines`. + /// + /// No join points are created. Intersecting [lines] have their intersecting + /// pixels drawn multiple times. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`line_width`] + /// - [`line_style`] + /// - [`cap_style`] + /// - [`fill_style`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// This [request] may also use these [options], depending on the + /// configuration of the `graphics_context`: + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`tile`] + /// - [`stipple`] + /// - [`tile_stipple_x`] + /// - [`tile_stipple_y`] + /// - [`dash_offset`] + /// - [`dashes`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [lines]: Line + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`line_width`]: GraphicsOptions::line_width + /// [`line_style`]: GraphicsOptions::line_style + /// [`cap_style`]: GraphicsOptions::cap_style + /// [`fill_style`]: GraphicsOptions::fill_style + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`tile`]: GraphicsOptions::tile + /// [`stipple`]: GraphicsOptions::stipple + /// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x + /// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y + /// [`dash_offset`]: GraphicsOptions::dash_offset + /// [`dashes`]: GraphicsOptions::dashes + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("PolySegment", "DrawSegment"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct DrawLines: Request(66, DrawLinesError) { + /// The [drawable] on which the given `lines` are drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The lines which are to be drawn. + /// + /// The lines are drawn in the order that they appear in this list. + #[context(self::remaining => remaining / Line::X11_SIZE)] + pub lines: Vec, + } +} + +request_error! { + #[doc(alias("PolyRectangleError"))] + pub enum DrawRectanglesError for DrawRectangles { + Drawable, + GraphicsContext, + Match, + } +} + +derive_xrb! { + /// A [request] that draws the outlines of the given `rectangles`. + /// + /// The outlines are drawn as [lines] connecting each corner of the + /// [rectangles], starting at the top-left corner and going clockwise, with + /// a join point at each corner - thus forming a closed path. + /// + /// If any of the given `rectangles` intersect, the intersecting pixels are + /// drawn multiple times. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`line_width`] + /// - [`line_style`] + /// - [`cap_style`] + /// - [`join_style`] + /// - [`fill_style`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// This [request] may also use these [options], depending on the + /// configuration of the `graphics_context`: + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`tile`] + /// - [`stipple`] + /// - [`tile_stipple_x`] + /// - [`tile_stipple_y`] + /// - [`dash_offset`] + /// - [`dashes`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [window]: Window + /// [pixmap]: Pixmap + /// [lines]: Line + /// [rectangles]: Rectangle + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`line_width`]: GraphicsOptions::line_width + /// [`line_style`]: GraphicsOptions::line_style + /// [`cap_style`]: GraphicsOptions::cap_style + /// [`join_style`]: GraphicsOptions::join_style + /// [`fill_style`]: GraphicsOptions::fill_style + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`tile`]: GraphicsOptions::tile + /// [`stipple`]: GraphicsOptions::stipple + /// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x + /// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y + /// [`dash_offset`]: GraphicsOptions::dash_offset + /// [`dashes`]: GraphicsOptions::dashes + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("PolyRectangle"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct DrawRectangles: Request(67, DrawRectanglesError) { + /// The [drawable] on which the `rectangles`' outlines are drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The [rectangles] which are to have their outlines drawn. + /// + /// Their `x` and `y` coordinates are relative to the top-left corner of + /// the `target` [drawable]. + /// + /// The [rectangles] are drawn in the order that they appear in this + /// list. + /// + /// [rectangles]: Rectangle + /// [drawable]: Drawable + #[context(self::remaining => remaining / Rectangle::X11_SIZE)] + pub rectangles: Vec, + } +} + +request_error! { + #[doc(alias("PolyArcError"))] + pub enum DrawArcsError for DrawArcs { + Drawable, + GraphicsContext, + Match, + } +} + +derive_xrb! { + /// A [request] that draws circular or elliptical [arcs][arc]. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`line_width`] + /// - [`line_style`] + /// - [`cap_style`] + /// - [`join_style`] + /// - [`fill_style`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// This [request] may also use these [options], depending on the + /// configuration of the `graphics_context`: + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`tile`] + /// - [`stipple`] + /// - [`tile_stipple_x`] + /// - [`tile_stipple_y`] + /// - [`dash_offset`] + /// - [`dashes`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [arc]: Arc + /// [window]: Window + /// [pixmap]: Pixmap + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`line_width`]: GraphicsOptions::line_width + /// [`line_style`]: GraphicsOptions::line_style + /// [`cap_style`]: GraphicsOptions::cap_style + /// [`join_style`]: GraphicsOptions::join_style + /// [`fill_style`]: GraphicsOptions::fill_style + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`tile`]: GraphicsOptions::tile + /// [`stipple`]: GraphicsOptions::stipple + /// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x + /// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y + /// [`dash_offset`]: GraphicsOptions::dash_offset + /// [`dashes`]: GraphicsOptions::dashes + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("PolyArc"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct DrawArcs: Request(68, DrawArcsError) { + /// The [drawable] on which the [arcs] are drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [arcs]: Arc + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The [arcs][arc] which are to be drawn. + /// + /// If the last point in an [arc] is in the same location as the first + /// point in the next [arc], the two [arcs][arc] are joined with a + /// joinpoint. If the last point of the last [arc] and the first point + /// of the first [arc] are in the same location, they will also be + /// joined. + /// + /// [arc]: Arc + #[context(self::remaining => remaining / Arc::X11_SIZE)] + pub arcs: Vec, + } +} + +request_error! { + #[doc(alias("FillPolyError"))] + pub enum FillPolygonError for FillPolygon { + Drawable, + GraphicsContext, + Match, + Value, + } +} + +/// Specifies properties of a polygon that may allow for optimizations when +/// drawing it. +/// +/// This is used in the [`FillPolygon` request]. +/// +/// [`FillPolygon` request]: FillPolygon +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum ShapeMode { + /// The shape may intersect itself. + Complex, + + /// The shape may not intersect itself, but it is not (fully) convex. + /// + /// If these properties are known to be true by the client, specifying this + /// mode may improve performance over [`Complex`](ShapeMode::Complex). + Nonconvex, + + /// The shape may not intersect itself and it is convex. + /// + /// Convex means that no [line] could be drawn between two points in the + /// shape which intersects the shape's path. + /// + /// If these properties are known to be true by the client, specifying this + /// mode may improve performance over [`Nonconvex`](ShapeMode::Nonconvex). + /// + /// [line]: Line + Convex, +} + +derive_xrb! { + /// A [request] that fills the area enclosed the given path. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`fill_style`] + /// - [`fill_rule`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// This [request] may also use these [options], depending on the + /// configuration of the `graphics_context`: + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`tile`] + /// - [`stipple`] + /// - [`tile_stipple_x`] + /// - [`tile_stipple_y`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [window]: Window + /// [pixmap]: Pixmap + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`fill_style`]: GraphicsOptions::fill_style + /// [`fill_rule`]: GraphicsOptions::fill_rule + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`tile`]: GraphicsOptions::tile + /// [`stipple`]: GraphicsOptions::stipple + /// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x + /// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("FillPoly"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct FillPolygon: Request(69, FillPolygonError) { + /// The [drawable] on which the filled polygon is drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// Specifies whether the polygon self-intersects and whether it is + /// convex. + /// + /// If the relevant properties of the specified polygon are known by the + /// client, specifying a more restrictive [shape mode] may improve + /// performance. + /// + /// See [`ShapeMode`] for more information. + /// + /// [shape mode]: ShapeMode + pub shape: ShapeMode, + /// Whether the [coordinates] of each point in `points` are relative to + /// the `target` or to the previous point. + /// + /// The first point is always relative to the `target` [drawable]. + /// + /// [coordinates]: Coords + /// [drawable]: Drawable + pub coordinate_mode: CoordinateMode, + [_; 2], + + /// The points which, when connected, specify the path of the polygon. + /// + /// Each point is represented by its [coordinates]. + /// + /// The points are connected in the order that they appear in this list. + /// If the last point is not in the same location as the first point, it + /// is automatically connected to the first point to close the path. + /// + /// [coordinates]: Coords + #[context(self::remaining => remaining / Coords::X11_SIZE)] + pub points: Vec, + } +} + +request_error! { + #[doc(alias("PolyFillRectangleError"))] + pub enum FillRectanglesError for FillRectangles { + Drawable, + GraphicsContext, + Match, + } +} + +derive_xrb! { + /// A [request] that fills the given `rectangles`. + /// + /// This is effectively the same as if a [`FillPolygon` request] were sent + /// for each [rectangle] with the [rectangle]'s four points, starting with + /// the top-left corner and going clockwise. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`fill_style`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// This [request] may also use these [options], depending on the + /// configuration of the `graphics_context`: + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`tile`] + /// - [`stipple`] + /// - [`tile_stipple_x`] + /// - [`tile_stipple_y`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [window]: Window + /// [pixmap]: Pixmap + /// [options]: GraphicsOptions + /// [rectangle]: Rectangle + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`fill_style`]: GraphicsOptions::fill_style + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`tile`]: GraphicsOptions::tile + /// [`stipple`]: GraphicsOptions::stipple + /// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x + /// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y + /// + /// [`FillPolygon` request]: FillPolygon + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("PolyFillRectangle"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct FillRectangles: Request(70, FillRectanglesError) { + /// The [drawable] on which the [rectangles] are filled. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [rectangles]: Rectangle + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The [rectangles] which are to be filled. + /// + /// The [rectangles] are filled in the order that they appear in this + /// list. For intersecting [rectangles], the intersecting pixels are + /// drawn multiple times. + /// + /// [rectangles]: Rectangle + #[context(self::remaining => remaining / Rectangle::X11_SIZE)] + pub rectangles: Vec, + } +} + +request_error! { + #[doc(alias("PolyFillArcError"))] + pub enum FillArcsError for FillArcs { + Drawable, + GraphicsContext, + Match, + } +} + +derive_xrb! { + /// A [request] that fills the given `arcs`. + /// + /// If the `graphics_context`'s [`arc_mode`] is [`ArcMode::Chord`], the + /// [arcs][arc] are filled by joining each [arc]'s endpoints with a single + /// [line]. If the [`arc_mode`] is [`ArcMode::PieSlice`], however, the + /// [arcs][arc] are filled by joining each [arc]'s endpoints with the center + /// of that [arc], thus using two [lines][line]. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`fill_style`] + /// - [`arc_mode`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// This [request] may also use these [options], depending on the + /// configuration of the `graphics_context`: + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`tile`] + /// - [`stipple`] + /// - [`tile_stipple_x`] + /// - [`tile_stipple_y`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [window]: Window + /// [pixmap]: Pixmap + /// [options]: GraphicsOptions + /// [arc]: Arc + /// [line]: Line + /// [request]: Request + /// + /// [`ArcMode::Chord`]: crate::set::ArcMode::Chord + /// [`ArcMode::PieSlice`]: crate::set::ArcMode::PieSlice + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`fill_style`]: GraphicsOptions::fill_style + /// [`arc_mode`]: GraphicsOptions::arc_mode + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`tile`]: GraphicsOptions::tile + /// [`stipple`]: GraphicsOptions::stipple + /// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x + /// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("PolyFillArc"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct FillArcs: Request(71, FillArcsError) { + /// The [drawable] on which the [arcs] are filled. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [arcs]: Arc + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The [arcs] which are to be filled. + /// + /// The [arcs] are filled in the order that they appear in this list. If + /// there are intersecting [arcs], the intersecting pixels will be drawn + /// multiple times. + /// + /// [arcs]: Arc + #[context(self::remaining => remaining / Arc::X11_SIZE)] + pub arcs: Vec, + } +} + +request_error! { + #[doc(alias("PutImageError"))] + pub enum PlaceImageError for PlaceImage { + Drawable, + GraphicsContext, + Match, + Value, + } +} + +/// The format of an image sent in a [`PlaceImage` request]. +/// +/// [`PlaceImage` request]: PlaceImage +#[doc(alias("PutImageFormat"))] +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum PlaceImageFormat { + /// The image must be in XY format. + /// + /// The `graphics_context`'s [`foreground_color`] is used where the image + /// has bits set to `1`, while the [`background_color`] is used where the + /// image has bits set to `0`. + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + Bitmap, + + /// The image must be in XY format. + XyPixmap, + + /// The image must be in Z format. + Zpixmap, +} + +derive_xrb! { + /// A [request] that places the given image on the given [drawable]. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`function`] + /// - [`plane_mask`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// This [request] may also use these [options], depending on the + /// provided `format`: + /// - [`foreground_color`] + /// - [`background_color`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// A [`Match` error] is generated if [`PlaceImageFormat::XyPixmap`] or + /// [`PlaceImageFormat::Zpixmap`] is used and `depth` does not match the + /// depth of the `target` [drawable]. + /// + /// A [`Match` error] is generated if [`PlaceImageFormat::Bitmap`] is used + /// and `depth` is not `1`. + /// + /// A [`Match` error] is generated if [`PlaceImageFormat::Bitmap`] or + /// [`PlaceImageFormat::XyPixmap`] is used and `left_padding` is not less + /// than `bitmap_scanline_padding` (given in + /// [`connection::ConnectionSuccess`]). + /// + /// A [`Match` error] is generated if [`PlaceImageFormat::Zpixmap`] is used + /// and `left_padding` is not `0`. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`function`]: GraphicsOptions::function + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// + /// [`connection::ConnectionSuccess`]: crate::connection::ConnectionSuccess + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + /// [`Match` error]: error::Match + #[doc(alias("PutImage"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct PlaceImage: Request(72, PlaceImageError) { + /// The [image format] used. + /// + /// [image format]: PlaceImageFormat + #[metabyte] + pub format: PlaceImageFormat, + + /// The [drawable] on which the image is placed. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The image's width and height. + pub dimensions: Dimensions, + /// The [coordinates] at which the image will be placed on the `target` + /// [drawable]. + /// + /// These coordinates are relative to the top-left corner of the + /// [drawable]. + /// + /// [drawable]: Drawable + /// [coordinates]: Coords + pub coordinates: Coords, + + /// The number of bits in each scanline of the image which are to be + /// ignored by the X server. + /// + /// The actual image begins this many bits into the scanline. + /// + /// This is only used for [`PlaceImageFormat::Bitmap`] and + /// [`PlaceImageFormat::XyPixmap`]. It must be `0` for + /// [`PlaceImageFormat::Zpixmap`]. + /// + /// # Errors + /// A [`Match` error] is generated if `format` is + /// [`PlaceImageFormat::Bitmap`] or [`PlaceImageFormat::XyPixmap`] and + /// this is not less than `bitmap_scanline_padding` (given in + /// [`connection::ConnectionSuccess`]). + /// + /// A [`Match` error] is generated if `format` is + /// [`PlaceImageFormat::Zpixmap`] and this is not `0`. + /// + /// [`connection::ConnectionSuccess`]: crate::connection::ConnectionSuccess + /// + /// [`Match` error]: error::Match + pub left_padding: u8, + + /// The depth of the image. + /// + /// If `format` is [`PlaceImageFormat::Bitmap`], this must be `1`. + /// + /// # Errors + /// A [`Match` error] is generated if `format` is + /// [`PlaceImageFormat::XyPixmap`] or [`PlaceImageFormat::Zpixmap`] + /// and this does not match the depth of the `target` [drawable]. + /// + /// A [`Match` error] is generated if `format` is + /// [`PlaceImageFormat::Bitmap`] and this is not `1`. + /// + /// [drawable]: Drawable + /// + /// [`Match` error]: error::Match + pub depth: u8, + [_; 2], + + // FIXME: how do we know what is padding and what is data?????? + /// The image's data. + #[context(self::remaining => remaining)] + pub data: Vec, + [_; data => pad(data)], + } +} + +request_error! { + #[doc(alias("GetImageError"))] + pub enum CaptureImageError for CaptureImage { + Drawable, + Match, + Value, + } +} + +/// The format of the image returned in a [`CaptureImage` reply]. +/// +/// This is used in the [`CaptureImage` request]. +/// +/// [`CaptureImage` request]: CaptureImage +/// [`CaptureImage` reply]: reply::CaptureImage +#[doc(alias("GetImageFormat"))] +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum CaptureImageFormat { + /// The image is returned in XY format. + XyPixmap, + + /// The image is returned in Z format. + Zpixmap, +} + +derive_xrb! { + /// A [request] that returns the contents of the given `area` of the given + /// [drawable] as an image. + /// + /// If the `target` is a [window], the [window]'s border may be included in + /// the image. + /// + /// If the `target` is a [window], its contents are [maintained], and it is + /// obscured by a [window] which is not one of its descendents, the + /// [maintained] contents will be returned for those obscured regions. + /// Otherwise, if the contents are not [maintained], or the [window] is + /// obscured by one of its descendents, the contents of those obscured + /// regions in the returned image are undefined. The contents of visible + /// regions of descendents with a different depth than the `target` are also + /// undefined. + /// + /// The cursor is never included in the returned image. + /// + /// # Replies + /// This [request] generates a [`CaptureImage` reply]. + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`Match` error] is generated if the given area is not fully contained + /// within the `target` [drawable]. + /// + /// A [`Match` error] is generated if the `target` is a [window] and the + /// [window] is not viewable. + /// + /// A [`Match` error] is generated if the `target` is a [window] and the + /// [window] is not fully contained within the [screen]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [screen]: crate::visual::Screen + /// [request]: Request + /// + /// [maintained]: crate::MaintainContents + /// + /// [`CaptureImage` reply]: reply::CaptureImage + /// + /// [`Drawable` error]: error::Drawable + /// [`Match` error]: error::Match + #[doc(alias("GetImage"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct CaptureImage: Request(73, CaptureImageError) -> reply::CaptureImage { + /// The [image format] of the image that is returned in the + /// [`CaptureImage` reply]. + /// + /// [image format]: CaptureImageFormat + /// [`CaptureImage` reply]: reply::CaptureImage + #[metabyte] + pub format: CaptureImageFormat, + + /// The [drawable] for which this [request] captures an image from the + /// given `area`. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [request]: Request + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The area of the `target` [drawable] which this [request] captures an + /// image of. + /// + /// [drawable]: Drawable + /// [request]: Request + #[doc(alias("x", "y", "width", "height"))] + pub area: Rectangle, + + /// A mask applied to the returned image's bit planes. + /// + /// If the given `format` is [`CaptureImageFormat::XyPixmap`], only the + /// bit planes specified in this mask are transmitted, and they are + /// transmitted from most significant to least significant in bit order. + /// + /// If the given `format` is [`CaptureImageFormat::Zpixmap`], all bits + /// in planes not specified by this mask are zero. + pub plane_mask: u32, + } +} + +request_error! { + #[doc(alias("PolyText8Error"))] + pub enum DrawText8Error for DrawText8 { + Drawable, + Font, + GraphicsContext, + Match, + } +} + +/// A 'text item' specified in a [`DrawText8` request]. +/// +/// [`DrawText8` request]: DrawText8 +#[derive(Debug, Hash, PartialEq, Eq)] +pub enum TextItem8 { + /// Specifies text that is to be drawn with the `graphics_context`'s current + /// [font]. + /// + /// [font]: Font + Text(Box), + + /// Changes the `graphics_context`'s current [font]. + /// + /// This new [font] will be used for subsequent text items. + /// + /// [font]: Font + // Font must always be big-endian + Font(Font), +} + +impl X11Size for TextItem8 { + fn x11_size(&self) -> usize { + match self { + Self::Text(text) => text.x11_size(), + Self::Font(font) => font.x11_size() + u8::X11_SIZE, + } + } +} + +impl Readable for TextItem8 { + fn read_from(buf: &mut impl Buf) -> ReadResult + where + Self: Sized, + { + Ok(match buf.get_u8() { + font_shift if font_shift == 255 => Self::Font(Font::new(buf.get_u32())), + string_len => Self::Text(Box::new(Text8::read_with(buf, &string_len)?)), + }) + } +} + +impl Writable for TextItem8 { + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + match self { + Self::Text(text) => text.write_to(buf)?, + + Self::Font(font) => { + // Font-shift indicator + buf.put_u8(255); + + font.write_to(buf)?; + }, + } + + Ok(()) + } +} + +/// A [text item] that specifies [`String8`] text to be drawn using the +/// `graphics_context`'s current [`font`]. +/// +/// This is used in the [`DrawText8` request]. +/// +/// [text item]: TextItem8 +/// [`font`]: GraphicsOptions::font +/// +/// [`DrawText8` request]: DrawText8 +#[derive(Debug, Hash, PartialEq, Eq)] +pub struct Text8 { + horizontal_offset: Px, + string: String8, +} + +/// An error returned when the given string is too long. +#[derive(Debug, Hash, PartialEq, Eq, Error)] +#[error("the maximum length allowed here is {max}, found {found}")] +pub struct TextTooLong { + /// The maximum length of the string. + pub max: u8, + /// The length of the string that was given. + pub found: usize, +} + +impl Text8 { + /// Creates a new `Text8` with the given `horizontal_offset` and `string`. + /// + /// `horizontal_offset` specifies the offset that is applied to the start of + /// the `string`. + /// + /// # Errors + /// A [`TextTooLong`] error is returned if `string.len() > 255`. + pub fn new(horizontal_offset: Px, string: String8) -> Result { + if string.len() > 255 { + Err(TextTooLong { + max: 255, + found: string.len(), + }) + } else { + Ok(Self { + horizontal_offset, + string, + }) + } + } + + /// The horizontal offset applied to the start of the [`string`]. + /// + /// [`string`]: Text8::string + #[must_use] + pub const fn horizontal_offset(&self) -> Px { + self.horizontal_offset + } + + /// The text which is to be drawn. + #[must_use] + pub const fn string(&self) -> &String8 { + &self.string + } + + /// Unwraps this `Text8`, returning the `horizontal_offset` and `string`. + #[must_use] + #[allow(clippy::missing_const_for_fn, reason = "false positive")] + pub fn unwrap(self) -> (Px, String8) { + (self.horizontal_offset, self.string) + } +} + +impl X11Size for Text8 { + fn x11_size(&self) -> usize { + u8::X11_SIZE + i8::X11_SIZE + self.string.x11_size() + } +} + +impl ReadableWithContext for Text8 { + type Context = u8; + + fn read_with(buf: &mut impl Buf, string_len: &u8) -> ReadResult { + Ok(Self { + horizontal_offset: Px(i8::read_from(buf)?), + string: String8::read_with(buf, &usize::from(*string_len))?, + }) + } +} + +impl Writable for Text8 { + #[allow(clippy::cast_possible_truncation)] + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + buf.put_u8(self.string.len() as u8); + self.horizontal_offset.write_to(buf)?; + self.string.write_to(buf)?; + + Ok(()) + } +} + +/// A [request] that draws the given [`String8`] text on the given [drawable]. +/// +/// # Graphics options used +/// This [request] uses the following [options] of the `graphics_context`: +/// - [`function`] +/// - [`plane_mask`] +/// - [`fill_style`] +/// - [`font`] +/// - [`child_mode`] +/// - [`clip_x`] +/// - [`clip_y`] +/// - [`clip_mask`] +/// +/// This [request] may also use these [options], depending on the configuration +/// of the `graphics_context`: +/// - [`foreground_color`] +/// - [`background_color`] +/// - [`tile`] +/// - [`stipple`] +/// - [`tile_stipple_x`] +/// - [`tile_stipple_y`] +/// +/// # Errors +/// A [`Drawable` error] is generated if `target` does not refer to a defined +/// [window] nor [pixmap]. +/// +/// A [`GraphicsContext` error] is generated if `graphics_context` does not +/// refer to a defined [`GraphicsContext`]. +/// +/// A [`Font` error] is generated if a [font item] given in `text_items` does +/// not refer to a defined [font]. Previous [text items] may have already been +/// drawn. +/// +/// [drawable]: Drawable +/// [window]: Window +/// [pixmap]: Pixmap +/// [font]: Font +/// [font item]: TextItem8::Font +/// [text items]: TextItem8 +/// [options]: GraphicsOptions +/// [request]: Request +/// +/// [`function`]: GraphicsOptions::function +/// [`plane_mask`]: GraphicsOptions::plane_mask +/// [`fill_style`]: GraphicsOptions::fill_style +/// [`font`]: GraphicsOptions::font +/// [`child_mode`]: GraphicsOptions::child_mode +/// [`clip_x`]: GraphicsOptions::clip_x +/// [`clip_y`]: GraphicsOptions::clip_y +/// [`clip_mask`]: GraphicsOptions::clip_mask +/// +/// [`foreground_color`]: GraphicsOptions::foreground_color +/// [`background_color`]: GraphicsOptions::background_color +/// [`tile`]: GraphicsOptions::tile +/// [`stipple`]: GraphicsOptions::stipple +/// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x +/// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y +/// +/// [`Drawable` error]: error::Drawable +/// [`GraphicsContext` error]: error::GraphicsContext +/// [`Font` error]: error::Font +#[doc(alias("PolyText8"))] +#[derive(Debug, Hash, PartialEq, Eq)] +pub struct DrawText8 { + /// The [drawable] on which the text is drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The starting position of the first character of text. + /// + /// These coordinates are relative to the top-left corner of the `target` + /// [drawable]. + /// + /// [drawable]: Drawable + #[doc(alias("x", "y"))] + pub coordinates: Coords, + + /// The [text items] which control the drawing of the text. + /// + /// Each item can either [specify text], in which case that text is drawn + /// (with the provided [`horizontal_offset`] applied), or a [font item], in + /// which case that [font] is stored in the `graphics_context` and used for + /// subsequent text. + /// + /// # Errors + /// A [`Font` error] is generated if a [font item] does not refer to a + /// defined [font]. Previous [text items] may have already been drawn. + /// + /// [specify text]: TextItem8::Text + /// [font item]: TextItem8::Font + /// [`horizontal_offset`]: Text8::horizontal_offset + /// [text items]: TextItem8 + /// + /// [font]: Font + #[doc(alias("items"))] + pub text_items: Vec, +} + +impl Request for DrawText8 { + type OtherErrors = DrawText8Error; + type Reply = (); + + const MAJOR_OPCODE: u8 = 74; + const MINOR_OPCODE: Option = None; +} + +impl X11Size for DrawText8 { + fn x11_size(&self) -> usize { + const HEADER: usize = 4; + + const CONSTANT_SIZES: usize = { + HEADER + + Drawable::X11_SIZE // `target` + + GraphicsContext::X11_SIZE // `graphics_context` + + Coords::X11_SIZE // `coordinates` + }; + + CONSTANT_SIZES + self.text_items.x11_size() + pad(&self.text_items) + } +} + +impl Readable for DrawText8 { + fn read_from(buf: &mut impl Buf) -> ReadResult + where + Self: Sized, + { + /// The maximum number of padding bytes that there could be at the end. + /// + /// As long as there are more than this many bytes remaining, we know + /// that there are still text items left to read. + const MAX_PADDING: usize = 1; + + // major opcode is already read + + // Metabyte position is unused. + buf.advance(1); + + // Read the length and bound buf to not read more than it. + let length = (usize::from(buf.get_u16()) * 4) - 2; + let buf = &mut buf.take(length); + + let target = Drawable::read_from(buf)?; + let graphics_context = GraphicsContext::read_from(buf)?; + let coordinates = Coords::read_from(buf)?; + + let text_items = { + let mut text_items = Vec::new(); + + while buf.remaining() > MAX_PADDING { + text_items.push(TextItem8::read_from(buf)?); + } + + text_items + }; + + // Advance the padding bytes at the end. + buf.advance(pad(&text_items)); + + Ok(Self { + target, + graphics_context, + coordinates, + text_items, + }) + } +} + +impl Writable for DrawText8 { + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + let buf = &mut buf.limit(self.x11_size()); + + buf.put_u8(Self::MAJOR_OPCODE); + // Unused metabyte position. + buf.put_u8(0); + buf.put_u16(self.length()); + + self.target.write_to(buf)?; + self.graphics_context.write_to(buf)?; + self.coordinates.write_to(buf)?; + self.text_items.write_to(buf)?; + + // Unused padding bytes at the end. + buf.put_bytes(0, pad(&self.text_items)); + + Ok(()) + } +} + +request_error! { + #[doc(alias("PolyText16Error"))] + pub enum DrawText16Error for DrawText16 { + Drawable, + Font, + GraphicsContext, + Match, + } +} + +/// A 'text item' specified in a [`DrawText16` request]. +/// +/// [`DrawText16` request]: DrawText16 +#[derive(Debug, Hash, PartialEq, Eq)] +pub enum TextItem16 { + /// Specifies text that is to be drawn with the `graphics_context`'s current + /// [font]. + /// + /// [font]: Font + Text(Box), + + /// Changes the `graphics_context`'s current [font]. + /// + /// This new [font] will be used for subsequent text items. + /// + /// [font]: Font + // Font must always be big-endian + Font(Font), +} + +impl X11Size for TextItem16 { + fn x11_size(&self) -> usize { + match self { + Self::Text(text) => text.x11_size(), + Self::Font(font) => font.x11_size() + u8::X11_SIZE, + } + } +} + +impl Readable for TextItem16 { + fn read_from(buf: &mut impl Buf) -> ReadResult + where + Self: Sized, + { + Ok(match buf.get_u8() { + font_shift if font_shift == 255 => Self::Font(Font::new(buf.get_u32())), + string_len => Self::Text(Box::new(Text16::read_with(buf, &string_len)?)), + }) + } +} + +impl Writable for TextItem16 { + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + match self { + Self::Text(text) => text.write_to(buf)?, + + Self::Font(font) => { + // Font-shift indicator + buf.put_u8(255); + + font.write_to(buf)?; + }, + } + + Ok(()) + } +} + +/// A [text item] that specifies [`String16`] text to be drawn using the +/// `graphics_context`'s current [`font`]. +/// +/// This is used in the [`DrawText16` request]. +/// +/// [text item]: TextItem16 +/// [`font`]: GraphicsOptions::font +/// +/// [`DrawText16` request]: DrawText16 +#[derive(Debug, Hash, PartialEq, Eq)] +pub struct Text16 { + horizontal_offset: Px, + string: String16, +} + +impl Text16 { + /// Creates a new `Text16` with the given `horizontal_offset` and `string`. + /// + /// `horizontal_offset` specifies the offset that is applied to the start of + /// the `string`. + /// + /// # Errors + /// A [`TextTooLong`] error is returned if `string.len() > 255`. + pub fn new(horizontal_offset: Px, string: String16) -> Result { + if string.len() > 255 { + Err(TextTooLong { + max: 255, + found: string.len(), + }) + } else { + Ok(Self { + horizontal_offset, + string, + }) + } + } + + /// The horizontal offset applied to the start of the [`string`]. + /// + /// [`string`]: Text16::string + #[must_use] + pub const fn horizontal_offset(&self) -> Px { + self.horizontal_offset + } + + /// The text which is to be drawn. + #[must_use] + pub const fn string(&self) -> &String16 { + &self.string + } + + /// Unwraps this `Text16`, returning the `horizontal_offset` and `string`. + #[must_use] + #[allow(clippy::missing_const_for_fn, reason = "false positive")] + pub fn unwrap(self) -> (Px, String16) { + (self.horizontal_offset, self.string) + } +} + +impl X11Size for Text16 { + fn x11_size(&self) -> usize { + u8::X11_SIZE + i8::X11_SIZE + self.string.x11_size() + } +} + +impl ReadableWithContext for Text16 { + type Context = u8; + + fn read_with(buf: &mut impl Buf, string_len: &u8) -> ReadResult { + Ok(Self { + horizontal_offset: Px(i8::read_from(buf)?), + string: String16::read_with(buf, &usize::from(*string_len))?, + }) + } +} + +impl Writable for Text16 { + #[allow(clippy::cast_possible_truncation)] + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + buf.put_u8(self.string.len() as u8); + self.horizontal_offset.write_to(buf)?; + self.string.write_to(buf)?; + + Ok(()) + } +} + +/// A [request] that draws the given [`String16`] text on the given [drawable]. +/// +/// For [fonts][font] using linear indexing rather than two-byte matrix +/// indexing, the X server will interpret each [`Char16`] as a `u16` index. +/// +/// # Graphics options used +/// This [request] uses the following [options] of the `graphics_context`: +/// - [`function`] +/// - [`plane_mask`] +/// - [`fill_style`] +/// - [`font`] +/// - [`child_mode`] +/// - [`clip_x`] +/// - [`clip_y`] +/// - [`clip_mask`] +/// +/// This [request] may also use these [options], depending on the configuration +/// of the `graphics_context`: +/// - [`foreground_color`] +/// - [`background_color`] +/// - [`tile`] +/// - [`stipple`] +/// - [`tile_stipple_x`] +/// - [`tile_stipple_y`] +/// +/// # Errors +/// A [`Drawable` error] is generated if `target` does not refer to a defined +/// [window] nor [pixmap]. +/// +/// A [`GraphicsContext` error] is generated if `graphics_context` does not +/// refer to a defined [`GraphicsContext`]. +/// +/// A [`Font` error] is generated if a [font item] given in `text_items` does +/// not refer to a defined [font]. Previous [text items] may have already been +/// drawn. +/// +/// [drawable]: Drawable +/// [window]: Window +/// [pixmap]: Pixmap +/// [font]: Font +/// [font item]: TextItem16::Font +/// [text items]: TextItem16 +/// [options]: GraphicsOptions +/// [request]: Request +/// +/// [`Char16`]: crate::Char16 +/// +/// [`function`]: GraphicsOptions::function +/// [`plane_mask`]: GraphicsOptions::plane_mask +/// [`fill_style`]: GraphicsOptions::fill_style +/// [`font`]: GraphicsOptions::font +/// [`child_mode`]: GraphicsOptions::child_mode +/// [`clip_x`]: GraphicsOptions::clip_x +/// [`clip_y`]: GraphicsOptions::clip_y +/// [`clip_mask`]: GraphicsOptions::clip_mask +/// +/// [`foreground_color`]: GraphicsOptions::foreground_color +/// [`background_color`]: GraphicsOptions::background_color +/// [`tile`]: GraphicsOptions::tile +/// [`stipple`]: GraphicsOptions::stipple +/// [`tile_stipple_x`]: GraphicsOptions::tile_stipple_x +/// [`tile_stipple_y`]: GraphicsOptions::tile_stipple_y +/// +/// [`Drawable` error]: error::Drawable +/// [`GraphicsContext` error]: error::GraphicsContext +/// [`Font` error]: error::Font +#[doc(alias("PolyText16"))] +#[derive(Debug, Hash, PartialEq, Eq)] +pub struct DrawText16 { + /// The [drawable] on which the text is drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The starting position of the first character of text. + /// + /// These coordinates are relative to the top-left corner of the `target` + /// [drawable]. + /// + /// [drawable]: Drawable + #[doc(alias("x", "y"))] + pub coordinates: Coords, + + /// The [text items] which control the drawing of the text. + /// + /// Each item can either [specify text], in which case that text is drawn + /// (with the provided [`horizontal_offset`] applied), or a [font item], in + /// which case that [font] is stored in the `graphics_context` and used for + /// subsequent text. + /// + /// # Errors + /// A [`Font` error] is generated if a [font item] does not refer to a + /// defined [font]. Previous [text items] may have already been drawn. + /// + /// [specify text]: TextItem16::Text + /// [font item]: TextItem16::Font + /// [`horizontal_offset`]: Text16::horizontal_offset + /// [text items]: TextItem16 + /// + /// [font]: Font + #[doc(alias("items"))] + pub text_items: Vec, +} + +impl Request for DrawText16 { + type OtherErrors = DrawText8Error; + type Reply = (); + + const MAJOR_OPCODE: u8 = 75; + const MINOR_OPCODE: Option = None; +} + +impl X11Size for DrawText16 { + fn x11_size(&self) -> usize { + const HEADER: usize = 4; + + const CONSTANT_SIZES: usize = { + HEADER + + Drawable::X11_SIZE // `target` + + GraphicsContext::X11_SIZE // `graphics_context` + + Coords::X11_SIZE // `coordinates` + }; + + CONSTANT_SIZES + self.text_items.x11_size() + pad(&self.text_items) + } +} + +impl Readable for DrawText16 { + fn read_from(buf: &mut impl Buf) -> ReadResult + where + Self: Sized, + { + /// The maximum number of padding bytes that there could be at the end. + /// + /// As long as there are more than this many bytes remaining, we know + /// that there are still text items left to read. + const MAX_PADDING: usize = 1; + + // major opcode is already read + + // Metabyte position is unused. + buf.advance(1); + + // Read the length and bound buf to not read more than it. + let length = (usize::from(buf.get_u16()) * 4) - 2; + let buf = &mut buf.take(length); + + let target = Drawable::read_from(buf)?; + let graphics_context = GraphicsContext::read_from(buf)?; + let coordinates = Coords::read_from(buf)?; + + let text_items = { + let mut text_items = Vec::new(); + + while buf.remaining() > MAX_PADDING { + text_items.push(TextItem16::read_from(buf)?); + } + + text_items + }; + + // Advance the padding bytes at the end. + buf.advance(pad(&text_items)); + + Ok(Self { + target, + graphics_context, + coordinates, + text_items, + }) + } +} + +impl Writable for DrawText16 { + fn write_to(&self, buf: &mut impl BufMut) -> WriteResult { + let buf = &mut buf.limit(self.x11_size()); + + buf.put_u8(Self::MAJOR_OPCODE); + // Unused metabyte position. + buf.put_u8(0); + buf.put_u16(self.length()); + + self.target.write_to(buf)?; + self.graphics_context.write_to(buf)?; + self.coordinates.write_to(buf)?; + self.text_items.write_to(buf)?; + + // Unused padding bytes at the end. + buf.put_bytes(0, pad(&self.text_items)); + + Ok(()) + } +} + +request_error! { + pub enum ImageText8Error for ImageText8 { + Drawable, + GraphicsContext, + Match, + } +} + +derive_xrb! { + /// A [request] that draws [`String8`] text on a rectangular background on + /// the given [drawable]. + /// + /// The text is filled with the `graphics_context`'s [`foreground_color`], + /// while the background is filled with the `graphics_context`'s + /// [`background_color`] + /// + /// In relation to the text extents returned in the + /// [`QueryTextExtents` reply], the background [rectangle] is defined as: + /// ``` + /// # use xrb::{Rectangle, Coords, unit::Px}; + /// # + /// # fn main() -> Result<(), >::Error> { + /// # let coordinates = Coords::new(Px(0), Px(0)); + /// # + /// # let (font_ascent, font_descent) = (Px(0), Px(0)); + /// # let overall_width = Px(1); + /// # + /// Rectangle { + /// x: coordinates.x, + /// y: coordinates.y - font_ascent, + /// width: overall_width, + /// height: (font_ascent + font_descent).map(|height| height as u16), + /// } + /// # ; + /// # + /// # Ok(()) + /// # } + /// ``` + /// + /// `graphics_context`'s [`function`] and [`fill_style`] are ignored in this + /// [request]. Effectively, [`Function::Copy`] and [`FillStyle::Solid`] are + /// used. + /// + /// For [fonts] using two-byte indexing, each [`Char8`] `char` is + /// interpreted as [Char16]::[new](0, char.[unwrap()]). + /// + /// [`Char8`]: crate::Char8 + /// [unwrap()]: crate::Char8::unwrap + /// + /// [Char16]: crate::Char16 + /// [new]: crate::Char16::new + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`plane_mask`] + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`font`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [fonts]: Font + /// [rectangle]: Rectangle + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`Function::Copy`]: crate::set::Function + /// [`FillStyle::Solid`]: crate::set::FillStyle::Solid + /// + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`font`]: GraphicsOptions::font + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`QueryTextExtents` reply]: reply::QueryTextExtents + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct ImageText8: Request(76, ImageText8Error) { + // The length of `string`. + #[metabyte] + #[allow(clippy::cast_possible_truncation)] + let string_len: u8 = string => string.len() as u8, + + /// The [drawable] on which the text is drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The coordinates of the background [rectangle] before the + /// `font_ascent` is subtracted[^subtracted]. + /// + /// [^subtracted]: See the [request docs]. + /// + /// [rectangle]: Rectangle + /// [request docs]: ImageText8 + pub coordinates: Coords, + + /// The text which is to be drawn. + #[context(string_len => usize::from(*string_len))] + pub string: String8, + [_; string => pad(string)], + } +} + +request_error! { + pub enum ImageText16Error for ImageText16 { + Drawable, + GraphicsContext, + Match, + } +} + +derive_xrb! { + /// A [request] that draws [`String16`] text on a rectangular background on + /// the given [drawable]. + /// + /// The text is filled with the `graphics_context`'s [`foreground_color`], + /// while the background is filled with the `graphics_context`'s + /// [`background_color`] + /// + /// In relation to the text extents returned in the + /// [`QueryTextExtents` reply], the background [rectangle] is defined as: + /// ``` + /// # use xrb::{Rectangle, Coords, unit::Px}; + /// # + /// # fn main() -> Result<(), >::Error> { + /// # let coordinates = Coords::new(Px(0), Px(0)); + /// # + /// # let (font_ascent, font_descent) = (Px(0), Px(0)); + /// # let overall_width = Px(1); + /// # + /// Rectangle { + /// x: coordinates.x, + /// y: coordinates.y - font_ascent, + /// width: overall_width, + /// height: (font_ascent + font_descent).map(|height| height as u16), + /// } + /// # ; + /// # + /// # Ok(()) + /// # } + /// ``` + /// + /// `graphics_context`'s [`function`] and [`fill_style`] are ignored in this + /// [request]. Effectively, [`Function::Copy`] and [`FillStyle::Solid`] are + /// used. + /// + /// For [fonts] using linear indexing, each [`Char16`] `char` is interpreted + /// as a big-endian `u16` value. + /// + /// # Graphics options used + /// This [request] uses the following [options] of the `graphics_context`: + /// - [`plane_mask`] + /// - [`foreground_color`] + /// - [`background_color`] + /// - [`font`] + /// - [`child_mode`] + /// - [`clip_x`] + /// - [`clip_y`] + /// - [`clip_mask`] + /// + /// # Errors + /// A [`Drawable` error] is generated if `target` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`GraphicsContext` error] is generated if `graphics_context` does not + /// refer to a defined [`GraphicsContext`]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// [fonts]: Font + /// [rectangle]: Rectangle + /// [options]: GraphicsOptions + /// [request]: Request + /// + /// [`Char16`]: crate::Char16 + /// + /// [`Function::Copy`]: crate::set::Function + /// [`FillStyle::Solid`]: crate::set::FillStyle::Solid + /// + /// [`plane_mask`]: GraphicsOptions::plane_mask + /// [`foreground_color`]: GraphicsOptions::foreground_color + /// [`background_color`]: GraphicsOptions::background_color + /// [`font`]: GraphicsOptions::font + /// [`child_mode`]: GraphicsOptions::child_mode + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// + /// [`QueryTextExtents` reply]: reply::QueryTextExtents + /// + /// [`Drawable` error]: error::Drawable + /// [`GraphicsContext` error]: error::GraphicsContext + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct ImageText16: Request(77, ImageText16Error) { + // The length of `string`. + #[metabyte] + #[allow(clippy::cast_possible_truncation)] + let string_len: u8 = string => string.len() as u8, + + /// The [drawable] on which the text is drawn. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + #[doc(alias("drawable"))] + pub target: Drawable, + + /// The [`GraphicsContext`] used in this graphics operation. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "context", "gcontext"))] + pub graphics_context: GraphicsContext, + + /// The coordinates of the background [rectangle] before the + /// `font_ascent` is subtracted[^subtracted]. + /// + /// [^subtracted]: See the [request docs]. + /// + /// [rectangle]: Rectangle + /// [request docs]: ImageText16 + pub coordinates: Coords, + + /// The text which is to be drawn. + #[context(string_len => usize::from(*string_len))] + pub string: String16, + [_; string => pad(string)], + } +} diff --git a/src/x11/request/graphics/config.rs b/src/x11/request/graphics/config.rs new file mode 100644 index 00000000..93115616 --- /dev/null +++ b/src/x11/request/graphics/config.rs @@ -0,0 +1,1185 @@ +// 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 [core X11 protocol] that relate to the +//! configuration and creation of graphics-related types. +//! +//! [Requests] are messages sent from an X client to the X server. +//! +//! [Requests]: Request +//! [core X11 protocol]: crate::x11 + +extern crate self as xrb; + +use xrbk::{pad, ConstantX11Size}; +use xrbk_macro::{derive_xrb, Readable, Writable, X11Size}; + +use crate::{ + message::Request, + set::{GraphicsOptions, GraphicsOptionsMask}, + unit::Px, + visual::RgbColor, + x11::{error, reply}, + CursorAppearance, + Dimensions, + Drawable, + Font, + GraphicsContext, + Pixmap, + Rectangle, +}; + +macro_rules! request_error { + ( + $(#[$meta:meta])* + $vis:vis enum $Name:ident for $Request:ty { + $($($Error:ident),+$(,)?)? + } + ) => { + #[doc = concat!( + "An [error](crate::message::Error) generated because of a failed [`", + stringify!($Request), + "` request](", + stringify!($Request), + ")." + )] + #[doc = ""] + $(#[$meta])* + $vis enum $Name { + $($( + #[doc = concat!( + "A [`", + stringify!($Error), + "` error](error::", + stringify!($Error), + ")." + )] + $Error(error::$Error) + ),+)? + } + }; +} + +request_error! { + pub enum CreatePixmapError for CreatePixmap { + Drawable, + ResourceIdChoice, + Value, + } +} + +derive_xrb! { + /// A [request] that creates a new [pixmap] and assigns the provided + /// [`Pixmap` ID][pixmap] to it. + /// + /// The initial contents of the [pixmap] are undefined. + /// + /// # Errors + /// A [`Value` error] is generated if `depth` is not a depth supported by + /// the `drawable`'s root [window]. + /// + /// A [`ResourceIdChoice` error] is generated if `pixmap_id` specifies an ID + /// already used for another resource, or an ID not allocated to your + /// client. + /// + /// A [`Drawable` error] is generated if `drawable` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [window]: Window + /// [pixmap]: Pixmap + /// [request]: Request + /// + /// [`Drawable` error]: error::Drawable + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + /// [`Value` error]: error::Value + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct CreatePixmap: Request(53, CreatePixmapError) { + /// The depth of the [pixmap]. + /// + /// # Errors + /// A [`Value` error] is generated if this depth is not supported by the + /// root [window] of the `drawable`. + /// + /// [pixmap]: Pixmap + /// [window]: Window + /// + /// [`Value` error]: error::Value + #[metabyte] + pub depth: u8, + + /// The [`Pixmap` ID][pixmap] which is to be assigned to the [pixmap]. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if this resource ID is + /// already used or if it isn't allocated to your client. + /// + /// [pixmap]: Pixmap + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + #[doc(alias = "pid")] + pub pixmap_id: Pixmap, + // TODO: what is this for?? + /// It is legal to use an [`InputOnly`] [window] as a [drawable] in this + /// [request]. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [window]: Window + /// [pixmap]: Pixmap + /// [drawable]: Drawable + /// [request]: Request + /// + /// [`InputOnly`]: crate::WindowClass::InputOnly + /// + /// [`Drawable` error]: error::Drawable + pub drawable: Drawable, + + /// The width of the [pixmap]. + /// + /// [pixmap]: Pixmap + pub width: Px, + /// The height of the [pixmap]. + /// + /// [pixmap]: Pixmap + pub height: Px, + } + + /// A [request] that removes the association between a given + /// [`Pixmap` ID][pixmap] and the [pixmap] it is associated with. + /// + /// The stored [pixmap] will be freed when it is no longer referenced by any + /// other resource. + /// + /// # Errors + /// A [`Pixmap` error] is generated if `target` does not refer to a defined + /// [pixmap]. + /// + /// [pixmap]: Pixmap + /// [request]: Request + /// + /// [`Pixmap` error]: error::Pixmap + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct FreePixmap: Request(54, error::Pixmap) { + /// The [pixmap] which is to have its association with its ID removed. + /// + /// # Errors + /// A [`Pixmap` error] is generated if this does not refer to a defined + /// [pixmap]. + /// + /// [pixmap]: Pixmap + /// + /// [`Pixmap` error]: error::Pixmap + #[doc(alias = "pixmap")] + pub target: Pixmap, + } +} + +request_error! { + pub enum CreateGraphicsContextError for CreateGraphicsContext { + Drawable, + Font, + ResourceIdChoice, + Match, + Pixmap, + Value, + } +} + +derive_xrb! { + /// A [request] that creates a new [`GraphicsContext`] and assigns the + /// provided [`GraphicsContext` ID] to it. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if `graphics_context_id` + /// specifies an ID already used for another resource, or an ID which is not + /// allocated to your client. + /// + /// A [`Drawable` error] is generated if `drawable` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [pixmap]: Pixmap + /// [window]: Window + /// [request]: Request + /// + /// [`GraphicsContext` ID]: GraphicsContext + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + /// [`Drawable` error]: error::Drawable + #[doc(alias("CreateGc", "CreateGC", "CreateGcontext", "CreateGContext"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct CreateGraphicsContext: Request(55, CreateGraphicsContextError) { + /// The [`GraphicsContext` ID] which is to be assigned to the + /// [`GraphicsContext`]. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if this resource ID is + /// already used or if it isn't allocated to your client. + /// + /// [`GraphicsContext` ID]: GraphicsContext + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + #[doc(alias("cid", "gid", "gcid", "context_id"))] + pub graphics_context_id: GraphicsContext, + + /// > *TODO*\ + /// > ***We don't yet understand what this field is for. If you have any + /// > ideas, please feel free to open an issue or a discussion on the + /// > [GitHub repo]!*** + /// > + /// > [GitHub repo]: https://github.com/XdotRS/xrb/ + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// [drawable]: Drawable + /// [window]: Window + /// [pixmap]: Pixmap + /// + /// [`Drawable` error]: error::Drawable + pub drawable: Drawable, + + /// The [graphics options] used in graphics operations when this + /// [`GraphicsContext`] is provided. + /// + /// These [graphics options] may be later configured through the + /// [`SetDashes` request], [`SetClipRectangles` request], and the + /// [`ChangeGraphicsOptions` request]. + /// + /// [graphics options]: GraphicsOptions + /// + /// [`SetDashes` request]: SetDashes + /// [`SetClipRectangles` request]: SetClipRectangles + /// [`ChangeGraphicsOptions` request]: ChangeGraphicsOptions + #[doc(alias("values", "value_mask", "value_list"))] + #[doc(alias("options", "option_mask", "option_list"))] + #[doc(alias("graphics_option_mask", "graphics_option_list"))] + pub graphics_options: GraphicsOptions, + } +} + +request_error! { + pub enum ChangeGraphicsOptionsError for ChangeGraphicsOptions { + Font, + GraphicsContext, + Match, + Pixmap, + Value, + } +} + +derive_xrb! { + /// A [request] that changes the [graphics options] configured in a + /// [`GraphicsContext`]. + /// + /// Changing the [`clip_mask`] overrides any [`SetClipRectangles` request] + /// on the [`GraphicsContext`]. + /// + /// Changing [`dash_offset`] or [`dashes`] overrides any + /// [`SetDashes` request] on the [`GraphicsContext`]. + /// + /// The [`SetDashes` request] allows an alternating pattern of dashes to be + /// configured, while configuring [`dashes`] with this [request] does not. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if `target` does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [request]: Request + /// + /// [graphics options]: GraphicsOptions + /// + /// [`clip_mask`]: GraphicsOptions::clip_mask + /// [`dashes`]: GraphicsOptions::dashes + /// [`dash_offset`]: GraphicsOptions::dash_offset + /// + /// [`SetClipRectangles` request]: SetClipRectangles + /// [`SetDashes` request]: SetDashes + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("ChangeGc", "ChangeGC", "ChangeGraphicsContext"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct ChangeGraphicsOptions: Request(56, ChangeGraphicsOptionsError) { + /// The [`GraphicsContext`] for which this [request] changes its + /// [graphics options]. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [graphics options]: GraphicsOptions + /// [request]: Request + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "graphics_context", "context"))] + pub target: GraphicsContext, + + /// The changes which are made to the `target`'s [graphics options]. + /// + /// [graphics options]: GraphicsOptions + #[doc(alias("values", "value_mask", "value_list"))] + #[doc(alias("options", "option_mask", "option_list"))] + #[doc(alias("graphics_option_mask", "graphics_option_list"))] + pub changed_options: GraphicsOptions, + } +} + +request_error! { + pub enum CopyGraphicsOptionsError for CopyGraphicsOptions { + GraphicsContext, + Match, + Value, + } +} + +derive_xrb! { + /// A [request] that copies the specified [graphics options] from the + /// `source` [`GraphicsContext`] into the `destination` [`GraphicsContext`]. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if either the `source` or the + /// `destination` do not refer to defined [`GraphicsContext`s]. + /// + /// A [`Match` error] is generated if the `source` and the `destination` do + /// not have the same root [window] and depth. + /// + /// [graphics options]: GraphicsOptions + /// [window]: Window + /// [request]: Request + /// + /// [`GraphicsContext`s]: GraphicsContext + /// + /// [`GraphicsContext` error]: error::GraphicsContext + /// [`Match` error]: error::Match + #[doc(alias("CopyGc", "CopyGC", "CopyGraphicsContext", "CopyGcontext"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct CopyGraphicsOptions: Request(57, CopyGraphicsOptionsError) { + /// The [`GraphicsContext`] from which the [options] specified in + /// `options_mask` are copied. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// A [`Match` error] is generated if this does not have the same root + /// [window] and depth as the `destination`. + /// + /// [options]: GraphicsOptions + /// [window]: Window + /// + /// [`GraphicsContext` error]: error::GraphicsContext + /// [`Match` error]: error::Match + #[doc(alias("src_gc", "source_graphics_context", "src"))] + pub source: GraphicsContext, + /// The [`GraphicsContext`] into which the [options] specified in + /// `options_mask` are copied from the `source`. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// A [`Match` error] is generated if this does not have the same root + /// [window] and depth as the `source`. + /// + /// [options]: GraphicsOptions + /// [window]: Window + /// + /// [`GraphicsContext` error]: error::GraphicsContext + /// [`Match` error]: error::Match + #[doc(alias("dst_gc", "destination_graphics_context", "dst"))] + pub destination: GraphicsContext, + + /// A mask that specifies which options are copied from the `source` + /// into the `destination`. + #[doc(alias("value_mask"))] + pub options_mask: GraphicsOptionsMask, + } +} + +request_error! { + pub enum SetDashesError for SetDashes { + GraphicsContext, + Value, + } +} + +derive_xrb! { + /// A [request] that sets the [`dash_offset`] and the pattern of dashes on a + /// [`GraphicsContext`]. + /// + /// Configuring [`dashes`] or [`dash_offset`] with a + /// [`ChangeGraphicsOptions` request] overrides the effects of this + /// [request]. + /// + /// Configuring [`dashes`] with a [`ChangeGraphicsOptions` request] does not + /// allow an alternating pattern of dashes to be specified; this [request] + /// does. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if `target` does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [request]: Request + /// + /// [`dashes`]: GraphicsOptions::dashes + /// [`dash_offset`]: GraphicsOptions::dash_offset + /// + /// [`ChangeGraphicsOptions` request]: ChangeGraphicsOptions + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct SetDashes: Request(58, SetDashesError) { + /// The [`GraphicsContext`] on which this [request] configures its + /// dashes. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [request]: Request + pub target: GraphicsContext, + + /// The offset from the endpoints or joinpoints of a dashed line before + /// the dashes are drawn. + pub dash_offset: Px, + + // The length of `dashes`. + #[allow(clippy::cast_possible_truncation)] + let dashes_len: u16 = dashes => dashes.len() as u16, + /// The pattern of dashes used when drawing dashed lines. + /// + /// Each element represents the length of a dash in the pattern, + /// measured in pixels. A `dashes` list of odd length is appended to + /// itself to produce a list of even length. + #[context(dashes_len => usize::from(*dashes_len))] + pub dashes: Vec>, + [_; dashes => pad(dashes)], + } +} + +request_error! { + pub enum SetClipRectanglesError for SetClipRectangles { + GraphicsContext, + Match, + Value, + } +} + +/// Specifies the ordering of [rectangles] given by `clip_rectangles` in a +/// [`SetClipRectangles` request]. +/// +/// [rectangles]: Rectangle +/// +/// [`SetClipRectangles` request]: SetClipRectangles +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum ClipRectanglesOrdering { + /// No particular order is specified. + /// + /// The [rectangles] given by `clip_rectangles` in a + /// [`SetClipRectangles` request] are given in no particular order. + /// + /// [rectangles]: Rectangle + /// + /// [`SetClipRectangles` request]: SetClipRectangles + Unsorted, + + /// [Rectangles][rectangles] are ordered by their y coordinate. + /// + /// The [rectangles] given by `clip_rectangles` in a + /// [`SetClipRectangles` request] are sorted by their y coordinate from low + /// to high. + /// + /// The ordering among [rectangles] with equal y coordinates is not + /// specified. + /// + /// [rectangles]: Rectangle + /// + /// [`SetClipRectangles` request]: SetClipRectangles + SortedByY, + + /// [Rectangles][rectangles] are ordered primarily by their y coordinate, + /// and secondarily by their x coordinate. + /// + /// The [rectangles] given by `clip_rectangles` in a + /// [`SetClipRectangles` request] are sorted by their y coordinate from low + /// to high, and those of equal y are sorted by their x coordinate from low + /// to high. + /// + /// [rectangles]: Rectangle + /// + /// [`SetClipRectangles` request]: SetClipRectangles + SortedByYx, + + /// [Rectangles][rectangles] are ordered primarily by their y coordinate, + /// secondarily by their x coordinate, and each one which intersects a given + /// y coordinate has an equal y coordinate and height. + /// + /// The [rectangles] given by `clip_rectangles` in a + /// [`SetClipRectangles` request] are sorted by their y coordinate from low + /// to high, those of equal y are sorted by their x coordinate from low to + /// high, and every [rectangle][rectangles] which intersects a given y + /// coordinate is guaranteed to have the same y coordinate and height as + /// every other intersecting [rectangle][rectangles]. + /// + /// [rectangles]: Rectangle + /// + /// [`SetClipRectangles` request]: SetClipRectangles + BandedByYx, +} + +derive_xrb! { + /// A [request] that configures the clip mask of a [`GraphicsContext`] using + /// a list of [rectangles]. + /// + /// This [request] also sets the [`clip_x`] and [`clip_y`] of the clip mask. + /// The coordinates used in the [rectangles] are relative to the [`clip_x`] + /// and [`clip_y`]. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if `target` does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [rectangles]: Rectangle + /// [request]: Request + /// + /// [`clip_x`]: GraphicsOptions::clip_x + /// [`clip_y`]: GraphicsOptions::clip_y + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct SetClipRectangles: Request(59, SetClipRectanglesError) { + /// Specifies the ordering of [rectangles] within `clip_rectangles`. + /// + /// See [`ClipRectanglesOrdering`] for more information. + /// + /// [rectangles]: Rectangle + #[metabyte] + pub ordering: ClipRectanglesOrdering, + + /// The [`GraphicsContext`] on which this [request] configures its clip + /// mask. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [request]: Request + /// + /// [`GraphicsContext` error]: error::GraphicsContext + pub target: GraphicsContext, + + /// The x coordinate of the top-left corner of the clip mask. + /// + /// This is relative to the top-left corner of the destination + /// [drawable] used in a particular graphics operation. + /// + /// The coordinates used in the [rectangles] in `clip_rectangles` are + /// relative to this x coordinate. + /// + /// [drawable]: Drawable + /// [rectangles]: Rectangle + pub clip_x: Px, + /// The y coordinate of the top-left corner of the clip mask. + /// + /// This is relative to the top-left corner of the destination + /// [drawable] used in a particular graphics operation. + /// + /// The coordinates used in the [rectangles] in `clip_rectangles` are + /// relative to this y coordinate. + /// + /// [drawable]: Drawable + /// [rectangles]: Rectangle + pub clip_y: Px, + + /// A list of non-overlapping [rectangles] that are used to mask the + /// effects of a graphics operation. + /// + /// These [rectangles] specify the areas within which the effects of a + /// graphics operation are applied. + /// + /// If this list is empty, graphics operations will have no graphical + /// effect. + /// + /// [rectangles]: Rectangle + #[context(self::remaining => remaining / Rectangle::X11_SIZE)] + pub clip_rectangles: Vec, + } + + /// A [request] that deletes the given [`GraphicsContext`]. + /// + /// The association between the [`GraphicsContext` ID] and the + /// [`GraphicsContext`] itself is removed in the process. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if `target` does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [request]: Request + /// + /// [`GraphicsContext` ID]: GraphicsContext + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("FreeGc", "FreeGcontext", "FreeGraphicsContext"))] + #[doc(alias("DestroyGc", "DestroyGcontext"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct DestroyGraphicsContext: Request(60, error::GraphicsContext) { + /// The [`GraphicsContext`] which is to be deleted. + /// + /// # Errors + /// A [`GraphicsContext` error] is generated if this does not refer to a + /// defined [`GraphicsContext`]. + /// + /// [`GraphicsContext` error]: error::GraphicsContext + #[doc(alias("gc", "graphics_context", "context", "gcontext"))] + pub target: GraphicsContext, + } +} + +request_error! { + #[doc(alias("CreateCursorError"))] + pub enum CreateCursorAppearanceError for CreateCursorAppearance { + ResourceIdChoice, + Match, + Pixmap, + } +} + +derive_xrb! { + /// A [request] that creates a new [`CursorAppearance`]. + /// + /// The given `cursor_appearance_id` is assigned to the + /// [`CursorAppearance`] that is created. + /// + /// The options provided in this [request] may be arbitrarily transformed by + /// the X server to meet display limitations. + /// + /// The effect of changes to the `source` or `mask` [pixmaps][pixmap] made + /// after the creation of this [`CursorAppearance`] have an undefined + /// effect: the X server may or may not create a copy of the provided + /// [pixmaps][pixmap]. + /// + /// # Errors + /// An [`Alloc` error] is generated if the X server fails to allocate the + /// [`CursorAppearance`]. + /// + /// A [`ResourceIdChoice` error] is generated if `cursor_appearance_id` + /// specifies an ID already used for another resource, or an ID not + /// allocated to your client. + /// + /// A [`Match` error] is generated if `source`'s depth is not `1`. + /// + /// A [`Match` error] is generated if `mask` is [`Some`] and `mask`'s depth + /// is not `1`. + /// + /// A [`Match` error] is generated if `mask` is [`Some`] and `mask` is not + /// the same size as `source`. + /// + /// A [`Match` error] is generated if `hotspot_x` is not contained within + /// the `source` [pixmap]. + /// + /// A [`Match` error] is generated if `hotspot_y` is not contained within + /// the `source` [pixmap]. + /// + /// A [`Pixmap` error] is generated if `source` does not refer to a defined + /// [pixmap]. + /// + /// A [`Pixmap` error] is generated if `mask` is [`Some`] and does not refer + /// to a defined [pixmap]. + /// + /// [pixmap]: Pixmap + /// [request]: Request + /// + /// [`Alloc` error]: error::Alloc + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + /// [`Match` error]: error::Match + /// [`Pixmap` error]: error::Pixmap + #[doc(alias("CreateCursor"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct CreateCursorAppearance: Request(93, CreateCursorAppearanceError) { + /// The [`CursorAppearance` ID] which is to be assigned to the + /// [`CursorAppearance`]. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if this resource ID is + /// already used or if it isn't allocated to your client. + /// + /// [`CursorAppearance` ID]: CursorAppearance + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + #[doc(alias("cid", "cursor_id", "caid"))] + pub cursor_appearance_id: CursorAppearance, + + /// The [pixmap] that specifies the appearance of the cursor. + /// + /// For each bit set to `1` in this [pixmap], the [`foreground_color`] + /// is used. For each bit set to `0`, the [`background_color`] is used. + /// + /// # Errors + /// A [`Pixmap` error] is generated if this does not refer to a defined + /// [pixmap]. + /// + /// A [`Match` error] is generated if this [pixmap] does not have a + /// depth of `1`. + /// + /// [pixmap]: Pixmap + /// + /// [`foreground_color`]: CreateCursorAppearance::foreground_color + /// [`background_color`]: CreateCursorAppearance::background_color + /// + /// [`Pixmap` error]: error::Pixmap + pub source: Pixmap, + /// An optional [pixmap] that applies a mask to the cursor's appearance. + /// + /// If this is [`Some`], it masks the `source` [pixmap]. For each bit + /// set to `1`, the corresponding pixel in the `source` is shown. For + /// each bit set to `0`, the corresponding pixel in the `source` is + /// hidden. + /// + /// # Errors + /// A [`Pixmap` error] is generated if this is [`Some`] but does not + /// refer to a defined [pixmap]. + /// + /// A [`Match` error] is generated if this is [`Some`] but does not have + /// the same size as the `source` [pixmap]. + /// + /// A [`Match` error] is generated if this is [`Some`] but does not have + /// a depth of `1`. + /// + /// [pixmap]: Pixmap + /// + /// [`Pixmap` error]: error::Pixmap + /// [`Match` error]: error::Match + pub mask: Option, + + /// The foreground color used for the cursor's visual appearance. + /// + /// This foreground color is used for each bit set to `1` in the + /// `source` [pixmap]. + /// + /// [pixmap]: Pixmap + #[doc(alias("fore_red", "fore_green", "fore_blue"))] + #[doc(alias("foreground_red", "foreground_green", "foreground_blue"))] + #[doc(alias("fore_color"))] + pub foreground_color: RgbColor, + /// The background color used for the cursor's visual appearance. + /// + /// This background color is used for each bit set to `0` in the + /// `source` [pixmap]. + /// + /// [pixmap]: Pixmap + #[doc(alias("back_red", "back_green", "back_blue"))] + #[doc(alias("background_red", "background_green", "background_blue"))] + #[doc(alias("back_color"))] + pub background_color: RgbColor, + + /// The x coordinate of the cursor's 'hotspot'. + /// + /// This coordinate is relative to the top-left corner of the `source` + /// [pixmap]. + /// + /// The hotspot refers to the point within the cursor's appearance which + /// is placed directly over the coordinates of the cursor. For example, + /// for a normal arrow cursor, that will be the tip of the arrow. For a + /// pen cursor, that will be the tip of the pen. + /// + /// # Errors + /// A [`Match` error] is generated if this coordinate is not within the + /// `source` [pixmap] (i.e. it is greater than or equal to its width). + /// + /// [pixmap]: Pixmap + /// + /// [`Match` error]: error::Match + #[doc(alias("x"))] + pub hotspot_x: Px, + /// The y coordinate of the cursor's 'hotspot'. + /// + /// This coordinate is relative to the top-left corner of the `source` + /// [pixmap]. + /// + /// The hotspot refers to the point within the cursor's appearance which + /// is placed directly over the coordinates of the cursor. For example, + /// for a normal arrow cursor, that will be the tip of the arrow. For a + /// pen cursor, that will be the tip of the pen. + /// + /// # Errors + /// A [`Match` error] is generated if this coordinate is not within the + /// `source` [pixmap] (i.e. it is greater than or equal to its height). + /// + /// [pixmap]: Pixmap + /// + /// [`Match` error]: error::Match + #[doc(alias("y"))] + pub hotspot_y: Px, + } +} + +request_error! { + #[doc(alias("CreateGlyphCursorError"))] + pub enum CreateGlyphCursorAppearanceError for CreateGlyphCursorAppearance { + Font, + ResourceIdChoice, + Value, + } +} + +derive_xrb! { + /// A [request] that creates a new [`CursorAppearance`] using the specified + /// glyphs. + /// + /// The given `cursor_appearance_id` is assigned to the + /// [`CursorAppearance`] that is created. + /// + /// The hotspot (that is, the point that is aligned to the exact coordinates + /// of the cursor: for a typical arrow cursor, that's the tip of the arrow) + /// is the top-left corner of the `source_char`. + /// + /// The options provided in this [request] may be arbitrarily transformed by + /// the X server to meet display limitations. + /// + /// # Errors + /// An [`Alloc` error] is generated if the X server fails to allocate the + /// [`CursorAppearance`]; see [`RequestError::Alloc`]. + /// + /// A [`ResourceIdChoice` error] is generated if `cursor_appearance_id` + /// specifies an ID already used for another resource, or an ID not + /// allocated to your client. + /// + /// A [`Font` error] is generated if `source_font` does not refer to a + /// defined [font]. + /// + /// A [`Font` error] is generated if `mask_font` is [`Some`] and `mask_font` + /// does not refer to a defined [font]. + /// + /// A [`Value` error] is generated if `source_char` does not refer to a + /// glyph defined in the [font] specified by `source_font`. + /// + /// A [`Value` error] is generated if `mask_font` is [`Some`] but + /// `mask_char` is [`None`]. + /// + /// A [`Value` error] is generated if both `mask_font` and `mask_char` are + /// [`Some`] but `mask_char` does not refer to a glyph defined in the font + /// specified by `mask_font`. + /// + /// [font]: Font + /// [request]: Request + /// + /// [`RequestError::Alloc`]: crate::message::RequestError::Alloc + /// [`Alloc` error]: error::Alloc + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + /// [`Font` error]: error::Font + /// [`Value` error]: error::Value + #[doc(alias("CreateGlyphCursor"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct CreateGlyphCursorAppearance: Request(94, CreateGlyphCursorAppearanceError) { + /// The [`CursorAppearance` ID] which is to be assigned to the + /// [`CursorAppearance`]. + /// + /// # Errors + /// A [`ResourceIdChoice` error] is generated if this resource ID is + /// already used or if it isn't allocated to your client. + /// + /// [`CursorAppearance` ID]: CursorAppearance + /// + /// [`ResourceIdChoice` error]: error::ResourceIdChoice + #[doc(alias("cid", "cursor_id", "caid"))] + pub cursor_appearance_id: CursorAppearance, + + /// The [font] that is used for the [`source_char`]. + /// + /// # Errors + /// A [`Font` error] is generated if this does not refer to a defined + /// [font]. + /// + /// [`source_char`]: CreateGlyphCursorAppearance::source_char + /// + /// [font]: Font + /// + /// [`Font` error]: error::Font + pub source_font: Font, + /// The [font] that is used for the [`mask_char`]. + /// + /// If this is specified (is [`Some`]), the [`mask_char`] must be + /// specified too. + /// + /// # Errors + /// A [`Font` error] is generated if this is [`Some`] but does not refer + /// to a defined [font]. + /// + /// [`mask_char`]: CreateGlyphCursorAppearance::mask_char + /// + /// [font]: Font + /// + /// [`Font` error]: error::Font + pub mask_font: Option, + + /// The character used as the appearance of the cursor. + /// + /// This character is displayed in the [font] specified by + /// [`source_font`]. + /// + /// For [fonts][font] that use two-byte matrix indexing, this value + /// should be specified like so: + /// ``` + /// use xrb::Char16; + /// + /// # let (byte1, byte2) = (0, 0); + /// # + /// let char = Char16::new(byte1, byte2); + /// + /// let source_char = u16::from(char); + /// ``` + /// + /// # Errors + /// A [`Value` error] is generated if this does not refer to a glyph + /// defined in the [font] specified by [`source_font`]. + /// + /// [`source_font`]: CreateGlyphCursorAppearance::source_font + /// + /// [font]: Font + /// + /// [`Value` error]: error::Value + pub source_char: u16, + /// An optional character which masks the appearance of the cursor. + /// + /// If this is [`Some`], it masks the character specified by + /// [`source_char`]. + /// + /// For [fonts][font] that use two-byte matrix indexing, this value + /// should be specified like so: + /// ``` + /// use xrb::Char16; + /// + /// # let (byte1, byte2) = (0, 0); + /// # + /// let char = Char16::new(byte1, byte2); + /// + /// let mask_char = Some(u16::from(char)); + /// ``` + /// + /// # Errors + /// A [`Value` error] is generated if [`mask_font`] is [`Some`] but this + /// is [`None`]. + /// + /// A [`Value` error] is generated if this does not refer to a glyph + /// defined in the [font] specified by [`mask_font`]. + /// + /// [`mask_font`]: CreateGlyphCursorAppearance::mask_font + /// + /// [font]: Font + /// + /// [`Value` error]: error::Value + pub mask_char: Option, + + /// The foreground color used for the cursor's visual appearance. + /// + /// This foreground color is used for the [`source_char`]. + /// + /// [`source_char`]: CreateGlyphCursorAppearance::source_char + pub foreground_color: RgbColor, + /// The background color used for the cursor's visual appearance. + /// + /// This background color is used for areas of the cursor's visual + /// appearance which are not the [`source_char`]. + /// + /// [`source_char`]: CreateGlyphCursorAppearance::source_char + pub background_color: RgbColor, + } + + /// A [request] that deletes the association between the given + /// [`CursorAppearance` ID] and the [`CursorAppearance`] it refers to. + /// + /// The [`CursorAppearance`] will be deleted once no resources reference it + /// any longer. + /// + /// # Errors + /// A [`CursorAppearance` error] is generated if `target` does not refer to + /// a defined [`CursorAppearance`]. + /// + /// [request]: Request + /// + /// [`CursorAppearance` ID]: CursorAppearance + /// + /// [`CursorAppearance` error]: error::CursorAppearance + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct DestroyCursorAppearance: Request(95, error::CursorAppearance) { + /// The [`CursorAppearance`] that is to be deleted. + /// + /// # Errors + /// A [`CursorAppearance` error] is generated if this does not refer to + /// a defined [`CursorAppearance`]. + /// + /// [`CursorAppearance` error]: error::CursorAppearance + #[doc(alias("cursor", "cursor_appearance"))] + pub target: CursorAppearance, + } + + /// A [request] that changes the foreground and background colors of a + /// [`CursorAppearance`]. + /// + /// If the [`CursorAppearance`] is currently being displayed, this change + /// will be immediately visible. + /// + /// # Errors + /// A [`CursorAppearance` error] is generated if `target` does not refer to + /// a defined [`CursorAppearance`]. + /// + /// [request]: Request + /// + /// [`CursorAppearance` error]: error::CursorAppearance + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct RecolorCursorAppearance: Request(96, error::CursorAppearance) { + /// The [`CursorAppearance`] which is to be recolored. + /// + /// # Errors + /// A [`CursorAppearance` error] is generated if this does not refer to + /// a defined [`CursorAppearance`]. + /// + /// [`CursorAppearance` error]: error::CursorAppearance + #[doc(alias("cursor", "cursor_appearance"))] + pub target: CursorAppearance, + + /// The new foreground color for the [`CursorAppearance`]. + /// + /// This foreground color is used for each bit set to `1` in the + /// `target` [`CursorAppearance`]'s `source` [pixmap]. + /// + /// If the `target`'s `source` is a character, then this foreground + /// color is used for that character. + /// + /// [pixmap]: Pixmap + pub foreground_color: RgbColor, + /// The new background color for the [`CursorAppearance`]. + /// + /// This background color is used for each bit set to `0` in the + /// `target` [`CursorAppearance`]'s `source` [pixmap]. + /// + /// If the `target`'s `source` is a character, then this background + /// color is used for the parts of the `target`'s `source` which is not + /// the character. + /// + /// [pixmap]: Pixmap + pub background_color: RgbColor, + } +} + +request_error! { + #[doc(alias("QueryBestSizeError"))] + pub enum QueryIdealDimensionsError for QueryIdealDimensions { + Drawable, + Match, + Value, + } +} + +/// Specifies how the ideal [dimensions] should be chosen in a +/// [`QueryIdealDimensions` request]. +/// +/// [dimensions]: Dimensions +/// +/// [`QueryIdealDimension` request]: QueryIdealDimensions +#[doc(alias("QueryBestSizeClass", "QueryIdealDimensionsClass"))] +#[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] +pub enum DimensionClass { + /// The largest [`CursorAppearance`] [dimensions] that can be fully + /// displayed are returned. + /// + /// [dimensions]: Dimensions + CursorAppearance, + + /// The [dimensions] which can be tiled fastest are returned. + /// + /// See [`GraphicsOptions::tile`] for more information on tiling. + /// + /// [dimensions]: Dimensions + Tile, + /// The [dimensions] which can be stippled fastest are returned. + /// + /// See [`GraphicsOptions::stipple`] for more information on stippling. + /// + /// [dimensions]: Dimensions + Stipple, +} + +derive_xrb! { + /// A [request] that returns the ideal [dimensions] for the given + /// [`DimensionClass`] and baseline [dimensions]. + /// + /// For [`DimensionClass::CursorAppearance`], the largest + /// [`CursorAppearance`] [dimensions] that can be fully displayed are + /// returned. + /// + /// For [`DimensionClass::Tile`], the [dimensions] that can be tiled + /// fastest are returned. + /// + /// For [`DimensionClass::Stipple`], the [dimensions] that can be stippled + /// fastest are returned. + /// + /// # Errors + /// A [`Drawable` error] is generated if `drawable` does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`Match` error] is generated if `class` is [`Tile`] or [`Stipple`] and + /// `drawable` is an [`InputOnly`] [window]. + /// + /// [window]: crate::Window + /// [pixmap]: Pixmap + /// [dimensions]: Dimensions + /// [request]: Request + /// + /// [`Tile`]: DimensionClass::Tile + /// [`Stipple`]: DimensionClass::Stipple + /// + /// [`InputOnly`]: crate::WindowClass::InputOnly + /// + /// [`Drawable` error]: error::Drawable + /// [`Match` error]: error::Match + #[doc(alias("QueryBestSize"))] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable)] + pub struct QueryIdealDimensions: Request( + 97, + QueryIdealDimensionsError, + ) -> reply::QueryIdealDimensions { + /// The [class] of ideal [dimensions] requested. + /// + /// [class]: DimensionClass + /// [dimensions]: Dimensions + #[metabyte] + pub class: DimensionClass, + + /// Indicates the desired [screen] and possibly the depth as well. + /// + /// For [`DimensionClass::CursorAppearance`], this only indicates the + /// desired [screen]. + /// + /// For [`DimensionClass::Tile`] or [`DimensionClass::Stipple`], this + /// possibly indicates the depth as well. + /// + /// # Errors + /// A [`Drawable` error] is generated if this does not refer to a + /// defined [window] nor [pixmap]. + /// + /// A [`Match` error] is generated if an [`InputOnly`] [window] is used + /// for [`DimensionClass::Tile`] or [`DimensionClass::Stipple`]. + /// + /// [window]: crate::Window + /// [pixmap]: Pixmap + /// [screen]: crate::visual::Screen + /// + /// [window class]: crate::WindowClass + /// [`InputOnly`]: crate::WindowClass::InputOnly + /// + /// [`Drawable` error]: error::Drawable + /// [`Match` error]: error::Match + pub drawable: Drawable, + + /// The [dimensions] which the returned ideal [dimensions] are closest + /// to. + /// + /// [dimensions]: Dimensions + #[doc(alias("width", "height"))] + pub dimensions: Dimensions, + } +} diff --git a/src/x11/request/input.rs b/src/x11/request/input.rs new file mode 100644 index 00000000..efb6f949 --- /dev/null +++ b/src/x11/request/input.rs @@ -0,0 +1,2133 @@ +// 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 [core X11 protocol] that relate to input devices, +//! grabs, and coordinates. +//! +//! [Requests] are messages sent from an X client to the X server. +//! +//! [Requests]: Request +//! [core X11 protocol]: crate::x11 + +extern crate self as xrb; + +use xrbk::{ + pad, + Buf, + BufMut, + ConstantX11Size, + ReadError, + ReadResult, + Readable, + Writable, + WriteResult, + X11Size, +}; +use xrbk_macro::{derive_xrb, Readable, Writable, X11Size}; + +use array_init::array_init; +use std::ops::RangeInclusive; +use thiserror::Error; + +use crate::{ + message::Request, + set::KeyboardOptions, + unit::{Px, SignedPercentage}, + x11::{error, reply}, + Any, + AnyModifierKeyMask, + Button, + Coords, + CurrentableTime, + CursorAppearance, + CursorEventMask, + FocusWindow, + FreezeMode, + Keycode, + Keysym, + Window, +}; + +macro_rules! request_error { + ( + $(#[$meta:meta])* + $vis:vis enum $Name:ident for $Request:ty { + $($($Error:ident),+$(,)?)? + } + ) => { + #[doc = concat!( + "An [error](crate::message::Error) generated because of a failed [`", + stringify!($Request), + "` request](", + stringify!($Request), + ")." + )] + #[doc = ""] + $(#[$meta])* + $vis enum $Name { + $($( + #[doc = concat!( + "A [`", + stringify!($Error), + "` error](error::", + stringify!($Error), + ")." + )] + $Error(error::$Error) + ),+)? + } + }; +} + +request_error! { + pub enum GrabCursorError for GrabCursor { + CursorAppearance, + Value, + Window, + } +} + +derive_xrb! { + /// A [request] that actively grabs control of the cursor. + /// + /// This [request] generates [`EnterWindow`] and [`LeaveWindow`] events. + /// + /// # Replies + /// This [request] generates a [`GrabCursor` reply]. + /// + /// # Errors + /// A [`Window` error] is generated if either the `grab_window` or the + /// `confine_to` [window] do not refer to defined [windows][window]. + /// + /// A [`CursorAppearance` error] is generated if the `cursor_appearance` is + /// [`Some`] and does not refer to a defined [cursor appearance]. + /// + /// [cursor appearance]: CursorAppearance + /// [window]: Window + /// [request]: Request + /// + /// [`EnterWindow`]: crate::x11::event::EnterWindow + /// [`LeaveWindow`]: crate::x11::event::LeaveWindow + /// [`GrabCursor` reply]: reply::GrabCursor + /// + /// [`Window` error]: error::Window + /// [`CursorAppearance` error]: error::CursorAppearance + #[doc(alias = "GrabPointer")] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct GrabCursor: Request(26, GrabCursorError) -> reply::GrabCursor { + /// Whether cursor [events] which would normally be reported to this + /// client are reported normally. + /// + /// [events]: crate::message::Event + #[metabyte] + pub owner_events: bool, + + /// The [window] on which the cursor is grabbed. + /// + /// # Errors + /// A [`Window` error] is generated if this does not refer to a defined + /// [window]. + /// + /// [window]: Window + /// + /// [`Window` error]: error::Window + pub grab_window: Window, + + /// A mask of the cursor [events] which are to be reported to the + /// your client. + /// + /// [events]: crate::message::Event + pub event_mask: CursorEventMask, + + /// The [freeze mode] applied to the cursor. + /// + /// For [`FreezeMode::Unfrozen`], cursor [event] processing continues + /// as normal. + /// + /// For [`FreezeMode::Frozen`], cursor [event] processing appears to + /// freeze - cursor [events][event] generated during this time are not + /// lost: they are queued to be processed later. The freeze ends when + /// either the grabbing client sends an [`AllowEvents` request], or when + /// the cursor grab is released. + /// + /// [event]: crate::message::Event + /// [freeze mode]: FreezeMode + /// + /// [`AllowEvents` request]: AllowEvents + #[doc(alias("pointer_mode", "cursor_mode"))] + pub cursor_freeze: FreezeMode, + /// The [freeze mode] applied to the keyboard. + /// + /// For [`FreezeMode::Unfrozen`], keyboard [event] processing + /// continues as normal. + /// + /// For [`FreezeMode::Frozen`], keyboard [event] processing appears + /// to freeze - keyboard [events][event] generated during this time are + /// not lost: they are queued to be processed later. The freeze ends + /// when either the grabbing client sends an [`AllowEvents` request], or + /// when the keyboard grab is released. + /// + /// [event]: crate::message::Event + /// [freeze mode]: FreezeMode + /// + /// [`AllowEvents` request]: AllowEvents + #[doc(alias = "keyboard_mode")] + pub keyboard_freeze: FreezeMode, + + /// Optionally confines the cursor to the given [window]. + /// + /// This [window] does not need to have any relation to the + /// `grab_window`. + /// + /// The cursor will be warped to the closest edge of this [window] if it + /// is not already within it. Subsequent changes to the configuration of + /// the [window] which cause the cursor to be outside of the [window] + /// will also trigger the cursor to be warped to the [window] again. + /// + /// # Errors + /// A [`Window` error] is generated if this is [`Some`] and does not + /// refer to a defined [window]. + /// + /// [window]: Window + /// + /// [`Window` error]: error::Window + pub confine_to: Option, + + /// Optionally overrides the [appearance of the cursor], no matter which + /// [window] it is within, for the duration of the grab. + /// + /// # Errors + /// A [`CursorAppearance` error] is generated if this does not refer to + /// a defined [cursor appearance]. + /// + /// [cursor appearance]: CursorAppearance + /// [appearance of the cursor]: CursorAppearance + /// [window]: Window + /// + /// [`CursorAppearance` error]: error::CursorAppearance + #[doc(alias = "cursor")] + pub cursor_appearance: Option, + + /// The [time] at which this grab is recorded as having been initiated. + /// + /// [time]: crate::Timestamp + pub time: CurrentableTime, + } + + /// A [request] that ends an active cursor grab by your client. + /// + /// Any queued [events] are released. + /// + /// This [request] generates [`EnterWindow`] and [`LeaveWindow`] events. + /// + /// [request]: Request + /// [events]: crate::message::Event + /// + /// [`EnterWindow`]: crate::x11::event::EnterWindow + /// [`LeaveWindow`]: crate::x11::event::LeaveWindow + #[doc(alias = "UngrabPointer")] + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct UngrabCursor: Request(27) { + /// The [time] at which the grab is recorded as having been released. + /// + /// [time]: crate::Timestamp + pub time: CurrentableTime, + } +} + +request_error! { + pub enum GrabButtonError for GrabButton { + Access, + CursorAppearance, + Value, + Window, + } +} + +derive_xrb! { + /// A [request] that establishes a passive cursor grab for a given `button` + /// and `modifiers` combination. + /// + /// If the following conditions are true, the grab is converted into an + /// active cursor grab (as described in the [`GrabCursor` request]): + /// - the cursor is not already actively grabbed; and + /// - the specified `button` and specified `modifiers` are held; and + /// - the cursor is within the `grab_window`; and + /// - if the `confine_to` [window] is specified, it is viewable; and + /// - a passive grab for the same `button` and `modifiers` combination does + /// not exist for any ancestor of the `grab_window`. + /// + /// # Errors + /// A [`Window` error] is generated if either the `grab_window` or the + /// `confine_to` [window] do not refer to defined [windows][window]. + /// + /// A [`CursorAppearance` error] is generated if the `cursor_appearance` is + /// [`Some`] and does not refer to a defined [cursor appearance]. + /// + /// An [`Access` error] is generated if some other client has already sent a + /// `GrabButton` [request] with the same `button` and `modifiers` + /// combination on the same `grab_window`. + /// + /// [cursor appearance]: CursorAppearance + /// [window]: Window + /// [request]: Request + /// + /// [`GrabCursor` request]: GrabCursor + /// + /// [`Access` error]: error::Access + /// [`Window` error]: error::Window + /// [`CursorAppearance` error]: error::CursorAppearance + #[derive(Debug, Hash, PartialEq, Eq, X11Size, Readable, Writable, ConstantX11Size)] + pub struct GrabButton: Request(28, GrabButtonError) { + /// Whether cursor [events] which would normally be reported to this + /// client are reported normally. + /// + /// [events]: crate::message::Event + #[metabyte] + pub owner_events: bool, + + /// The [window] on which the `button` is grabbed. + /// + /// # Errors + /// A [`Window` error] is generated if this does not refer to a defined + /// [window]. + /// + /// [window]: Window + /// + /// [`Window` error]: error::Window + pub grab_window: Window, + + /// A mask of the cursor [events] which are to be reported to the + /// grabbing client. + /// + /// [events]: crate::message::Event + pub event_mask: CursorEventMask, + + /// The [freeze mode] applied to the cursor. + /// + /// For [`FreezeMode::Unfrozen`], cursor [event] processing continues + /// as normal. + /// + /// For [`FreezeMode::Frozen`], cursor [event] processing appears to + /// freeze - cursor [events][event] generated during this time are not + /// lost: they are queued to be processed later. The freeze ends when + /// either the grabbing client sends an [`AllowEvents` request], or when + /// the cursor grab is released. + /// + /// [event]: crate::message::Event + /// [freeze mode]: FreezeMode + /// + /// [`AllowEvents` request]: AllowEvents + #[doc(alias("pointer_mode", "cursor_mode"))] + pub cursor_freeze: FreezeMode, + /// The [freeze mode] applied to the keyboard. + /// + /// For [`FreezeMode::Unfrozen`], keyboard [event] processing + /// continues as normal. + /// + /// For [`FreezeMode::Frozen`], keyboard [event] processing appears + /// to freeze - keyboard [events][event] generated during this time are + /// not lost: they are queued to be processed later. The freeze ends + /// when either the grabbing client sends an [`AllowEvents` request], or + /// when the keyboard grab is released. + /// + /// [event]: crate::message::Event + /// [freeze mode]: FreezeMode + /// + /// [`AllowEvents` request]: AllowEvents + #[doc(alias = "keyboard_mode")] + pub keyboard_freeze: FreezeMode, + + /// Optionally confines the cursor to the given [window]. + /// + /// This [window] does not need to have any relation to the + /// `grab_window`. + /// + /// The cursor will be warped to the closest edge of this [window] if it + /// is not already within it. Subsequent changes to the configuration of + /// the [window] which cause the cursor to be outside of the [window] + /// will also trigger the cursor to be warped to the [window] again. + /// + /// # Errors + /// A [`Window` error] is generated if this is [`Some`] and does not + /// refer to a defined [window]. + /// + /// [window]: Window + /// + /// [`Window` error]: error::Window + pub confine_to: Option, + + /// Optionally overrides the [appearance of the cursor], no matter which + /// [window] it is within, for the duration of the grab. + /// + /// # Errors + /// A [`CursorAppearance` error] is generated if this does not refer to + /// a defined [cursor appearance]. + /// + /// [cursor appearance]: CursorAppearance + /// [appearance of the cursor]: CursorAppearance + /// [window]: Window + /// + /// [`CursorAppearance` error]: error::CursorAppearance + pub cursor_appearance: Option, + + /// The [button] for which this grab is established. + /// + /// [`Any`] means that the grab is effectively established for all + /// possible [buttons][button]. + /// + /// When this button and the given `modifiers`, + /// + /// [button]: Button + /// + /// [`Any`]: Any::Any + pub button: Any