Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-ui-renderer/src/windowing/winit/linux/app.rs
1//! Linux-specific app level functionality for use with `winit`.
2//!
3//! For more information about X11 extensions and request codes/opcodes,
4//! see: https://www.x.org/wiki/Development/Documentation/Protocol/OpCodes.
5 
6use lazy_static::lazy_static;
7use std::sync::{Arc, Mutex};
8use wgpu::rwh::{HasDisplayHandle, RawDisplayHandle};
9use winit::event_loop::EventLoop;
10use x11rb::protocol::xproto::ConnectionExt as _;
11 
12lazy_static! {
13 static ref ENCOUNTERED_BAD_MATCH_FROM_DRI3_FENCE_FROM_FD: Arc<Mutex<bool>> = Default::default();
14}
15 
16/// Returns whether a `BadMatch` was returned from a `DRI3FenceFromFd` request.
17/// NOTE calling this function resets internal state. Subsequent calls to this function will return
18/// false until a new `BadMatch` error is encountered for the aforementioned request.
19pub fn take_encountered_bad_match_from_dri3_fence_from_fd() -> bool {
20 let Ok(mut guard) = ENCOUNTERED_BAD_MATCH_FROM_DRI3_FENCE_FROM_FD.lock() else {
21 return false;
22 };
23 
24 std::mem::take(&mut guard)
25}
26 
27/// Registers an xlib error hook with winit, if needed.
28pub fn maybe_register_xlib_error_hook<T>(event_loop: &EventLoop<T>) {
29 if !is_x11(event_loop) {
30 return;
31 }
32 
33 let extension_info_map = get_x11_extension_info_map();
34 
35 // Register a callback function with winit that we can use to
36 // observe and consume error events from the Xlib event loop.
37 // This returns a boolean indicating whether or not it was
38 // "handled" by this error hook. If `true` is returned,
39 // winit will ignore the error.
40 winit::platform::x11::register_xlib_error_hook(Box::new(move |_, error| {
41 static GRAB_KEY_REQUEST_CODE: u8 = 33;
42 static PRESENT_PIXMAP_MINOR_OPCODE: u8 = 1;
43 
44 /// Minor opcode for the `DRI3FenceFromFD` request within the DRI3 extension.
45 /// See https://cgit.freedesktop.org/xorg/proto/dri3proto/tree/dri3proto.txt.
46 static DRI3_FENCE_FROM_FD_MINOR_OPCODE: u8 = 4;
47 
48 let Some(error) = std::ptr::NonNull::new(error as *mut x11_dl::xlib::XErrorEvent) else {
49 return false;
50 };
51 let error = unsafe { error.as_ref() };
52 
53 // Ignore errors due to global-hotkey attempting to register a
54 // hotkey that's already been registered.
55 if error.error_code == x11_dl::xlib::BadAccess
56 && error.request_code == GRAB_KEY_REQUEST_CODE
57 {
58 return true;
59 }
60 
61 let Some(extension_info) = extension_info_map.get(&error.request_code) else {
62 // If we can't get information about the extension, let winit
63 // handle it. It will log the error, so we don't need to.
64 return false;
65 };
66 
67 // If there's a BadWindow error from a PresentPixmap
68 // request, ignore it - this is a known bug in Mesa.
69 if error.error_code == x11_dl::xlib::BadWindow
70 && extension_info.name == "Present"
71 && error.minor_code == PRESENT_PIXMAP_MINOR_OPCODE
72 {
73 log::warn!("Ignoring BadWindow error from PresentPixmap request");
74 return true;
75 }
76 
77 // Specifically handle a `BadMatch` from a `DRI3_FENCE_FROM_FD` request. From error
78 // reporting, we only seem to get this error when a user has the Performance PRIME profile
79 // enabled (indicating to NVIDIA Optimus that the NVIDIA GPU should always be used).
80 if error.error_code == x11_dl::xlib::BadMatch
81 && extension_info.name == "DRI3"
82 && error.minor_code == DRI3_FENCE_FROM_FD_MINOR_OPCODE
83 {
84 log::warn!("Ignoring a BadMatch from a DRI3FenceFromFD request. The NVIDIA Performance PRIME profile is likely enabled.");
85 *ENCOUNTERED_BAD_MATCH_FROM_DRI3_FENCE_FROM_FD
86 .lock()
87 .unwrap() = true;
88 return true;
89 }
90 
91 // For other errors from requests defined in extensions, log some
92 // relevant extension information, then let winit decide what to do
93 // with it. winit will log an error if we don't handle it, hence only logging a warning
94 // here.
95 log::warn!(
96 "Detected X11 error in {} extension (major opcode: {}; first error: {})",
97 extension_info.name,
98 error.request_code,
99 extension_info.first_error,
100 );
101 
102 if *ENCOUNTERED_BAD_MATCH_FROM_DRI3_FENCE_FROM_FD
103 .lock()
104 .expect("Mutex should not be poisoned")
105 && extension_info.name == "Present"
106 {
107 log::warn!("Ignoring an error from the PRESENT extension after catching a BadMatch from a DRI3FenceFromFD request. Minor opcode: {}; Error code: {}",
108 error.minor_code,
109 error.error_code);
110 return true;
111 }
112 
113 false
114 }));
115}
116 
117/// Queries the X11 server to get information about which extensions are
118/// available and metadata about them.
119fn get_x11_extension_info_map() -> std::collections::HashMap<u8, X11ExtensionInfo> {
120 let mut extension_map = Default::default();
121 let Ok((xcb, _)) = x11rb::rust_connection::RustConnection::connect(None) else {
122 return extension_map;
123 };
124 
125 let Ok(cookie) = xcb.list_extensions() else {
126 return extension_map;
127 };
128 
129 let Ok(extensions) = cookie.reply() else {
130 return extension_map;
131 };
132 
133 extensions.names.iter().for_each(|name| {
134 if let Ok(cookie) = xcb.query_extension(&name.name) {
135 if let Ok(result) = cookie.reply() {
136 if let Ok(name) = String::from_utf8(name.name.clone()) {
137 extension_map.insert(
138 result.major_opcode,
139 X11ExtensionInfo {
140 name,
141 first_error: result.first_error,
142 },
143 );
144 }
145 }
146 }
147 });
148 
149 extension_map
150}
151 
152/// A collection of information about an X11 extension.
153struct X11ExtensionInfo {
154 /// The name of the extension.
155 name: String,
156 
157 /// The ID offset applied to errors defined in this extension.
158 first_error: u8,
159}
160 
161/// Returns whether or not the provided event loop is using X11 as the
162/// underlying platform implementation.
163fn is_x11<T>(event_loop: &EventLoop<T>) -> bool {
164 matches!(
165 event_loop
166 .owned_display_handle()
167 .display_handle()
168 .map(|dh| dh.as_raw()),
169 Ok(RawDisplayHandle::Xlib(_)) | Ok(RawDisplayHandle::Xcb(_))
170 )
171}
172