winit/platform_impl/linux/x11/util/
window_property.rs1use std::error::Error;
2use std::fmt;
3use std::sync::Arc;
4
5use bytemuck::{NoUninit, Pod};
6
7use x11rb::connection::Connection;
8use x11rb::errors::ReplyError;
9
10use super::*;
11
12pub const CARDINAL_SIZE: usize = mem::size_of::<u32>();
13
14pub type Cardinal = u32;
15
16#[derive(Debug, Clone)]
17pub enum GetPropertyError {
18 X11rbError(Arc<ReplyError>),
19 TypeMismatch(xproto::Atom),
20 FormatMismatch(c_int),
21}
22
23impl GetPropertyError {
24 pub fn is_actual_property_type(&self, t: xproto::Atom) -> bool {
25 if let GetPropertyError::TypeMismatch(actual_type) = *self {
26 actual_type == t
27 } else {
28 false
29 }
30 }
31}
32
33impl<T: Into<ReplyError>> From<T> for GetPropertyError {
34 fn from(e: T) -> Self {
35 Self::X11rbError(Arc::new(e.into()))
36 }
37}
38
39impl fmt::Display for GetPropertyError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 GetPropertyError::X11rbError(err) => err.fmt(f),
43 GetPropertyError::TypeMismatch(err) => write!(f, "type mismatch: {err}"),
44 GetPropertyError::FormatMismatch(err) => write!(f, "format mismatch: {err}"),
45 }
46 }
47}
48
49impl Error for GetPropertyError {}
50
51const PROPERTY_BUFFER_SIZE: u32 = 1024; impl XConnection {
56 pub fn get_property<T: Pod>(
57 &self,
58 window: xproto::Window,
59 property: xproto::Atom,
60 property_type: xproto::Atom,
61 ) -> Result<Vec<T>, GetPropertyError> {
62 let mut iter = PropIterator::new(self.xcb_connection(), window, property, property_type);
63 let mut data = vec![];
64
65 loop {
66 if !iter.next_window(&mut data)? {
67 break;
68 }
69 }
70
71 Ok(data)
72 }
73
74 pub fn change_property<'a, T: NoUninit>(
75 &'a self,
76 window: xproto::Window,
77 property: xproto::Atom,
78 property_type: xproto::Atom,
79 mode: xproto::PropMode,
80 new_value: &[T],
81 ) -> Result<VoidCookie<'a>, X11Error> {
82 assert!([1usize, 2, 4].contains(&mem::size_of::<T>()));
83 self.xcb_connection()
84 .change_property(
85 mode,
86 window,
87 property,
88 property_type,
89 (mem::size_of::<T>() * 8) as u8,
90 new_value.len().try_into().expect("too many items for property"),
91 bytemuck::cast_slice::<T, u8>(new_value),
92 )
93 .map_err(Into::into)
94 }
95}
96
97struct PropIterator<'a, C: ?Sized, T> {
99 conn: &'a C,
101
102 window: xproto::Window,
104
105 property: xproto::Atom,
107
108 property_type: xproto::Atom,
110
111 offset: u32,
113
114 format: u8,
116
117 _phantom: std::marker::PhantomData<T>,
119}
120
121impl<'a, C: Connection + ?Sized, T: Pod> PropIterator<'a, C, T> {
122 fn new(
124 conn: &'a C,
125 window: xproto::Window,
126 property: xproto::Atom,
127 property_type: xproto::Atom,
128 ) -> Self {
129 let format = match mem::size_of::<T>() {
130 1 => 8,
131 2 => 16,
132 4 => 32,
133 _ => unreachable!(),
134 };
135
136 Self {
137 conn,
138 window,
139 property,
140 property_type,
141 offset: 0,
142 format,
143 _phantom: Default::default(),
144 }
145 }
146
147 fn next_window(&mut self, data: &mut Vec<T>) -> Result<bool, GetPropertyError> {
151 let reply = self
153 .conn
154 .get_property(
155 false,
156 self.window,
157 self.property,
158 self.property_type,
159 self.offset,
160 PROPERTY_BUFFER_SIZE,
161 )?
162 .reply()?;
163
164 if reply.type_ != self.property_type {
166 return Err(GetPropertyError::TypeMismatch(reply.type_));
167 }
168
169 if reply.format != self.format {
171 return Err(GetPropertyError::FormatMismatch(reply.format.into()));
172 }
173
174 if mem::size_of::<T>() == 1 && mem::align_of::<T>() == 1 {
176 data.extend_from_slice(bytemuck::cast_slice(&reply.value));
178 } else {
179 let old_len = data.len();
187 let added_len = reply.value.len() / mem::size_of::<T>();
188 data.resize(old_len + added_len, T::zeroed());
189 bytemuck::cast_slice_mut::<T, u8>(&mut data[old_len..]).copy_from_slice(&reply.value);
190 }
191
192 self.offset += PROPERTY_BUFFER_SIZE;
194 Ok(reply.bytes_after != 0)
195 }
196}