Skip to main content

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}