winit_x11/lib.rs
1//! # X11
2
3use dpi::Size;
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6use winit_core::event_loop::ActiveEventLoop as CoreActiveEventLoop;
7use winit_core::window::{ActivationToken, PlatformWindowAttributes, Window as CoreWindow};
8
9pub use crate::event_loop::{ActiveEventLoop, EventLoop};
10pub use crate::window::Window;
11
12macro_rules! os_error {
13 ($error:expr) => {{ winit_core::error::OsError::new(line!(), file!(), $error) }};
14}
15
16mod activation;
17mod atoms;
18mod dnd;
19mod event_loop;
20mod event_processor;
21pub mod ffi;
22mod ime;
23mod monitor;
24mod util;
25mod window;
26mod xdisplay;
27mod xsettings;
28
29/// X window type. Maps directly to
30/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
31#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
32#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
33pub enum WindowType {
34 /// A desktop feature. This can include a single window containing desktop icons with the same
35 /// dimensions as the screen, allowing the desktop environment to have full control of the
36 /// desktop, without the need for proxying root window clicks.
37 Desktop,
38 /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all
39 /// other windows.
40 Dock,
41 /// Toolbar windows. "Torn off" from the main application.
42 Toolbar,
43 /// Pinnable menu windows. "Torn off" from the main application.
44 Menu,
45 /// A small persistent utility window, such as a palette or toolbox.
46 Utility,
47 /// The window is a splash screen displayed as an application is starting up.
48 Splash,
49 /// This is a dialog window.
50 Dialog,
51 /// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
52 /// This property is typically used on override-redirect windows.
53 DropdownMenu,
54 /// A popup menu that usually appears when the user right clicks on an object.
55 /// This property is typically used on override-redirect windows.
56 PopupMenu,
57 /// A tooltip window. Usually used to show additional information when hovering over an object
58 /// with the cursor. This property is typically used on override-redirect windows.
59 Tooltip,
60 /// The window is a notification.
61 /// This property is typically used on override-redirect windows.
62 Notification,
63 /// This should be used on the windows that are popped up by combo boxes.
64 /// This property is typically used on override-redirect windows.
65 Combo,
66 /// This indicates the window is being dragged.
67 /// This property is typically used on override-redirect windows.
68 Dnd,
69 /// This is a normal, top-level window.
70 #[default]
71 Normal,
72}
73
74/// The first argument in the provided hook will be the pointer to `XDisplay`
75/// and the second one the pointer to [`XErrorEvent`]. The returned `bool` is an
76/// indicator whether the error was handled by the callback.
77///
78/// [`XErrorEvent`]: https://linux.die.net/man/3/xerrorevent
79pub type XlibErrorHook =
80 Box<dyn Fn(*mut std::ffi::c_void, *mut std::ffi::c_void) -> bool + Send + Sync>;
81
82/// A unique identifier for an X11 visual.
83pub type XVisualID = u32;
84
85/// A unique identifier for an X11 window.
86pub type XWindow = u32;
87
88/// Hook to winit's xlib error handling callback.
89///
90/// This method is provided as a safe way to handle the errors coming from X11
91/// when using xlib in external crates, like glutin for GLX access. Trying to
92/// handle errors by speculating with `XSetErrorHandler` is [`unsafe`].
93///
94/// **Be aware that your hook is always invoked and returning `true` from it will
95/// prevent `winit` from getting the error itself. It's wise to always return
96/// `false` if you're not initiated the `Sync`.**
97///
98/// [`unsafe`]: https://www.remlab.net/op/xlib.shtml
99#[inline]
100pub fn register_xlib_error_hook(hook: XlibErrorHook) {
101 // Append new hook.
102 crate::event_loop::XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
103}
104
105/// Additional methods on [`ActiveEventLoop`] that are specific to X11.
106///
107/// [`ActiveEventLoop`]: winit_core::event_loop::ActiveEventLoop
108pub trait ActiveEventLoopExtX11 {
109 /// True if the event loop uses X11.
110 fn is_x11(&self) -> bool;
111}
112
113impl ActiveEventLoopExtX11 for dyn CoreActiveEventLoop + '_ {
114 #[inline]
115 fn is_x11(&self) -> bool {
116 self.cast_ref::<ActiveEventLoop>().is_some()
117 }
118}
119
120/// Additional methods on [`EventLoop`] that are specific to X11.
121pub trait EventLoopExtX11 {
122 /// True if the [`EventLoop`] uses X11.
123 fn is_x11(&self) -> bool;
124}
125
126/// Additional methods when building event loop that are specific to X11.
127pub trait EventLoopBuilderExtX11 {
128 /// Force using X11.
129 fn with_x11(&mut self) -> &mut Self;
130
131 /// Whether to allow the event loop to be created off of the main thread.
132 ///
133 /// By default, the window is only allowed to be created on the main
134 /// thread, to make platform compatibility easier.
135 fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
136}
137
138/// Additional methods on [`Window`] that are specific to X11.
139///
140/// [`Window`]: crate::window::Window
141pub trait WindowExtX11 {}
142
143impl WindowExtX11 for dyn CoreWindow {}
144
145#[derive(Debug, Clone, PartialEq, Eq)]
146pub(crate) struct ApplicationName {
147 pub(crate) general: String,
148 pub(crate) instance: String,
149}
150
151#[derive(Clone, Debug)]
152pub struct WindowAttributesX11 {
153 pub(crate) name: Option<ApplicationName>,
154 pub(crate) activation_token: Option<ActivationToken>,
155 pub(crate) visual_id: Option<XVisualID>,
156 pub(crate) screen_id: Option<i32>,
157 pub(crate) base_size: Option<Size>,
158 pub(crate) override_redirect: bool,
159 pub(crate) x11_window_types: Vec<WindowType>,
160
161 /// The parent window to embed this window into.
162 pub(crate) embed_window: Option<XWindow>,
163}
164
165impl Default for WindowAttributesX11 {
166 fn default() -> Self {
167 Self {
168 name: None,
169 activation_token: None,
170 visual_id: None,
171 screen_id: None,
172 base_size: None,
173 override_redirect: false,
174 x11_window_types: vec![WindowType::Normal],
175 embed_window: None,
176 }
177 }
178}
179
180impl WindowAttributesX11 {
181 /// Create this window with a specific X11 visual.
182 pub fn with_x11_visual(mut self, visual_id: XVisualID) -> Self {
183 self.visual_id = Some(visual_id);
184 self
185 }
186
187 pub fn with_x11_screen(mut self, screen_id: i32) -> Self {
188 self.screen_id = Some(screen_id);
189 self
190 }
191
192 /// Build window with the given `general` and `instance` names.
193 ///
194 /// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the
195 /// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "instance",
196 /// "general"`.
197 ///
198 /// For details about application ID conventions, see the
199 /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
200 pub fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
201 self.name = Some(ApplicationName { general: general.into(), instance: instance.into() });
202 self
203 }
204
205 /// Build window with override-redirect flag; defaults to false.
206 pub fn with_override_redirect(mut self, override_redirect: bool) -> Self {
207 self.override_redirect = override_redirect;
208 self
209 }
210
211 /// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`.
212 pub fn with_x11_window_type(mut self, x11_window_types: Vec<WindowType>) -> Self {
213 self.x11_window_types = x11_window_types;
214 self
215 }
216
217 /// Build window with base size hint.
218 ///
219 /// ```
220 /// # use winit::dpi::{LogicalSize, PhysicalSize};
221 /// # use winit::window::{Window, WindowAttributes};
222 /// # use winit::platform::x11::WindowAttributesX11;
223 /// // Specify the size in logical dimensions like this:
224 /// WindowAttributesX11::default().with_base_size(LogicalSize::new(400.0, 200.0));
225 ///
226 /// // Or specify the size in physical dimensions like this:
227 /// WindowAttributesX11::default().with_base_size(PhysicalSize::new(400, 200));
228 /// ```
229 pub fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
230 self.base_size = Some(base_size.into());
231 self
232 }
233
234 /// Embed this window into another parent window.
235 ///
236 /// # Example
237 ///
238 /// ```no_run
239 /// use winit::window::{Window, WindowAttributes};
240 /// use winit::event_loop::ActiveEventLoop;
241 /// use winit::platform::x11::{XWindow, WindowAttributesX11};
242 /// # fn create_window(event_loop: &dyn ActiveEventLoop) -> Result<(), Box<dyn std::error::Error>> {
243 /// let parent_window_id = std::env::args().nth(1).unwrap().parse::<XWindow>()?;
244 /// let window_x11_attributes = WindowAttributesX11::default().with_embed_parent_window(parent_window_id);
245 /// let window_attributes = WindowAttributes::default().with_platform_attributes(Box::new(window_x11_attributes));
246 /// let window = event_loop.create_window(window_attributes)?;
247 /// # Ok(()) }
248 /// ```
249 pub fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self {
250 self.embed_window = Some(parent_window_id);
251 self
252 }
253
254 #[inline]
255 pub fn with_activation_token(mut self, token: ActivationToken) -> Self {
256 self.activation_token = Some(token);
257 self
258 }
259}
260
261impl PlatformWindowAttributes for WindowAttributesX11 {
262 fn box_clone(&self) -> Box<dyn PlatformWindowAttributes> {
263 Box::from(self.clone())
264 }
265}