Skip to content

Commit f5ae585

Browse files
authored
X11: split off Visual Info negotiation into a separate module (#177)
1 parent e8b1236 commit f5ae585

File tree

4 files changed

+116
-59
lines changed

4 files changed

+116
-59
lines changed

src/x11/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ pub use window::*;
66

77
mod cursor;
88
mod keyboard;
9+
mod visual_info;

src/x11/visual_info.rs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
use crate::x11::xcb_connection::XcbConnection;
2+
use std::error::Error;
3+
use x11rb::connection::Connection;
4+
use x11rb::protocol::xproto::{
5+
Colormap, ColormapAlloc, ConnectionExt, Screen, VisualClass, Visualid,
6+
};
7+
use x11rb::COPY_FROM_PARENT;
8+
9+
pub(super) struct WindowVisualConfig {
10+
#[cfg(feature = "opengl")]
11+
pub fb_config: Option<crate::gl::x11::FbConfig>,
12+
13+
pub visual_depth: u8,
14+
pub visual_id: Visualid,
15+
pub color_map: Option<Colormap>,
16+
}
17+
18+
// TODO: make visual negotiation actually check all of a visual's parameters
19+
impl WindowVisualConfig {
20+
#[cfg(feature = "opengl")]
21+
pub fn find_best_visual_config_for_gl(
22+
connection: &XcbConnection, gl_config: Option<crate::gl::GlConfig>,
23+
) -> Result<Self, Box<dyn Error>> {
24+
let Some(gl_config) = gl_config else { return Self::find_best_visual_config(connection) };
25+
26+
// SAFETY: TODO
27+
let (fb_config, window_config) = unsafe {
28+
crate::gl::platform::GlContext::get_fb_config_and_visual(connection.dpy, gl_config)
29+
}
30+
.expect("Could not fetch framebuffer config");
31+
32+
Ok(Self {
33+
fb_config: Some(fb_config),
34+
visual_depth: window_config.depth,
35+
visual_id: window_config.visual,
36+
color_map: Some(create_color_map(connection, window_config.visual)?),
37+
})
38+
}
39+
40+
pub fn find_best_visual_config(connection: &XcbConnection) -> Result<Self, Box<dyn Error>> {
41+
match find_visual_for_depth(connection.screen(), 32) {
42+
None => Ok(Self::copy_from_parent()),
43+
Some(visual_id) => Ok(Self {
44+
#[cfg(feature = "opengl")]
45+
fb_config: None,
46+
visual_id,
47+
visual_depth: 32,
48+
color_map: Some(create_color_map(connection, visual_id)?),
49+
}),
50+
}
51+
}
52+
53+
const fn copy_from_parent() -> Self {
54+
Self {
55+
#[cfg(feature = "opengl")]
56+
fb_config: None,
57+
visual_depth: COPY_FROM_PARENT as u8,
58+
visual_id: COPY_FROM_PARENT,
59+
color_map: None,
60+
}
61+
}
62+
}
63+
64+
// For this 32-bit depth to work, you also need to define a color map and set a border
65+
// pixel: https://cgit.freedesktop.org/xorg/xserver/tree/dix/window.c#n818
66+
pub fn create_color_map(
67+
connection: &XcbConnection, visual_id: Visualid,
68+
) -> Result<Colormap, Box<dyn Error>> {
69+
let colormap = connection.conn.generate_id()?;
70+
connection.conn.create_colormap(
71+
ColormapAlloc::NONE,
72+
colormap,
73+
connection.screen().root,
74+
visual_id,
75+
)?;
76+
77+
Ok(colormap)
78+
}
79+
80+
fn find_visual_for_depth(screen: &Screen, depth: u8) -> Option<Visualid> {
81+
for candidate_depth in &screen.allowed_depths {
82+
if candidate_depth.depth != depth {
83+
continue;
84+
}
85+
86+
for candidate_visual in &candidate_depth.visuals {
87+
if candidate_visual.class == VisualClass::TRUE_COLOR {
88+
return Some(candidate_visual.visual_id);
89+
}
90+
}
91+
}
92+
93+
None
94+
}

src/x11/window.rs

Lines changed: 15 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ use raw_window_handle::{
1414

1515
use x11rb::connection::Connection;
1616
use x11rb::protocol::xproto::{
17-
AtomEnum, ChangeWindowAttributesAux, ColormapAlloc, ConfigureWindowAux, ConnectionExt as _,
18-
CreateGCAux, CreateWindowAux, EventMask, PropMode, Screen, VisualClass, Visualid,
19-
Window as XWindow, WindowClass,
17+
AtomEnum, ChangeWindowAttributesAux, ConfigureWindowAux, ConnectionExt as _, CreateGCAux,
18+
CreateWindowAux, EventMask, PropMode, Visualid, Window as XWindow, WindowClass,
2019
};
2120
use x11rb::protocol::Event as XEvent;
2221
use x11rb::wrapper::ConnectionExt as _;
@@ -31,6 +30,7 @@ use super::keyboard::{convert_key_press_event, convert_key_release_event, key_mo
3130

3231
#[cfg(feature = "opengl")]
3332
use crate::gl::{platform, GlContext};
33+
use crate::x11::visual_info::WindowVisualConfig;
3434

3535
pub struct WindowHandle {
3636
raw_window_handle: Option<RawWindowHandle>,
@@ -187,11 +187,9 @@ impl<'a> Window<'a> {
187187
// FIXME: baseview error type instead of unwrap()
188188
let xcb_connection = XcbConnection::new()?;
189189

190-
// Get screen information (?)
191-
let setup = xcb_connection.conn.setup();
192-
let screen = &setup.roots[xcb_connection.screen];
193-
194-
let parent_id = parent.unwrap_or_else(|| screen.root);
190+
// Get screen information
191+
let screen = xcb_connection.screen();
192+
let parent_id = parent.unwrap_or(screen.root);
195193

196194
let gc_id = xcb_connection.conn.generate_id()?;
197195
xcb_connection.conn.create_gc(
@@ -207,39 +205,16 @@ impl<'a> Window<'a> {
207205

208206
let window_info = WindowInfo::from_logical_size(options.size, scaling);
209207

210-
// Now it starts becoming fun. If we're creating an OpenGL context, then we need to create
211-
// the window with a visual that matches the framebuffer used for the OpenGL context. So the
212-
// idea is that we first retrieve a framebuffer config that matches our wanted OpenGL
213-
// configuration, find the visual that matches that framebuffer config, create the window
214-
// with that visual, and then finally create an OpenGL context for the window. If we don't
215-
// use OpenGL, then we'll just take a random visual with a 32-bit depth.
216-
let create_default_config = || {
217-
Self::find_visual_for_depth(screen, 32)
218-
.map(|visual| (32, visual))
219-
.unwrap_or((x11rb::COPY_FROM_PARENT as u8, x11rb::COPY_FROM_PARENT as u32))
220-
};
221208
#[cfg(feature = "opengl")]
222-
let (fb_config, (depth, visual)) = match options.gl_config {
223-
Some(gl_config) => unsafe {
224-
platform::GlContext::get_fb_config_and_visual(xcb_connection.dpy, gl_config)
225-
.map(|(fb_config, window_config)| {
226-
(Some(fb_config), (window_config.depth, window_config.visual))
227-
})
228-
.expect("Could not fetch framebuffer config")
229-
},
230-
None => (None, create_default_config()),
231-
};
232-
#[cfg(not(feature = "opengl"))]
233-
let (depth, visual) = create_default_config();
209+
let visual_info =
210+
WindowVisualConfig::find_best_visual_config_for_gl(&xcb_connection, options.gl_config)?;
234211

235-
// For this 32-bith depth to work, you also need to define a color map and set a border
236-
// pixel: https://cgit.freedesktop.org/xorg/xserver/tree/dix/window.c#n818
237-
let colormap = xcb_connection.conn.generate_id()?;
238-
xcb_connection.conn.create_colormap(ColormapAlloc::NONE, colormap, screen.root, visual)?;
212+
#[cfg(not(feature = "opengl"))]
213+
let visual_info = WindowVisualConfig::find_best_visual_config(&xcb_connection)?;
239214

240215
let window_id = xcb_connection.conn.generate_id()?;
241216
xcb_connection.conn.create_window(
242-
depth,
217+
visual_info.visual_depth,
243218
window_id,
244219
parent_id,
245220
0, // x coordinate of the new window
@@ -248,7 +223,7 @@ impl<'a> Window<'a> {
248223
window_info.physical_size().height as u16, // window height
249224
0, // window border
250225
WindowClass::INPUT_OUTPUT,
251-
visual,
226+
visual_info.visual_id,
252227
&CreateWindowAux::new()
253228
.event_mask(
254229
EventMask::EXPOSURE
@@ -263,7 +238,7 @@ impl<'a> Window<'a> {
263238
)
264239
// As mentioned above, these two values are needed to be able to create a window
265240
// with a depth of 32-bits when the parent window has a different depth
266-
.colormap(colormap)
241+
.colormap(visual_info.color_map)
267242
.border_pixel(0),
268243
)?;
269244
xcb_connection.conn.map_window(window_id)?;
@@ -292,7 +267,7 @@ impl<'a> Window<'a> {
292267
// no error handling anymore at this point. Everything is more or less unchanged
293268
// compared to when raw-gl-context was a separate crate.
294269
#[cfg(feature = "opengl")]
295-
let gl_context = fb_config.map(|fb_config| {
270+
let gl_context = visual_info.fb_config.map(|fb_config| {
296271
use std::ffi::c_ulong;
297272

298273
let window = window_id as c_ulong;
@@ -308,7 +283,7 @@ impl<'a> Window<'a> {
308283
xcb_connection,
309284
window_id,
310285
window_info,
311-
visual_id: visual,
286+
visual_id: visual_info.visual_id,
312287
mouse_cursor: MouseCursor::default(),
313288

314289
frame_interval: Duration::from_millis(15),
@@ -387,22 +362,6 @@ impl<'a> Window<'a> {
387362
pub fn gl_context(&self) -> Option<&crate::gl::GlContext> {
388363
self.inner.gl_context.as_ref()
389364
}
390-
391-
fn find_visual_for_depth(screen: &Screen, depth: u8) -> Option<Visualid> {
392-
for candidate_depth in &screen.allowed_depths {
393-
if candidate_depth.depth != depth {
394-
continue;
395-
}
396-
397-
for candidate_visual in &candidate_depth.visuals {
398-
if candidate_visual.class == VisualClass::TRUE_COLOR {
399-
return Some(candidate_visual.visual_id);
400-
}
401-
}
402-
}
403-
404-
None
405-
}
406365
}
407366

408367
impl WindowInner {

src/x11/xcb_connection.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use x11::{xlib, xlib::Display, xlib_xcb};
55

66
use x11rb::connection::Connection;
77
use x11rb::cursor::Handle as CursorHandle;
8-
use x11rb::protocol::xproto::Cursor;
8+
use x11rb::protocol::xproto::{Cursor, Screen};
99
use x11rb::resource_manager;
1010
use x11rb::xcb_ffi::XCBConnection;
1111

@@ -76,8 +76,7 @@ impl XcbConnection {
7676
// If neither work, I guess just assume 96.0 and don't do any scaling.
7777
fn get_scaling_screen_dimensions(&self) -> f64 {
7878
// Figure out screen information
79-
let setup = self.conn.setup();
80-
let screen = &setup.roots[self.screen];
79+
let screen = self.screen();
8180

8281
// Get the DPI from the screen struct
8382
//
@@ -115,4 +114,8 @@ impl XcbConnection {
115114
}
116115
}
117116
}
117+
118+
pub fn screen(&self) -> &Screen {
119+
&self.conn.setup().roots[self.screen]
120+
}
118121
}

0 commit comments

Comments
 (0)