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