Skip to content

Commit 7e3407f

Browse files
committed
x11: Check visuals for validity
A visual describes how colors are laid out by the X11 server. So far, softbuffer did not do anything with visuals and just passed them around. This commit adds checks that should ensure that a given visual actually lays out pixels in the only format supported by softbuffer: - 32 bit per pixel - 00RRGGBB byte order In this patch, I also try to handle big endian systems and mixed endian situations where we are e.g. running on a big endian system and talking to a little endian X11 server. However, this is all theoretical and completely untested. I only tested my X11 server's default visual works and some ARGB visual is rejected. Fixes: rust-windowing#184 Signed-off-by: Uli Schlachter <[email protected]>
1 parent 64dad39 commit 7e3407f

File tree

1 file changed

+72
-1
lines changed

1 file changed

+72
-1
lines changed

src/x11.rs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustix::{
1717
};
1818

1919
use std::{
20+
collections::HashSet,
2021
fmt,
2122
fs::File,
2223
io, mem,
@@ -31,7 +32,7 @@ use x11rb::connection::{Connection, SequenceNumber};
3132
use x11rb::cookie::Cookie;
3233
use x11rb::errors::{ConnectionError, ReplyError, ReplyOrIdError};
3334
use x11rb::protocol::shm::{self, ConnectionExt as _};
34-
use x11rb::protocol::xproto::{self, ConnectionExt as _};
35+
use x11rb::protocol::xproto::{self, ConnectionExt as _, ImageOrder, VisualClass, Visualid};
3536
use x11rb::xcb_ffi::XCBConnection;
3637

3738
pub struct X11DisplayImpl<D: ?Sized> {
@@ -41,6 +42,9 @@ pub struct X11DisplayImpl<D: ?Sized> {
4142
/// SHM extension is available.
4243
is_shm_available: bool,
4344

45+
/// All visuals using softbuffer's pixel representation
46+
supported_visuals: HashSet<Visualid>,
47+
4448
/// The generic display where the `connection` field comes from.
4549
///
4650
/// Without `&mut`, the underlying connection cannot be closed without other unsafe behavior.
@@ -100,9 +104,12 @@ impl<D: HasDisplayHandle + ?Sized> X11DisplayImpl<D> {
100104
log::warn!("SHM extension is not available. Performance may be poor.");
101105
}
102106

107+
let supported_visuals = supported_visuals(&connection);
108+
103109
Ok(Self {
104110
connection: Some(connection),
105111
is_shm_available,
112+
supported_visuals,
106113
_display: display,
107114
})
108115
}
@@ -254,6 +261,16 @@ impl<D: HasDisplayHandle + ?Sized, W: HasWindowHandle> X11Impl<D, W> {
254261
(geometry_reply, visual_id)
255262
};
256263

264+
if !display.supported_visuals.contains(&visual_id) {
265+
return Err(SoftBufferError::PlatformError(
266+
Some(format!(
267+
"Visual 0x{visual_id:x} does not use softbuffer's pixel format and is unsupported"
268+
)),
269+
None,
270+
)
271+
.into());
272+
}
273+
257274
// See if SHM is available.
258275
let buffer = if display.is_shm_available {
259276
// SHM is available.
@@ -855,6 +872,60 @@ fn is_shm_available(c: &impl Connection) -> bool {
855872
matches!((attach.check(), detach.check()), (Ok(()), Ok(())))
856873
}
857874

875+
/// Collect all visuals that use softbuffer's pixel format
876+
fn supported_visuals(c: &impl Connection) -> HashSet<Visualid> {
877+
// Check that depth 24 uses 32 bits per pixels
878+
if !c
879+
.setup()
880+
.pixmap_formats
881+
.iter()
882+
.any(|f| f.depth == 24 && f.bits_per_pixel == 32)
883+
{
884+
log::warn!("X11 server does not have a depth 24 format with 32 bits per pixel");
885+
return HashSet::new();
886+
}
887+
888+
// How does the server represent red, green, blue components of a pixel?
889+
#[cfg(target_endian = "little")]
890+
let own_byte_order = ImageOrder::LSB_FIRST;
891+
#[cfg(target_endian = "big")]
892+
let own_byte_order = ImageOrder::MSB_FIRST;
893+
let expected_masks = if c.setup().image_byte_order == own_byte_order {
894+
(0xff0000, 0xff00, 0xff)
895+
} else {
896+
// This is the byte-swapped version of our wished-for format
897+
(0xff00, 0xff0000, 0xff000000)
898+
};
899+
900+
c.setup()
901+
.roots
902+
.iter()
903+
.flat_map(|screen| {
904+
screen
905+
.allowed_depths
906+
.iter()
907+
.filter(|depth| depth.depth == 24)
908+
.flat_map(|depth| {
909+
depth
910+
.visuals
911+
.iter()
912+
.filter(|visual| {
913+
// Ignore grayscale or indexes / color palette visuals
914+
matches!(
915+
visual.class,
916+
VisualClass::TRUE_COLOR | VisualClass::DIRECT_COLOR
917+
)
918+
})
919+
.filter(|visual| {
920+
// Colors must be laid out as softbuffer expects
921+
expected_masks == (visual.red_mask, visual.green_mask, visual.blue_mask)
922+
})
923+
.map(|visual| visual.visual_id)
924+
})
925+
})
926+
.collect()
927+
}
928+
858929
/// An error that can occur when pushing a buffer to the window.
859930
#[derive(Debug)]
860931
enum PushBufferError {

0 commit comments

Comments
 (0)